[讀書筆記] 深入探索C++物件模型-第三章 Data語意學(中)__C++

來源:互聯網
上載者:User

繼續整理第三章的內容(以下部分圖片來自於原書):

1. 在只有繼承沒有多態的情況下,子類是的內容就是父類加上子類特有的資料成員,例如,對於如下兩個類,Point2d和Point3d,後者公有繼承自前者,此時的資料布局如下所示:


在某些情況下,把一個類分解成多層,可能會導致類所佔用空間的膨脹,例如,如下類,算上對齊操作大小為8 (4 + 1 + 1+1 +(1對齊)):


而如果相同的資料(val,c1,c2,c3),被分散的放入繼承體系之中,現在一個包含所有這些資料的類Concrete3的大小就膨脹到了16,這是因為上面的無繼承的類設計中,只有一個位元組被用來對齊,而此種繼承體系之下,有9個位元組被用來對齊:


這時的類的布局如下:


為什麼不把Concrete2和Concrete3的資料填補到Concrete1用於對齊的空間中呢。原因時,在此種情況下,當發生Concrete1的複製操作時,會破壞Concrete2的內容:


2. 加入多態時的情況,例如,街上上述的例子,對於Point2d,現在加入幾個虛函數,用以支援多態特性:


此時會帶來額外的空間以及存取時間上的額外負擔:

a. 虛函數表會被產生出來(virtual table)。

b. 每一個類對象中會加入一個指向上述虛表的指標(vptr)。

c. 加強建構函式,使之可以為vptr設定初值。

d. 加強解構函式,使之可以清除指向虛函數表的vptr。

此時的類的記憶體布局會增加一個指向虛表的指標(該指標可能放在頭部也可能放在尾部):


3. 多種繼承的情況,在單繼承中,可以看到,基類和子類的對象都是從相同的地址開始的,差異只是子類比較大,例如以下操作,

P3d p3d; P2d* p = &p3d;

以基類指標指向子類指標並不需要修改地址,多重繼承則不一樣,因為第二個乃至後面的基類起始地址與對應基類的地址並不一樣,例如對於如下繼承體系以及對應的記憶體布局:


從上圖看以看出,最左端基類(P2d和P3d)的起始地址和子類V3d是一樣的,而之後的基類Vertex則和子類不一致,因此,對於如下對象和指標:

Vertex3d v3d;Vertex* pv;Point2d* p2d;Point3d* p3d;

如下的賦值操作:pv = v3d; 需要內部轉化為:pv = (Vertex*)(((char*)&v3d) + sizeof(Point3d));即需要位移才可以指向子類中對應的該基類的部分,而對於如下賦值:p2d = &v3d;p3d = &v3d; 則不需要任何調整。

如果要存取第二個或者後繼基類中的一個資料成員,並不需要額外負擔,因為資料成員的位置在編譯時間期就固定了,因此存取只是一個簡答的位移(offset)運算,並不需要額外成本。

4. 虛擬繼承的情況,考慮如下繼承體系,Vertex和Point3d虛擬繼承自Point2d,Vertex3d共有繼承(非虛繼承)自Vertex和Point3d


a. 此時必須有在子類對象中安插指標指向虛基類,一種可能的布局如下,子類需要維護指向虛基類地址的指標,此種策略下,對於Point3d運算子:

void Point3d::operator+=(const Point3d& rhs){    _x += rhs._x;    _y += rhs._y;    _z += rhs._z;};
會內部轉化為:

pPoint2d->_x += rhs.pPoint2d->_x;pPoint2d->_y += rhs.pPoint2d->_y;_z += rhs._z;
而對於一個子類和基類的轉換:

Point2d* p2d = pv3d; //Vertex3d* pv3d

會被轉化為(必須判斷pv3d是否為空白,否則可能導致不正確的賦值):

Point2d* p2d = pv3d ? pv3d->pPoint2d : 0;


b. 另一種策略是將虛基類的位移量(而不是地址)存入虛表中(以下例子中,虛表中的正值索引會索引到虛函數地址,負值索引會索引到虛基類位移量),也就是與虛函數放到一個表中,針對上例,此種策略下可能的布局如下,此種策略下,上述+=運算子會被轉化為:

(this + __vptr__Point3d[-1])->_x += (rhs + rhs.__vptr__Point3d[-1])->_x;(this + __vptr__Point3d[-1])->_y += (rhs + rhs.__vptr__Point3d[-1])->_y;_z += rhs._z;
對於

Point2d* p2d  = pv3d;

會轉化為:

Point2d* p2d  = pv3d 。 pv3d + pv3d->__vptr__Point3d[-1] : 0;

由於虛擬繼承的存在帶來了額外的負擔以及高度的複雜性,所以,一般而言,“虛基類的最有效運用形式就是:一個抽象的虛基類,沒有任何資料成員。”

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.