C++繼承之成員變量

C++語言中類的繼承是C++重要特性之一,暫且將類的繼承分爲成員變量以及成員函數兩個方面。我們知道標準C++選擇的方式是每個類對象存儲一份自己的成員變量,那麼這份成員變量的存儲區是怎麼佈局的呢?下面分幾種情況記錄一些自己看法和體會(部分參考《Inside The C++ Object Model》)。

 

1,非靜態成員變量的單一繼承且不含VIRTUAL函數

 

C++標準並沒有對繼承來的基類的成員變量和派生類自身的成員變量的佈局做一個順序上的規定,而是由編譯器來決定。大部分的編譯器都是將基類成員變量放在前面,且變量之間遵循其聲明的次序。

 

例如:class A                              class B : public class A

{                                                {

      public:                                     public:

                int       val;                               char  a2;

                char    a1;

}                                                 }

 

對於A類對象其內存佈局很簡單,和一個struct結構體的內存佈局是相同的。32位計算機情況下,由於內存對齊的原因,其所佔的內存大小應該爲4 + 1 + 3 = 8字節,當B以public繼承方式繼承A的成員變量後, B的內存佈局即爲A的成員變量位於其內存分佈的前端,注意:此時B繼承並不僅僅繼承A中實際變量所佔的內存字節,其爲了內存對齊而補充的字節也一併繼承,即B中繼承的來自A的成員變量的內存字節數也爲8字節,而不是4+1=5字節。然後B定義的自身的成員變量緊跟於繼承而來的成員變量之後,同時也要注意內存對齊!則B的成員變量所佔字節總數應該是8 + 1 +3 = 12字節。

 

至於爲什麼要將內存對齊而補充的空間一併繼承據《inside……》一書介紹,是因爲在C++中經常會有利用基類指針指向一個派生類成員的情況出現,例如 B m_b; A* PA = &m_b; 由指針的性質知道,PA所能指向的內存區的大小應該爲數據類型A所佔的大小,即爲8字節,如果繼承時未將對齊而填充的字節也一併繼承,則m_b整個內存字節爲4+1+1+2 = 8字節,此時用PA竟然可以訪問不屬於A類型的成員,即a2,這是不符合C++語義的。因爲利用基類指針只可以訪問派生類對象中繼承的基類成員。這只是我的一種理解,不知是否正確,而《inside……》書中是以複製操作來解釋的,具體見P106。

 

2,非靜態成員的單一繼承,且基類中聲明瞭虛函數

 

若基類中定義了虛函數,首先基類對象的內存佈局中會增加對虛函數的支持,即一個指向VIRTUAL TABLE的指針VPTR成員,在運行期將使得每個對象利用此指針找到相應的virtual table。而構造函數應該支持對該指針賦正確的虛表地址。那麼VPTR如何佈局到對象的內存區域呢?一般來說該指針都被放在對象內存區域最前端!因此派生類繼承了基類成員之後,其對象內存區的前端也應該是一個虛表指針變量!

若基類中沒有定義虛函數,而派生類中定義了虛函數,則基類對象中不會添加虛表指針這個變量,而派生類中對象會添加虛表指針成員,且將此指針放於內存區的前端,即繼承而來的基類成員變量之前,在此種情況下如果利用一個基類指針指向派生類的對象,利用該指針訪問繼承來的基類成員時需要編譯器作出調整,該基類指針應該作出適當偏移,以跳過這個派生類的虛表指針。

若基類定義了虛函數,派生類也定義了虛函數(不管是否重載基類虛函數還是定義新的虛函數),由虛表的性質,派生類的虛表會由繼承的基類的虛函數列表和自身虛函數列表共同構成,且基類虛函數在前。因爲只有一張虛函數表,因此在派生類對象中只會有一個虛表指針成員,該指針也會被放於內存分佈的最前端。由於基類對象有虛表指針,此時用基類指針來指向一個派生類對象並訪問繼承的基類成員時,不需要編譯器作出偏移調整。

3,非靜態成員的多重繼承且無虛函數

多重繼承下,繼承的各個基類成員變量的排列爲:視各個基類對象內存佈局爲一個單元,按照多重繼承的順序,從左到右的先後順序在內存區上先後排列!最後爲派生類自己定義的成員變量!

4,非靜態成員的多重繼承且有虛函數

此時虛表指針的問題又出現了:

1)基類定義了虛擬函數,而派生類未定義虛擬函數。此時定義了虛擬函數的基類對象內存佈局中有虛表指針成員,因此有多少個定義了虛函數的基類就會在派生類對象內存佈局中有多少個虛表指針,分別指向繼承來的各個基類的虛函數表!且各個虛表指針成員位於該繼承來的基類對象內存單元的前端。

2)基類定義了虛擬函數,(待續)

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章