天天看點

深度探索C++對象模型【第三章1】

1:編譯器會為每一個空class安插一個char,使得這個class的兩個objects對象在記憶體中配置獨一無二的記憶體位址。這就解釋了空類的大小為何為1(即使該類為派生類,也需要為其安插一個char來區分其對象)。(傳統意義上這個char會被放到class 的尾端)

2:class 的大小主要和以下三種因素相關:

  • 虛拟機制帶來的負擔:一個指針,或是指向一個相關的vbtl,或是一個虛基類對象(或是其偏移位址)
  • 編譯器對特殊情況所提供的優化處理(某些編譯器會對empty virtual base class 進行特殊處理 VC++)
  • 補齊機制alignment

3:VC++編譯器對空虛基類的特殊處理:空虛基類被視為派生類對象的最開頭部分,也就是說其沒有占據任何的額外空間,這就節省了第一點所說的1個位元組。

4:編譯器的潛在差異正說明了C++對象模型的演化。

5:一個虛基類對象隻會在派生類中存在一份執行個體,無論它在class繼承體系中出現多次少次。

6:static data member 放置的是整個class感興趣的資料,而nonstatic data member 放置的是個别對象感興趣的資料。

7:對于static data member ,它被放置在一個全局程式段中,且并不會影響到個别對象的大小。在程式中,無論一個類産生了多少個對象,static data member 永遠隻存在一份執行個體(即使沒有對象,該執行個體也存在)(template class 中稍有不同~)

8:在早期的C++編譯器中,為了正确的資料綁定,通常會把

  • 所有的data member 都放在class聲明的開頭處
  • 所有的inline function 不管大小都放在class的聲明之外(但是C++2.0之後,對于member function的分析,會直到整個class的聲明都出現後再開始)

9:請将所有的内置類型都放置在class的開始處,因為可能仍需要這種防禦性的風格。

10:C++标準要求在同一個通路權限區段(public、private/protected)中,成員的排列需要符合“較晚出現的成員在對象中有較高的位址”(也就是從低到高排列),目前各家編譯器的操作都是将各個區塊的成員連續存儲,中間不會夾雜任何内容。

11:當一個類有虛拟機制時,其對象中會有vptr,不同的編譯器,可能會放在object的最前面或者最後面。

12:對于靜态資料成員,由于其在程式中隻有一份執行個體,且與object無關。使用對象或者對象指針或者類來存取無大的差異。若取一個靜态資料成員的位址,會得到一個指向其資料類型的指針,而不是一個指向其類成員的指針。因為靜态成員并不内含在一個類對象中。

13:如果兩個類有相同名稱的靜态成員,當其存在于data segment時,會出現命名沖突,編譯器的解決方法是暗中對每一個static data member 編碼(name_mangling),這樣可以獲得一個獨一無二的程式識别代碼。

14:Nonstatic data member 直接存放在每一個類對象中,必須經由類對象來存取,事實上成員函數中資料成員的直接存取,是經由this指針來完成的。

15:想要對一個非靜态資料成員進行存取,編譯器會把類對象的起始位址加上一定的data member偏移位址。

A a;
a.x = 0.0;
//其中的&a.x = &a +(&A::x - 1);
           

其中的減1,是因為指向資料成員的指針,其offset值總是被加上1,這樣可以使得編譯系統分辨出“一個指向資料成員的指針,用以指出class的第一個成員member”和“一個指向資料成員的指針,沒有指出任何member”,兩種情況。

解釋:

float A::*p1 =0;  
float A::*p2 = &A::x;  
//A::* 的意思是“指向A data member”的指針類型  
           

其中p1對應的是:一個指向data member的指針,卻沒有指出任何member;p2對應的是:一個指向data member的指針,用以指出class的第一個member。

至于“-1”的正解應該是這樣的:成員指針其實就是成員相對于目前對象的偏移量,按照這個說法p2指向的是A的第一個成員,理論上應該是0的,但是由于0被NULL指針定義了,是以認為地規定p2=1,以此類推。

繼續閱讀