若是一個類只定義了類名,沒定義任何方法和字段,如class A{};那麼class A的每一個實例佔用1個字節的內存,編譯器會會在這個其實例中安插一個char,以保證每一個A實例在內存中有惟一的地址,如A a,b;&a!=&b。若是一個直接或是間接的繼承(不是虛繼承)了多個類,若是這個類及其父類像A同樣沒有方法沒有字段,那麼這個類的每一個實例的大小都是1字節,若是有虛繼承,那就不是1字節了,每虛繼承一個類,這個類的實例就會多一個指向被虛繼承父類的指針。還有一點值得說明的就是像A這樣的類,編譯器不必定會產生傳說中的那6個方法,這些方法只會在須要的時候產生,如class A沒有被任何地方使用那這些方法編譯器就沒有必要產生,若是這個類實例化了,那麼會產生default constructor,而destructor則不必定產生。html
若是一個類中有static data member,nonstatic data member,還有const data member,enum,那麼它的內存佈局會是什麼樣的呢,看下面簡單的類Point:程序員
class Point { public: Point():maxCount(10){} private: int X; static int count; int Y; const int maxCount ; enum{ minCount=2 }; };
Sizeof(Point)=12,爲何佔12字節呢,我相信不少人都知道是哪幾個成員變量佔用的,就是X,Y,maxCount,maxCount做爲常量字段,但在Point的每一個實例中可能有不一樣的值,固然屬於Point實例的一部分,若是把maxCount定義成static,那它就不不是Point實例的一部分了,若是定義成static const int maxCount=1;則maxCount分配在.data段中,若是沒有初始化則分配在.bss段中,反正跟Point的實例無關,count分配在.bss段中,minCount分配在.rdata段中,總之count,maxCount,minCount在編譯鏈接完成以後,內存(虛擬地址)就分配好了,在程序加載的時候,會把他們的虛擬地址對應上實際的物理地址。佈局
Data member的內存佈局:nonstatic data member在class object中的順序和其申明的順序同樣,static data member和const member不在class object中由於他們只有一份,被class object共享,因此static data member和const data member,枚舉並不會響應class object的大小。關於段的信息,我以爲是每一個C/C++程序員必須知道的。而Point每次實例化的時候則只須要分配X,Y,maxCount須要的內存。性能
每一個類的data member在內存中應該是連續的,若是出現數據對齊的狀況,可能中間會有空白地帶。請看下面幾個類:spa
class AA { protected: int X; char a; }; class BB:public AA { protected: char b; }; class CC:public BB { protected: char c; };
Sizeof(AA)=8//對齊3字節指針
Sizeof(BB)=12//兩個3字節對齊code
Sizeof(CC)=16//編譯器「無恥」的用了3個3字節對齊htm
關於內存對齊問題我相信你們常常遇到,我就不廢話,我以前寫過一篇關於內存對齊的文章《數據對齊》對象
編譯器爲何要無恥的在class CC中加3個3字節對齊呢,這樣每一個CC的實例就大了9字節。若是編譯器不加這9字節的空白,那麼CC的每一個實例就是8字節,前面的X佔4字節,後面的a,b,c佔3字節,加1字節的空白對齊,恰好8字節,沒有誰很傻很天真的覺得最好是佔7字節吧。blog
若是CC佔用8字節內存,一樣的AA,BB都是8字節的內存,這樣的話,若是把一個指向AA實例的指針賦給一個指向CC實例的指針,那麼就會把AA中的8字節直接蓋到CC的8字節上,結果CC實例中的b,c都被賦上了不是咱們想要的值,這極可能會致使你的程序出問題。
父類的data member會在子類的實例中有完整的一份,這樣在有繼承關係的類之間進行類型轉換,就只用簡單的修改指針的指向。
Data Member的存取。對一個data member的存取,編譯器把對象實例的起始地址加上data member的偏移量。如CC c;
c.X=1;至關於&c+(&CC::X-1),減一實際上是爲了區分是指向object的指針仍是指向data member的指針,指向data member的要減一。每個data member的偏移量在編譯的時候是知道的,根據成員變量的類型和內存對齊,存在virtual繼承或是虛方法的狀況編譯器會自動加上一些輔助的指針,如指向虛方法的指針,指向虛繼承父類的指針等。
在data member的存取效率上,struct member 、class member、單一繼承或是多重繼承的狀況下效率都是同樣的,由於他們的存儲其實都是&obj+(&class.datamember-1)。在虛繼承的狀況下,可能會影響存儲性能,如經過一個指針來存取一個指向虛繼承而來的data member,那麼性能會有影響,由於在虛繼承的時候,在編譯的時候還不能肯定這個data member是來自子類仍是父類,只有在運行的時候才能推斷出來,其實就是多了一步指針的操做,在虛繼承中,若是是經過對象實例來操做虛繼承而來的data member,則不會有任何性能問題,由於不存在什麼多態性,全部東西在編譯的時候內存地址都肯定了。
虛繼承仍是虛方法爲了實現多態同樣,多了一步,若是不須要多態,而是經過對象實例調用相關的方法就不會有性能問題。