<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C語言 / C++基礎(chǔ)面試知識(shí)大集合

          共 3652字,需瀏覽 8分鐘

           ·

          2022-05-14 14:55


          相對(duì)而言,C語言和C++相關(guān)的面試題比較少見,沒有Java方向?qū)懙娜四敲炊啵@是一篇 C 語言與 C++面試知識(shí)點(diǎn)總結(jié)的文章,個(gè)人感覺非常難得,希望能對(duì)大家有所幫助。

          const

          作用

          1. 修飾變量,說明該變量不可以被改變;

          2. 修飾指針,分為指向常量的指針(pointer to const)和自身是常量的指針(常量指針,const pointer);

          3. 修飾引用,指向常量的引用(reference to const),用于形參類型,即避免了拷貝,又避免了函數(shù)對(duì)值的修改;

          4. 修飾成員函數(shù),說明該成員函數(shù)內(nèi)不能修改成員變量。

          const 的指針與引用

          1. 指針
          • 指向常量的指針(pointer to const)
          • 自身是常量的指針(常量指針,const pointer)
          1. 引用
          • 指向常量的引用(reference to const)
          • 沒有 const reference,因?yàn)橐帽旧砭褪?const pointer

          (為了方便記憶可以想成)被 const 修飾(在 const 后面)的值不可改變,如下文使用例子中的 p2、p3。

          使用

          // 類
          class A
          {
          private:
          ? ?const int a; ? ? ? ? ? ? ? ?// 常對(duì)象成員,只能在初始化列表賦值

          public:
          ? ?// 構(gòu)造函數(shù)
          ? ?A() : a(0) { };
          ? ?A(int x) : a(x) { }; ? ? ? ?// 初始化列表

          ? ?// const可用于對(duì)重載函數(shù)的區(qū)分
          ? ?int getValue(); ? ? ? ? ? ? // 普通成員函數(shù)
          ? ?int getValue() const; ? ? ? // 常成員函數(shù),不得修改類中的任何數(shù)據(jù)成員的值
          };

          void function()
          {
          ? ?// 對(duì)象
          ? ?A b; ? ? ? ? ? ? ? ? ? ? ? ?// 普通對(duì)象,可以調(diào)用全部成員函數(shù)、更新常成員變量
          ? ?const A a; ? ? ? ? ? ? ? ? ?// 常對(duì)象,只能調(diào)用常成員函數(shù)
          ? ?const A *p = &a; ? ? ? ? ? ?// 指針變量,指向常對(duì)象
          ? ?const A &q = a; ? ? ? ? ? ? // 指向常對(duì)象的引用

          ? ?// 指針
          ? ?char greeting[] = "Hello";
          ? ?char* p1 = greeting; ? ? ? ? ? ? ? ?// 指針變量,指向字符數(shù)組變量
          ? ?const char* p2 = greeting; ? ? ? ? ?// 指針變量,指向字符數(shù)組常量(const 后面是 char,說明指向的字符(char)不可改變)
          ? ?char* const p3 = greeting; ? ? ? ? ?// 自身是常量的指針,指向字符數(shù)組變量(const 后面是 p3,說明 p3 指針自身不可改變)
          ? ?const char* const p4 = greeting; ? ?// 自身是常量的指針,指向字符數(shù)組常量
          }

          // 函數(shù)
          void function1(const int Var); ? ? ? ? ? // 傳遞過來的參數(shù)在函數(shù)內(nèi)不可變
          void function2(const char* Var); ? ? ? ? // 參數(shù)指針?biāo)竷?nèi)容為常量
          void function3(char* const Var); ? ? ? ? // 參數(shù)指針為常量
          void function4(const int& Var); ? ? ? ? ?// 引用參數(shù)在函數(shù)內(nèi)為常量

          // 函數(shù)返回值
          const int function5(); ? ? ?// 返回一個(gè)常數(shù)
          const int* function6(); ? ? // 返回一個(gè)指向常量的指針變量,使用:const int *p = function6();
          int* const function7(); ? ? // 返回一個(gè)指向變量的常指針,使用:int* const p = function7();

          static

          作用

          1. 修飾普通變量,修改變量的存儲(chǔ)區(qū)域和生命周期,使變量存儲(chǔ)在靜態(tài)區(qū),在 main 函數(shù)運(yùn)行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統(tǒng)用默認(rèn)值初始化它。

          2. 修飾普通函數(shù),表明函數(shù)的作用范圍,僅在定義該函數(shù)的文件內(nèi)才能使用。在多人開發(fā)項(xiàng)目時(shí),為了防止與他人命名空間里的函數(shù)重名,可以將函數(shù)定位為 static。

          3. 修飾成員變量,修飾成員變量使所有的對(duì)象只保存一個(gè)該變量,而且不需要生成對(duì)象就可以訪問該成員。

          4. 修飾成員函數(shù),修飾成員函數(shù)使得不需要生成對(duì)象就可以訪問該函數(shù),但是在 static 函數(shù)內(nèi)不能訪問非靜態(tài)成員。

          this 指針

          1. this 指針是一個(gè)隱含于每一個(gè)非靜態(tài)成員函數(shù)中的特殊指針。它指向調(diào)用該成員函數(shù)的那個(gè)對(duì)象。

          2. 當(dāng)對(duì)一個(gè)對(duì)象調(diào)用成員函數(shù)時(shí),編譯程序先將對(duì)象的地址賦給 this 指針,然后調(diào)用成員函數(shù),每次成員函數(shù)存取數(shù)據(jù)成員時(shí),都隱式使用 this 指針。

          3. 當(dāng)一個(gè)成員函數(shù)被調(diào)用時(shí),自動(dòng)向它傳遞一個(gè)隱含的參數(shù),該參數(shù)是一個(gè)指向這個(gè)成員函數(shù)所在的對(duì)象的指針。

          4. this 指針被隱含地聲明為: ClassName?const this,這意味著不能給 this 指針賦值;在 ClassName 類的 const 成員函數(shù)中,this 指針的類型為:const ClassName?const,這說明不能對(duì) this 指針?biāo)赶虻倪@種對(duì)象是不可修改的(即不能對(duì)這種對(duì)象的數(shù)據(jù)成員進(jìn)行賦值操作);

          5. this 并不是一個(gè)常規(guī)變量,而是個(gè)右值,所以不能取得 this 的地址(不能 &this)。

          在以下場(chǎng)景中,經(jīng)常需要顯式引用 this 指針:

          • 為實(shí)現(xiàn)對(duì)象的鏈?zhǔn)揭茫?/section>
          • 為避免對(duì)同一對(duì)象進(jìn)行賦值操作;
          • 在實(shí)現(xiàn)一些數(shù)據(jù)結(jié)構(gòu)時(shí),如 list。

          inline 內(nèi)聯(lián)函數(shù)

          特征

          • 相當(dāng)于把內(nèi)聯(lián)函數(shù)里面的內(nèi)容寫在調(diào)用內(nèi)聯(lián)函數(shù)處;
          • 相當(dāng)于不用執(zhí)行進(jìn)入函數(shù)的步驟,直接執(zhí)行函數(shù)體;
          • 相當(dāng)于宏,卻比宏多了類型檢查,真正具有函數(shù)特性;
          • 編譯器一般不內(nèi)聯(lián)包含循環(huán)、遞歸、switch 等復(fù)雜操作的內(nèi)聯(lián)函數(shù);
          • 在類聲明中定義的函數(shù),除了虛函數(shù)的其他函數(shù)都會(huì)自動(dòng)隱式地當(dāng)成內(nèi)聯(lián)函數(shù)。

          使用

          inline 使用

          // 聲明1(加 inline,建議使用)
          inline int functionName(int first, int second,...);

          // 聲明2(不加 inline)
          int functionName(int first, int second,...);

          // 定義
          inline int functionName(int first, int second,...) {/****/};

          // 類內(nèi)定義,隱式內(nèi)聯(lián)
          class A {
          ? ?int doA() { return 0; } ? ? ? ? // 隱式內(nèi)聯(lián)
          }

          // 類外定義,需要顯式內(nèi)聯(lián)
          class A {
          ? ?int doA();
          }
          inline int A::doA() { return 0; } ? // 需要顯式內(nèi)聯(lián)

          編譯器對(duì) inline 函數(shù)處理步驟

          1. 將 inline 函數(shù)體復(fù)制到 inline 函數(shù)調(diào)用點(diǎn)處;
          2. 為所用 inline 函數(shù)中的局部變量分配內(nèi)存空間;
          3. 將 inline 函數(shù)的的輸入?yún)?shù)和返回值映射到調(diào)用方法的局部變量空間中;
          4. 如果 inline 函數(shù)有多個(gè)返回點(diǎn),將其轉(zhuǎn)變?yōu)?inline 函數(shù)代碼塊末尾的分支(使用 GOTO)。

          優(yōu)缺點(diǎn)


          優(yōu)點(diǎn)

          • 內(nèi)聯(lián)函數(shù)同宏函數(shù)一樣將在被調(diào)用處進(jìn)行代碼展開,省去了參數(shù)壓棧、棧幀開辟與回收,結(jié)果返回等,從而提高程序運(yùn)行速度。
          • 內(nèi)聯(lián)函數(shù)相比宏函數(shù)來說,在代碼展開時(shí),會(huì)做安全檢查或自動(dòng)類型轉(zhuǎn)換(同普通函數(shù)),而宏定義則不會(huì)。
          • 在類中聲明同時(shí)定義的成員函數(shù),自動(dòng)轉(zhuǎn)化為內(nèi)聯(lián)函數(shù),因此內(nèi)聯(lián)函數(shù)可以訪問類的成員變量,宏定義則不能。
          • 內(nèi)聯(lián)函數(shù)在運(yùn)行時(shí)可調(diào)試,而宏定義不可以。

          虛函數(shù)(virtual)可以是內(nèi)聯(lián)函數(shù)(inline)嗎?


          Are "inline virtual" member functions ever actually "inlined"?

          • 虛函數(shù)可以是內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)是可以修飾虛函數(shù)的,但是當(dāng)虛函數(shù)表現(xiàn)多態(tài)性的時(shí)候不能內(nèi)聯(lián)。
          • 內(nèi)聯(lián)是在編譯器建議編譯器內(nèi)聯(lián),而虛函數(shù)的多態(tài)性在運(yùn)行期,編譯器無法知道運(yùn)行期調(diào)用哪個(gè)代碼,因此虛函數(shù)表現(xiàn)為多態(tài)性時(shí)(運(yùn)行期)不可以內(nèi)聯(lián)。
          • inline virtual 唯一可以內(nèi)聯(lián)的時(shí)候是:編譯器知道所調(diào)用的對(duì)象是哪個(gè)類(如 Base::who()),這只有在編譯器具有實(shí)際對(duì)象而不是對(duì)象的指針或引用時(shí)才會(huì)發(fā)生。

          虛函數(shù)內(nèi)聯(lián)使用

          #include 
          using namespace std;
          class Base
          {
          public:
          inline virtual void who()
          {
          cout << "I am Base\n";
          }
          virtual ~Base() {}
          };
          class Derived : public Base
          {
          public:
          inline void who() ?// 不寫inline時(shí)隱式內(nèi)聯(lián)
          {
          cout << "I am Derived\n";
          }
          };

          int main()
          {
          // 此處的虛函數(shù) who(),是通過類(Base)的具體對(duì)象(b)來調(diào)用的,編譯期間就能確定了,所以它可以是內(nèi)聯(lián)的,但最終是否內(nèi)聯(lián)取決于編譯器。
          Base b;
          b.who();

          // 此處的虛函數(shù)是通過指針調(diào)用的,呈現(xiàn)多態(tài)性,需要在運(yùn)行時(shí)期間才能確定,所以不能為內(nèi)聯(lián)。
          Base *ptr = new Derived();
          ptr->who();

          // 因?yàn)锽ase有虛析構(gòu)函數(shù)(virtual ~Base() {}),所以 delete 時(shí),會(huì)先調(diào)用派生類(Derived)析構(gòu)函數(shù),再調(diào)用基類(Base)析構(gòu)函數(shù),防止內(nèi)存泄漏。
          delete ptr;
          ptr = nullptr;

          system("pause");
          return 0;
          }

          volatile

          volatile int i = 10;

          • volatile 關(guān)鍵字是一種類型修飾符,用它聲明的類型變量表示可以被某些編譯器未知的因素(操作系統(tǒng)、硬件、其它線程等)更改。所以使用 volatile 告訴編譯器不應(yīng)對(duì)這樣的對(duì)象進(jìn)行優(yōu)化。
          • volatile 關(guān)鍵字聲明的變量,每次訪問時(shí)都必須從內(nèi)存中取出值(沒有被 volatile 修飾的變量,可能由于編譯器的優(yōu)化,從 CPU 寄存器中取值)
          • const 可以是 volatile (如只讀的狀態(tài)寄存器)
          • 指針可以是 volatile

          assert()

          斷言,是宏,而非函數(shù)。assert 宏的原型定義在 (C)、(C++)中,其作用是如果它的條件返回錯(cuò)誤,則終止程序執(zhí)行。可以通過定義 NDEBUG 來關(guān)閉 assert,但是需要在源代碼的開頭,include 之前。

          assert() 使用

          #define NDEBUG ? ? ? ? ?// 加上這行,則 assert 不可用
          #include

          assert( p != NULL ); ? ?// assert 不可用

          sizeof()

          • sizeof 對(duì)數(shù)組,得到整個(gè)數(shù)組所占空間大小。
          • sizeof 對(duì)指針,得到指針本身所占空間大小。

          #pragma pack(n)

          設(shè)定結(jié)構(gòu)體、聯(lián)合以及類成員變量以 n 字節(jié)方式對(duì)齊

          #pragma pack(n) 使用

          #pragma pack(push) ?// 保存對(duì)齊狀態(tài)
          #pragma pack(4) ? ? // 設(shè)定為 4 字節(jié)對(duì)齊

          struct test
          {
          ? ?char m1;
          ? ?double m4;
          ? ?int m3;
          };

          #pragma pack(pop) ? // 恢復(fù)對(duì)齊狀態(tài)

          位域

          Bit mode: 2; // mode 占 2 位

          類可以將其(非靜態(tài))數(shù)據(jù)成員定義為位域(bit-field),在一個(gè)位域中含有一定數(shù)量的二進(jìn)制位。當(dāng)一個(gè)程序需要向其他程序或硬件設(shè)備傳遞二進(jìn)制數(shù)據(jù)時(shí),通常會(huì)用到位域。

          • 位域在內(nèi)存中的布局是與機(jī)器有關(guān)的
          • 位域的類型必須是整型或枚舉類型,帶符號(hào)類型中的位域的行為將因具體實(shí)現(xiàn)而定
          • 取地址運(yùn)算符(&)不能作用于位域,任何指針都無法指向類的位域

          extern "C"

          • 被 extern 限定的函數(shù)或變量是 extern 類型的
          • 被 extern "C" 修飾的變量和函數(shù)是按照 C 語言方式編譯和鏈接的

          extern "C" 的作用是讓 C++ 編譯器將 extern "C" 聲明的代碼當(dāng)作 C 語言代碼處理,可以避免 C++ 因符號(hào)修飾導(dǎo)致代碼不能和C語言庫中的符號(hào)進(jìn)行鏈接的問題。

          extern "C" 使用

          #ifdef __cplusplus
          extern "C" {
          #endif

          void *memset(void *, int, size_t);

          #ifdef __cplusplus
          }
          #endif

          struct 和 typedef struct

          C 中

          // c
          typedef struct Student {
          ? ?int age;
          } S;

          等價(jià)于

          // c
          struct Student {
          ? ?int age;
          };

          typedef struct Student S; 此時(shí) S 等價(jià)于 struct Student,但兩個(gè)標(biāo)識(shí)符名稱空間不相同。

          另外還可以定義與 struct Student 不沖突的 void Student() {}。

          C++ 中

          由于編譯器定位符號(hào)的規(guī)則(搜索規(guī)則)改變,導(dǎo)致不同于C語言。

          1.如果在類標(biāo)識(shí)符空間定義了 struct Student {...};,使用 Student me; 時(shí),編譯器將搜索全局標(biāo)識(shí)符表,Student 未找到,則在類標(biāo)識(shí)符內(nèi)搜索。

          即表現(xiàn)為可以使用 Student 也可以使用 struct Student,如下:

          // cpp
          struct Student {
          ? ?int age;
          };

          void f( Student me ); ? ? ? // 正確,"struct" 關(guān)鍵字可省略

          2.若定義了與 Student 同名函數(shù)之后,則 Student 只代表函數(shù),不代表結(jié)構(gòu)體,如下:

          typedef struct Student {
          ? ?int age;
          } S;

          void Student() {} ? ? ? ? ? // 正確,定義后 "Student" 只代表此函數(shù)

          //void S() {} ? ? ? ? ? ? ? // 錯(cuò)誤,符號(hào) "S" 已經(jīng)被定義為一個(gè) "struct Student" 的別名

          int main() {
          ? ?Student();
          ? ?struct Student me; ? ? ?// 或者 "S me";
          ? ?return 0;
          }

          C++ 中 struct 和 class

          總的來說,struct 更適合看成是一個(gè)數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)體,class 更適合看成是一個(gè)對(duì)象的實(shí)現(xiàn)體。

          區(qū)別:

          最本質(zhì)的一個(gè)區(qū)別就是默認(rèn)的訪問控制

          • 默認(rèn)的繼承訪問權(quán)限。struct 是 public 的,class 是 private 的。
          • struct 作為數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)體,它默認(rèn)的數(shù)據(jù)訪問控制是 public 的,而 class 作為對(duì)象的實(shí)現(xiàn)體,它默認(rèn)的成員變量訪問控制是 private 的。

          union 聯(lián)合

          聯(lián)合(union)是一種節(jié)省空間的特殊的類,一個(gè) union 可以有多個(gè)數(shù)據(jù)成員,但是在任意時(shí)刻只有一個(gè)數(shù)據(jù)成員可以有值。當(dāng)某個(gè)成員被賦值后其他成員變?yōu)槲炊x狀態(tài)。聯(lián)合有如下特點(diǎn):

          • 默認(rèn)訪問控制符為 public
          • 可以含有構(gòu)造函數(shù)、析構(gòu)函數(shù)
          • 不能含有引用類型的成員
          • 不能繼承自其他類,不能作為基類
          • 不能含有虛函數(shù)
          • 匿名 union 在定義所在作用域可直接訪問 union 成員
          • 匿名 union 不能包含 protected 成員或 private 成員
          • 全局匿名聯(lián)合必須是靜態(tài)(static)的

          union 使用

          #include

          union UnionTest {
          ? ?UnionTest() : i(10) {};
          ? ?int i;
          ? ?double d;
          };

          static union {
          ? ?int i;
          ? ?double d;
          };

          int main() {
          ? ?UnionTest u;

          ? ?union {
          ? ? ? ?int i;
          ? ? ? ?double d;
          ? ?};

          ? ?std::cout << u.i << std::endl; ?// 輸出 UnionTest 聯(lián)合的 10

          ? ?::i = 20;
          ? ?std::cout << ::i << std::endl; ?// 輸出全局靜態(tài)匿名聯(lián)合的 20

          ? ?i = 30;
          ? ?std::cout << i << std::endl; ? ?// 輸出局部匿名聯(lián)合的 30

          ? ?return 0;
          }

          C語言實(shí)現(xiàn)C++類

          C 實(shí)現(xiàn) C++ 的面向?qū)ο筇匦裕ǚ庋b、繼承、多態(tài))

          • 封裝:使用函數(shù)指針把屬性與方法封裝到結(jié)構(gòu)體中
          • 繼承:結(jié)構(gòu)體嵌套
          • 多態(tài):父類與子類方法的函數(shù)指針不同

          explicit(顯式)關(guān)鍵字

          • explicit 修飾構(gòu)造函數(shù)時(shí),可以防止隱式轉(zhuǎn)換和復(fù)制初始化
          • explicit 修飾轉(zhuǎn)換函數(shù)時(shí),可以防止隱式轉(zhuǎn)換,但 按語境轉(zhuǎn)換 除外

          explicit 使用

          struct A
          {
          A(int) { }
          operator bool() const { return true; }
          };

          struct B
          {
          explicit B(int) {}
          explicit operator bool() const { return true; }
          };

          void doA(A a) {}

          void doB(B b) {}

          int main()
          {
          A a1(1);// OK:直接初始化
          A a2 = 1;// OK:復(fù)制初始化
          A a3{ 1 };// OK:直接列表初始化
          A a4 = { 1 };// OK:復(fù)制列表初始化
          A a5 = (A)1;// OK:允許 static_cast 的顯式轉(zhuǎn)換
          doA(1);// OK:允許從 int 到 A 的隱式轉(zhuǎn)換
          if (a1);// OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
          bool a6(a1);// OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
          bool a7 = a1;// OK:使用轉(zhuǎn)換函數(shù) A::operator bool() 的從 A 到 bool 的隱式轉(zhuǎn)換
          bool a8 = static_cast(a1); ?// OK :static_cast 進(jìn)行直接初始化

          B b1(1);// OK:直接初始化
          B b2 = 1;// 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制初始化
          B b3{ 1 };// OK:直接列表初始化
          B b4 = { 1 };// 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以復(fù)制列表初始化
          B b5 = (B)1;// OK:允許 static_cast 的顯式轉(zhuǎn)換
          doB(1);// 錯(cuò)誤:被 explicit 修飾構(gòu)造函數(shù)的對(duì)象不可以從 int 到 B 的隱式轉(zhuǎn)換
          if (b1);// OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語境轉(zhuǎn)換
          bool b6(b1);// OK:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象可以從 B 到 bool 的按語境轉(zhuǎn)換
          bool b7 = b1;// 錯(cuò)誤:被 explicit 修飾轉(zhuǎn)換函數(shù) B::operator bool() 的對(duì)象不可以隱式轉(zhuǎn)換
          bool b8 = static_cast(b1); ?// OK:static_cast 進(jìn)行直接初始化

          return 0;
          }

          friend 友元類和友元函數(shù)

          • 能訪問私有成員
          • 破壞封裝性
          • 友元關(guān)系不可傳遞
          • 友元關(guān)系的單向性
          • 友元聲明的形式及數(shù)量不受限制

          using

          using 聲明

          一條 using 聲明 語句一次只引入命名空間的一個(gè)成員。它使得我們可以清楚知道程序中所引用的到底是哪個(gè)名字。如:

          using namespace_name::name;

          構(gòu)造函數(shù)的 using 聲明

          在 C++11 中,派生類能夠重用其直接基類定義的構(gòu)造函數(shù)。

          class Derived : Base {
          public:
          ? ?using Base::Base;
          ? ?/* ... */
          };

          如上 using 聲明,對(duì)于基類的每個(gè)構(gòu)造函數(shù),編譯器都生成一個(gè)與之對(duì)應(yīng)(形參列表完全相同)的派生類構(gòu)造函數(shù)。生成如下類型構(gòu)造函數(shù):Derived(parms) : Base(args) { }

          using 指示

          using 指示 使得某個(gè)特定命名空間中所有名字都可見,這樣我們就無需再為它們添加任何前綴限定符了。如:

          using namespace_name name;

          盡量少使用 using 指示 污染命名空間

          一般說來,使用 using 命令比使用 using 編譯命令更安全,這是由于它只導(dǎo)入了指定的名稱。如果該名稱與局部名稱發(fā)生沖突,編譯器將發(fā)出指示。using編譯命令導(dǎo)入所有的名稱,包括可能并不需要的名稱。如果與局部名稱發(fā)生沖突,則局部名稱將覆蓋名稱空間版本,而編譯器并不會(huì)發(fā)出警告。另外,名稱空間的開放性意味著名稱空間的名稱可能分散在多個(gè)地方,這使得難以準(zhǔn)確知道添加了哪些名稱。

          using 使用

          盡量少使用 using 指示

          using namespace std;

          應(yīng)該多使用 using 聲明

          int x;
          std::cin >> x ;
          std::cout << x << std::endl;

          或者

          using std::cin;
          using std::cout;
          using std::endl;
          int x;
          cin >> x;
          cout << x << endl;

          :: 范圍解析運(yùn)算符

          分類

          1. 全局作用域符(::name):用于類型名稱(類、類成員、成員函數(shù)、變量等)前,表示作用域?yàn)槿置臻g
          2. 類作用域符(class::name):用于表示指定類型的作用域范圍是具體某個(gè)類的
          3. 命名空間作用域符(namespace::name):用于表示指定類型的作用域范圍是具體某個(gè)命名空間的

          :: 使用

          int count = 11; ? ? ? ? // 全局(::)的 count

          class A {
          public:
          static int count; ? // 類 A 的 count(A::count)
          };
          int A::count = 21;

          void fun()
          {
          int count = 31; ? ? // 初始化局部的 count 為 31
          count = 32; ? ? ? ? // 設(shè)置局部的 count 的值為 32
          }

          int main() {
          ::count = 12; ? ? ? // 測(cè)試 1:設(shè)置全局的 count 的值為 12

          A::count = 22; ? ? ?// 測(cè)試 2:設(shè)置類 A 的 count 為 22

          fun(); ? ? ? ?// 測(cè)試 3

          return 0;
          }

          enum 枚舉類型

          定作用域的枚舉類型

          enum class open_modes { input, output, append };

          不限定作用域的枚舉類型

          enum color { red, yellow, green };
          ?
          enum { floatPrec = 6, doublePrec = 10 };

          decltype

          decltype 關(guān)鍵字用于檢查實(shí)體的聲明類型或表達(dá)式的類型及值分類。語法:

          decltype ( expression )

          decltype 使用

          // 尾置返回允許我們?cè)趨?shù)列表之后聲明返回類型
          template
          auto fcn(It beg, It end) -> decltype(*beg)
          {
          ? ?// 處理序列
          ? ?return *beg; ? ?// 返回序列中一個(gè)元素的引用
          }
          // 為了使用模板參數(shù)成員,必須用 typename
          template
          auto fcn2(It beg, It end) -> typename remove_reference::type
          {
          ? ?// 處理序列
          ? ?return *beg; ? ?// 返回序列中一個(gè)元素的拷貝
          }

          引用

          左值引用

          常規(guī)引用,一般表示對(duì)象的身份。

          右值引用

          右值引用就是必須綁定到右值(一個(gè)臨時(shí)對(duì)象、將要銷毀的對(duì)象)的引用,一般表示對(duì)象的值。

          右值引用可實(shí)現(xiàn)轉(zhuǎn)移語義(Move Sementics)和精確傳遞(Perfect Forwarding),它的主要目的有兩個(gè)方面:

          • 消除兩個(gè)對(duì)象交互時(shí)不必要的對(duì)象拷貝,節(jié)省運(yùn)算存儲(chǔ)資源,提高效率。
          • 能夠更簡潔明確地定義泛型函數(shù)。

          引用折疊

          • X& &、X& &&、X&& & 可折疊成 X&
          • X&& && 可折疊成 X&&

          宏定義可以實(shí)現(xiàn)類似于函數(shù)的功能,但是它終歸不是函數(shù),而宏定義中括弧中的“參數(shù)”也不是真的參數(shù),在宏展開的時(shí)候?qū)?“參數(shù)” 進(jìn)行的是一對(duì)一的替換。

          成員初始化列表

          好處

          1. 更高效:少了一次調(diào)用默認(rèn)構(gòu)造函數(shù)的過程。
          2. 有些場(chǎng)合必須要用初始化列表:
          • 常量成員,因?yàn)槌A恐荒艹跏蓟荒苜x值,所以必須放在初始化列表里面
          • 引用類型,引用必須在定義的時(shí)候初始化,并且不能重新賦值,所以也要寫在初始化列表里面
          • 沒有默認(rèn)構(gòu)造函數(shù)的類類型,因?yàn)槭褂贸跏蓟斜砜梢圆槐卣{(diào)用默認(rèn)構(gòu)造函數(shù)來初始化

          initializer_list 列表初始化

          用花括號(hào)初始化器列表初始化一個(gè)對(duì)象,其中對(duì)應(yīng)構(gòu)造函數(shù)接受一個(gè) std::initializer_list 參數(shù).

          initializer_list 使用

          #include 
          #include
          #include

          template
          struct S {
          ? ?std::vector v;
          ? ?S(std::initializer_list l) : v(l) {
          ? ? ? ? std::cout << "constructed with a " << l.size() << "-element list\n";
          ? ?}
          ? ?void append(std::initializer_list l) {
          ? ? ? ?v.insert(v.end(), l.begin(), l.end());
          ? ?}
          ? ?std::pair c_arr() const {
          ? ? ? ?return {&v[0], v.size()}; ?// 在 return 語句中復(fù)制列表初始化
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 這不使用 std::initializer_list
          ? ?}
          };

          template
          void templated_fn(T) {}

          int main()
          {
          ? ?S s = {1, 2, 3, 4, 5}; // 復(fù)制初始化
          ? ?s.append({6, 7, 8}); ? ? ?// 函數(shù)調(diào)用中的列表初始化

          ? ?std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";

          ? ?for (auto n : s.v)
          ? ? ? ?std::cout << n << ' ';
          ? ?std::cout << '\n';

          ? ?std::cout << "Range-for over brace-init-list: \n";

          ? ?for (int x : {-1, -2, -3}) // auto 的規(guī)則令此帶范圍 for 工作
          ? ? ? ?std::cout << x << ' ';
          ? ?std::cout << '\n';

          ? ?auto al = {10, 11, 12}; ? // auto 的特殊規(guī)則

          ? ?std::cout << "The list bound to auto has size() = " << al.size() << '\n';

          // ? templated_fn({1, 2, 3}); // 編譯錯(cuò)誤!“ {1, 2, 3} ”不是表達(dá)式,
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 它無類型,故 T 無法推導(dǎo)
          ? ?templated_fn>({1, 2, 3}); // OK
          ? ?templated_fn>({1, 2, 3}); ? ? ? ? ? // 也 OK
          }

          面向?qū)ο?/span>

          面向?qū)ο蟪绦蛟O(shè)計(jì)(Object-oriented programming,OOP)是種具有對(duì)象概念的程序編程典范,同時(shí)也是一種程序開發(fā)的抽象方針。

          面向?qū)ο筇卣?/p>

          面向?qū)ο笕筇卣?—— 封裝、繼承、多態(tài)

          封裝

          把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對(duì)象操作,對(duì)不可信的進(jìn)行信息隱藏。關(guān)鍵字:public, protected, private。不寫默認(rèn)為 private。

          • public 成員:可以被任意實(shí)體訪問
          • protected 成員:只允許被子類及本類的成員函數(shù)訪問
          • private 成員:只允許被本類的成員函數(shù)、友元類或友元函數(shù)訪問

          繼承

          • 基類(父類)——> 派生類(子類)

          多態(tài)

          1. 多態(tài),即多種狀態(tài)(形態(tài))。簡單來說,我們可以將多態(tài)定義為消息以多種形式顯示的能力。
          2. 多態(tài)是以封裝和繼承為基礎(chǔ)的。
          3. C++ 多態(tài)分類及實(shí)現(xiàn):
          • 重載多態(tài)(Ad-hoc Polymorphism,編譯期):函數(shù)重載、運(yùn)算符重載
          • 子類型多態(tài)(Subtype Polymorphism,運(yùn)行期):虛函數(shù)
          • 參數(shù)多態(tài)性(Parametric Polymorphism,編譯期):類模板、函數(shù)模板
          • 強(qiáng)制多態(tài)(Coercion Polymorphism,編譯期/運(yùn)行期):基本類型轉(zhuǎn)換、自定義類型轉(zhuǎn)換

          靜態(tài)多態(tài)(編譯期/早綁定)

          函數(shù)重載

          class A
          {
          public:
          ? ?void do(int a);
          ? ?void do(int a, int b);
          };

          動(dòng)態(tài)多態(tài)(運(yùn)行期期/晚綁定)

          • 虛函數(shù):用 virtual 修飾成員函數(shù),使其成為虛函數(shù)

          注意

          • 普通函數(shù)(非類成員函數(shù))不能是虛函數(shù)
          • 靜態(tài)函數(shù)(static)不能是虛函數(shù)
          • 構(gòu)造函數(shù)不能是虛函數(shù)(因?yàn)樵谡{(diào)用構(gòu)造函數(shù)時(shí),虛表指針并沒有在對(duì)象的內(nèi)存空間中,必須要構(gòu)造函數(shù)調(diào)用完成后才會(huì)形成虛表指針)
          • 內(nèi)聯(lián)函數(shù)不能是表現(xiàn)多態(tài)性時(shí)的虛函數(shù),解釋見:虛函數(shù)(virtual)可以是內(nèi)聯(lián)函數(shù)(inline)嗎?

          動(dòng)態(tài)多態(tài)使用

          class Shape ? ? ? ? ? ? ? ? ? ? // 形狀類
          {
          public:
          ? ?virtual double calcArea()
          ? ?{
          ? ? ? ?...
          ? ?}
          ? ?virtual ~Shape();
          };
          class Circle : public Shape ? ? // 圓形類
          {
          public:
          ? ?virtual double calcArea();
          ? ?...
          };
          class Rect : public Shape ? ? ? // 矩形類
          {
          public:
          ? ?virtual double calcArea();
          ? ?...
          };
          int main()
          {
          ? ?Shape * shape1 = new Circle(4.0);
          ? ?Shape * shape2 = new Rect(5.0, 6.0);
          ? ?shape1->calcArea(); ? ? ? ? // 調(diào)用圓形類里面的方法
          ? ?shape2->calcArea(); ? ? ? ? // 調(diào)用矩形類里面的方法
          ? ?delete shape1;
          ? ?shape1 = nullptr;
          ? ?delete shape2;
          ? ?shape2 = nullptr;
          ? ?return 0;
          }

          虛析構(gòu)函數(shù)

          虛析構(gòu)函數(shù)是為了解決基類的指針指向派生類對(duì)象,并用基類的指針刪除派生類對(duì)象。

          虛析構(gòu)函數(shù)使用

          class Shape
          {
          public:
          ? ?Shape(); ? ? ? ? ? ? ? ? ? ?// 構(gòu)造函數(shù)不能是虛函數(shù)
          ? ?virtual double calcArea();
          ? ?virtual ~Shape(); ? ? ? ? ? // 虛析構(gòu)函數(shù)
          };
          class Circle : public Shape ? ? // 圓形類
          {
          public:
          ? ?virtual double calcArea();
          ? ?...
          };
          int main()
          {
          ? ?Shape * shape1 = new Circle(4.0);
          ? ?shape1->calcArea();
          ? delete shape1; ?// 因?yàn)镾hape有虛析構(gòu)函數(shù),所以delete釋放內(nèi)存時(shí),先調(diào)用子類析構(gòu)函數(shù),再調(diào)用基類析構(gòu)函數(shù),防止內(nèi)存泄漏。
          ? ?shape1 = NULL;
          ? return 0;
          }

          純虛函數(shù)

          純虛函數(shù)是一種特殊的虛函數(shù),在基類中不能對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),而把它聲明為純虛函數(shù),它的實(shí)現(xiàn)留給該基類的派生類去做。

          virtual int A() = 0;

          虛函數(shù)、純虛函數(shù)

          • 類里如果聲明了虛函數(shù),這個(gè)函數(shù)是實(shí)現(xiàn)的,哪怕是空實(shí)現(xiàn),它的作用就是為了能讓這個(gè)函數(shù)在它的子類里面可以被覆蓋(override),這樣的話,編譯器就可以使用后期綁定來達(dá)到多態(tài)了。純虛函數(shù)只是一個(gè)接口,是個(gè)函數(shù)的聲明而已,它要留到子類里去實(shí)現(xiàn)。
          • 虛函數(shù)在子類里面可以不重寫;但純虛函數(shù)必須在子類實(shí)現(xiàn)才可以實(shí)例化子類。虛函數(shù)的類用于 “實(shí)作繼承”,繼承接口的同時(shí)也繼承了父類的實(shí)現(xiàn)。
          • 純虛函數(shù)關(guān)注的是接口的統(tǒng)一性,實(shí)現(xiàn)由子類完成。
          • 帶純虛函數(shù)的類叫抽象類,這種類不能直接生成對(duì)象,而只有被繼承,并重寫其虛函數(shù)后,才能使用。抽象類被繼承后,子類可以繼續(xù)是抽象類,也可以是普通類。
          • 虛基類是虛繼承中的基類,具體見下文虛繼承。

          虛函數(shù)指針、虛函數(shù)表

          • 虛函數(shù)指針:在含有虛函數(shù)類的對(duì)象中,指向虛函數(shù)表,在運(yùn)行時(shí)確定。
          • 虛函數(shù)表:在程序只讀數(shù)據(jù)段(.rodata section,見:目標(biāo)文件存儲(chǔ)結(jié)構(gòu)),存放虛函數(shù)指針,如果派生類實(shí)現(xiàn)了基類的某個(gè)虛函數(shù),則在虛表中覆蓋原本基類的那個(gè)虛函數(shù)指針,在編譯時(shí)根據(jù)類的聲明創(chuàng)建。

          虛繼承

          虛繼承用于解決多繼承條件下的菱形繼承問題(浪費(fèi)存儲(chǔ)空間、存在二義性)。

          底層實(shí)現(xiàn)原理與編譯器相關(guān),一般通過虛基類指針和虛基類表實(shí)現(xiàn),每個(gè)虛繼承的子類都有一個(gè)虛基類指針(占用一個(gè)指針的存儲(chǔ)空間,4字節(jié))和虛基類表(不占用類對(duì)象的存儲(chǔ)空間)(需要強(qiáng)調(diào)的是,虛基類依舊會(huì)在子類里面存在拷貝,只是僅僅最多存在一份而已,并不是不在子類里面了);當(dāng)虛繼承的子類被當(dāng)做父類繼承時(shí),虛基類指針也會(huì)被繼承。

          實(shí)際上,vbptr 指的是虛基類表指針(virtual base table pointer),該指針指向了一個(gè)虛基類表(virtual table),虛表中記錄了虛基類與本類的偏移地址;通過偏移地址,這樣就找到了虛基類成員,而虛繼承也不用像普通多繼承那樣維持著公共基類(虛基類)的兩份同樣的拷貝,節(jié)省了存儲(chǔ)空間。

          虛繼承、虛函數(shù)

          1. 相同之處:都利用了虛指針(均占用類的存儲(chǔ)空間)和虛表(均不占用類的存儲(chǔ)空間)
          2. 不同之處:

          虛繼承

          • 虛基類依舊存在繼承類中,只占用存儲(chǔ)空間
          • 虛基類表存儲(chǔ)的是虛基類相對(duì)直接繼承類的偏移

          虛函數(shù)

          • 虛函數(shù)不占用存儲(chǔ)空間
          • 虛函數(shù)表存儲(chǔ)的是虛函數(shù)地址

          模板類、成員模板、虛函數(shù)

          • 模板類中可以使用虛函數(shù)
          • 一個(gè)類(無論是普通類還是類模板)的成員模板(本身是模板的成員函數(shù))不能是虛函數(shù)

          抽象類、接口類、聚合類

          抽象類:含有純虛函數(shù)的類

          接口類:僅含有純虛函數(shù)的抽象類

          聚合類:用戶可以直接訪問其成員,并且具有特殊的初始化語法形式。滿足如下特點(diǎn):

          • 所有成員都是 public
          • 沒有定義任何構(gòu)造函數(shù)
          • 沒有類內(nèi)初始化
          • 沒有基類,也沒有 virtual 函數(shù)

          內(nèi)存分配和管理

          malloc、calloc、realloc、alloca


          1. malloc:申請(qǐng)指定字節(jié)數(shù)的內(nèi)存。申請(qǐng)到的內(nèi)存中的初始值不確定。
          2. calloc:為指定長度的對(duì)象,分配能容納其指定個(gè)數(shù)的內(nèi)存。申請(qǐng)到的內(nèi)存的每一位(bit)都初始化為 0。
          3. realloc:更改以前分配的內(nèi)存長度(增加或減少)。當(dāng)增加長度時(shí),可能需將以前分配區(qū)的內(nèi)容移到另一個(gè)足夠大的區(qū)域,而新增區(qū)域內(nèi)的初始值則不確定。
          4. alloca:在棧上申請(qǐng)內(nèi)存。程序在出棧的時(shí)候,會(huì)自動(dòng)釋放內(nèi)存。但是需要注意的是,alloca 不具可移植性, 而且在沒有傳統(tǒng)堆棧的機(jī)器上很難實(shí)現(xiàn)。alloca 不宜使用在必須廣泛移植的程序中。C99 中支持變長數(shù)組 (VLA),可以用來替代 alloca。

          malloc、free

          用于分配、釋放內(nèi)存

          malloc、free 使用

          申請(qǐng)內(nèi)存,確認(rèn)是否申請(qǐng)成功

          char *str = (char*) malloc(100);
          assert(str != nullptr);

          釋放內(nèi)存后指針置空

          free(p);
          p = nullptr;

          new、delete

          1. new / new[]:完成兩件事,先底層調(diào)用 malloc 分配了內(nèi)存,然后調(diào)用構(gòu)造函數(shù)(創(chuàng)建對(duì)象)。
          2. delete/delete[]:也完成兩件事,先調(diào)用析構(gòu)函數(shù)(清理資源),然后底層調(diào)用 free 釋放空間。
          3. new 在申請(qǐng)內(nèi)存時(shí)會(huì)自動(dòng)計(jì)算所需字節(jié)數(shù),而 malloc 則需我們自己輸入申請(qǐng)內(nèi)存空間的字節(jié)數(shù)。

          new、delete 使用

          申請(qǐng)內(nèi)存,確認(rèn)是否申請(qǐng)成功

          int main()
          {
          ? ?T* t = new T(); ? ? // 先內(nèi)存分配 ,再構(gòu)造函數(shù)
          ? ?delete t; ? ? ? ? ? // 先析構(gòu)函數(shù),再內(nèi)存釋放
          ? ?return 0;
          }

          定位 new

          定位 new(placement new)允許我們向 new 傳遞額外的地址參數(shù),從而在預(yù)先指定的內(nèi)存區(qū)域創(chuàng)建對(duì)象。

          new (place_address) type
          new (place_address) type (initializers)
          new (place_address) type [size]
          new (place_address) type [size] { braced initializer list }
          • place_address 是個(gè)指針
          • initializers 提供一個(gè)(可能為空的)以逗號(hào)分隔的初始值列表

          delete this 合法嗎?

          合法,但:

          • 必須保證 this 對(duì)象是通過 new(不是 new[]、不是 placement new、不是棧上、不是全局、不是其他對(duì)象成員)分配的
          • 必須保證調(diào)用 delete this 的成員函數(shù)是最后一個(gè)調(diào)用 this 的成員函數(shù)
          • 必須保證成員函數(shù)的 delete this 后面沒有調(diào)用 this 了
          • 必須保證 delete this 后沒有人使用了

          定義只在堆(棧)生成對(duì)象類

          只能在堆上

          方法:將析構(gòu)函數(shù)設(shè)置為私有

          原因:C++ 是靜態(tài)綁定語言,編譯器管理?xiàng)I蠈?duì)象的生命周期,編譯器在為類對(duì)象分配棧空間時(shí),會(huì)先檢查類的析構(gòu)函數(shù)的訪問性。若析構(gòu)函數(shù)不可訪問,則不能在棧上創(chuàng)建對(duì)象。

          能在棧上

          方法:將 new 和 delete 重載為私有

          原因:在堆上生成對(duì)象,使用 new 關(guān)鍵詞操作,其過程分為兩階段:第一階段,使用 new 在堆上尋找可用內(nèi)存,分配給對(duì)象;第二階段,調(diào)用構(gòu)造函數(shù)生成對(duì)象。將 new 操作設(shè)置為私有,那么第一階段就無法完成,就不能夠在堆上生成對(duì)象。

          智能指針

          C++ 標(biāo)準(zhǔn)庫(STL)中

          頭文件:#include

          C++ 98

          std::auto_ptr ps (new std::string(str));

          C++ 11

          對(duì)于該論述,歡迎讀者查閱之前發(fā)過的文章,你是《未來世界的幸存者》么?

          3.7 分割線

          可以在一行中用三個(gè)以上的減號(hào)來建立一個(gè)分隔線,同時(shí)需要在分隔線的上面空一行。如下:

          1. shared_ptr
          2. unique_ptr
          3. weak_ptr
          4. auto_ptr(被 C++11 棄用)
          • Class shared_ptr 實(shí)現(xiàn)共享式擁有(shared ownership)概念。多個(gè)智能指針指向相同對(duì)象,該對(duì)象和其相關(guān)資源會(huì)在 “最后一個(gè) reference 被銷毀” 時(shí)被釋放。為了在結(jié)構(gòu)較復(fù)雜的情景中執(zhí)行上述工作,標(biāo)準(zhǔn)庫提供 weak_ptr、bad_weak_ptr 和 enable_shared_from_this 等輔助類。
          • Class unique_ptr 實(shí)現(xiàn)獨(dú)占式擁有(exclusive ownership)或嚴(yán)格擁有(strict ownership)概念,保證同一時(shí)間內(nèi)只有一個(gè)智能指針可以指向該對(duì)象。你可以移交擁有權(quán)。它對(duì)于避免內(nèi)存泄漏(resource leak)——如 new 后忘記 delete ——特別有用。

          shared_ptr

          多個(gè)智能指針可以共享同一個(gè)對(duì)象,對(duì)象的最末一個(gè)擁有著有責(zé)任銷毀對(duì)象,并清理與該對(duì)象相關(guān)的所有資源。

          • 支持定制型刪除器(custom deleter),可防范 Cross-DLL 問題(對(duì)象在動(dòng)態(tài)鏈接庫(DLL)中被 new 創(chuàng)建,卻在另一個(gè) DLL 內(nèi)被 delete 銷毀)、自動(dòng)解除互斥鎖

          weak_ptr

          weak_ptr 允許你共享但不擁有某對(duì)象,一旦最末一個(gè)擁有該對(duì)象的智能指針失去了所有權(quán),任何 weak_ptr 都會(huì)自動(dòng)成空(empty)。因此,在 default 和 copy 構(gòu)造函數(shù)之外,weak_ptr 只提供 “接受一個(gè) shared_ptr” 的構(gòu)造函數(shù)。

          • 可打破環(huán)狀引用(cycles of references,兩個(gè)其實(shí)已經(jīng)沒有被使用的對(duì)象彼此互指,使之看似還在 “被使用” 的狀態(tài))的問題

          unique_ptr

          unique_ptr 是 C++11 才開始提供的類型,是一種在異常時(shí)可以幫助避免資源泄漏的智能指針。采用獨(dú)占式擁有,意味著可以確保一個(gè)對(duì)象和其相應(yīng)的資源同一時(shí)間只被一個(gè) pointer 擁有。一旦擁有著被銷毀或編程 empty,或開始擁有另一個(gè)對(duì)象,先前擁有的那個(gè)對(duì)象就會(huì)被銷毀,其任何相應(yīng)資源亦會(huì)被釋放。

          • unique_ptr 用于取代 auto_ptr

          auto_ptr

          被 c++11 棄用,原因是缺乏語言特性如 “針對(duì)構(gòu)造和賦值” 的 std::move 語義,以及其他瑕疵。

          auto_ptr 與 unique_ptr 比較

          • auto_ptr 可以賦值拷貝,復(fù)制拷貝后所有權(quán)轉(zhuǎn)移;unqiue_ptr 無拷貝賦值語義,但實(shí)現(xiàn)了move 語義;
          • auto_ptr 對(duì)象不能管理數(shù)組(析構(gòu)調(diào)用 delete),unique_ptr 可以管理數(shù)組(析構(gòu)調(diào)用 delete[] );

          強(qiáng)制類型轉(zhuǎn)換運(yùn)算符

          static_cast

          • 用于非多態(tài)類型的轉(zhuǎn)換
          • 不執(zhí)行運(yùn)行時(shí)類型檢查(轉(zhuǎn)換安全性不如 dynamic_cast)
          • 通常用于轉(zhuǎn)換數(shù)值數(shù)據(jù)類型(如 float -> int)
          • 可以在整個(gè)類層次結(jié)構(gòu)中移動(dòng)指針,子類轉(zhuǎn)化為父類安全(向上轉(zhuǎn)換),父類轉(zhuǎn)化為子類不安全(因?yàn)樽宇惪赡苡胁辉诟割惖淖侄位蚍椒ǎ?/section>

          dynamic_cast

          • 用于多態(tài)類型的轉(zhuǎn)換
          • 執(zhí)行行運(yùn)行時(shí)類型檢查
          • 只適用于指針或引用
          • 對(duì)不明確的指針的轉(zhuǎn)換將失敗(返回 nullptr),但不引發(fā)異常
          • 可以在整個(gè)類層次結(jié)構(gòu)中移動(dòng)指針,包括向上轉(zhuǎn)換、向下轉(zhuǎn)換

          const_cast

          • 用于刪除 const、volatile 和 __unaligned 特性(如將 const int 類型轉(zhuǎn)換為 int 類型 )

          reinterpret_cast

          • 用于位的簡單重新解釋
          • 濫用 reinterpret_cast 運(yùn)算符可能很容易帶來風(fēng)險(xiǎn)。除非所需轉(zhuǎn)換本身是低級(jí)別的,否則應(yīng)使用其他強(qiáng)制轉(zhuǎn)換運(yùn)算符之一。
          • 允許將任何指針轉(zhuǎn)換為任何其他指針類型(如 char* 到 int* 或 One_class* 到 Unrelated_class* 之類的轉(zhuǎn)換,但其本身并不安全)
          • 也允許將任何整數(shù)類型轉(zhuǎn)換為任何指針類型以及反向轉(zhuǎn)換。
          • reinterpret_cast 運(yùn)算符不能丟掉 const、volatile 或 __unaligned 特性。
          • reinterpret_cast 的一個(gè)實(shí)際用途是在哈希函數(shù)中,即,通過讓兩個(gè)不同的值幾乎不以相同的索引結(jié)尾的方式將值映射到索引。

          bad_cast

          • 由于強(qiáng)制轉(zhuǎn)換為引用類型失敗,dynamic_cast 運(yùn)算符引發(fā) bad_cast 異常。

          bad_cast 使用

          try {
          ? ?Circle& ref_circle = dynamic_cast(ref_shape);
          }
          catch (bad_cast b) {
          ? ?cout << "Caught: " << b.what();
          }

          運(yùn)行時(shí)類型信息 (RTTI)

          dynamic_cast

          • 用于多態(tài)類型的轉(zhuǎn)換

          typeid

          • typeid 運(yùn)算符允許在運(yùn)行時(shí)確定對(duì)象的類型
          • type_id 返回一個(gè) type_info 對(duì)象的引用
          • 如果想通過基類的指針獲得派生類的數(shù)據(jù)類型,基類必須帶有虛函數(shù)
          • 只能獲取對(duì)象的實(shí)際類型

          type_info

          • type_info 類描述編譯器在程序中生成的類型信息。此類的對(duì)象可以有效存儲(chǔ)指向類型的名稱的指針。type_info 類還可存儲(chǔ)適合比較兩個(gè)類型是否相等或比較其排列順序的編碼值。類型的編碼規(guī)則和排列順序是未指定的,并且可能因程序而異。
          • 頭文件:typeinfo

          typeid、type_info 使用

          #include 
          using namespace std;

          class Flyable ? ? ? ? ? ? ? ? ? ? ? // 能飛的
          {
          public:
          ? ?virtual void takeoff() = 0; ? ? // 起飛
          ? ?virtual void land() = 0; ? ? ? ?// 降落
          };
          class Bird : public Flyable ? ? ? ? // 鳥
          {
          public:
          ? ?void foraging() {...} ? ? ? ? ? // 覓食
          ? ?virtual void takeoff() {...}
          ? ?virtual void land() {...}
          ? ?virtual ~Bird(){}
          };
          class Plane : public Flyable ? ? ? ?// 飛機(jī)
          {
          public:
          ? ?void carry() {...} ? ? ? ? ? ? ?// 運(yùn)輸
          ? ?virtual void takeoff() {...}
          ? ?virtual void land() {...}
          };

          class type_info
          {
          public:
          ? ?const char* name() const;
          ? ?bool operator == (const type_info & rhs) const;
          ? ?bool operator != (const type_info & rhs) const;
          ? ?int before(const type_info & rhs) const;
          ? ?virtual ~type_info();
          private:
          ? ?...
          };

          void doSomething(Flyable *obj) ? ? ? ? ? ? ? ? // 做些事情
          {
          ? ?obj->takeoff();

          ? ?cout << typeid(*obj).name() << endl; ? ? ? ?// 輸出傳入對(duì)象類型("class Bird" or "class Plane")

          ? ?if(typeid(*obj) == typeid(Bird)) ? ? ? ? ? ?// 判斷對(duì)象類型
          ? ?{
          ? ? ? ?Bird *bird = dynamic_cast(obj); // 對(duì)象轉(zhuǎn)化
          ? ? ? ?bird->foraging();
          ? ?}

          ? ?obj->land();
          }

          int main(){
          Bird *b = new Bird();
          doSomething(b);
          delete b;
          b = nullptr;
          return 0;
          }


          END

          原文地址:https://github.com/huihut/interview#effective

          本文轉(zhuǎn)載自“嵌入式大雜燴”公眾號(hào),版權(quán)歸原作者所有,如有侵權(quán),請(qǐng)聯(lián)系刪除。

          瀏覽 60
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一区二区三区人妖视频 | 欧美乱伦熟妇 | 操逼网电影| 三级在线视频观看 | 亚洲V免费看 |