坑爹的虛繼承vs虛函數繼承

來源:互聯網
上載者:User

一:首先來總結一下兩者:

虛繼承:可以解決多重繼承的問題,使得基類可以被多次繼承。

如:

class A{//成員}class B:public A {//成員}class C:public A {//成員a}class D:public B,public C{//成員a}

這樣在建立D的時候,會建立2個A類,增加了開銷。並且如果有

D d;

d.a=0;//會有錯誤產生,因為不能確定是從B還是C類中繼承來的成員a

//必須改成下面的文法才正確

D d;

d.B::a = 0;

d.C::a = 0;

若改為:

class A{//成員}class B:public virtual A {//成員}class C:public virtual A {//成員函數test()}class D:public B,public C{//成員函數test()}

這樣在建立D的時候,只用建立1個共用的A類,相較於上一段代碼,節省了開銷

虛函數繼承:是物件導向多台的主要方式,在成員函數的聲明中加入virtual來實現,說白了就是可以在衍生類別中對基類的續成員函數重新編寫實現不同的操作。而且虛函數的特性會被繼承。

例:

class A{public:virtual test(){//實現1}}class B:public A {virtual test(){//實現2}}

這樣B類中的test()覆蓋了A類中的test(),且B類中的test()成員函數不必加上virtual限定也會預設其類型是virtual

二:現在我要描述我遇到的坑爹問題了:關於虛函數表的問題

當一個類有1個或多個虛函數時,編譯器為了實現虛函數,會為這個類建立一個虛函數表vtbl。這個表記錄了一個類中每一個虛函數成員的記憶體位址(按順序排列)。例如,基類中含有一個指標,指向該虛函數表;衍生類別對象裡則包含有一個獨立的指標,這個指標指向上面說的虛函數表。

對於每個類,編譯器都要建立一個虛函數地址表(以數組的形式實現就可以了)!

現在,有了第一個問題:衍生類別中重新定義了從基類繼承來的虛函數,這個虛函數表應該怎麼改進!!

如果一個虛函數被繼承下來,而且在衍生類別中沒有改變,那麼這個虛函數表中的記憶體位址不會有改變,這個衍生類別的虛指標仍然指向這個虛函數在基類中的定義;

但是如果在衍生類別中改變了,虛函數表儲存衍生類別中重新定義後該虛函數的地址,是的指標會指向衍生類別中對這個虛函數的新定義上。

若衍生類別中新增加了虛函數,則將這個虛函數的地址也加入到虛函數表中。

但是值得注意的是:無論,在類中包含了多少個虛函數,都只需要在對象添加一個地址成員就可以了,改變的只是表的大小!

聽著像繞口令。。。。。。我用《程式員面試寶典》上的一個例子來讓自己理解記憶下!!

1都不是虛繼承:此時,衍生類別和基類使用的是同一個虛函數表,當衍生類別對基類中的虛成員函數重新實現的時候,直接將新的函數實現覆蓋基類的函數實現。

#include<iostream>#include<memory.h>#include<assert.h>using namespace std;class A{char k[3];public:virtual void aa(){};};class B : public A//非虛繼承{char j[3]; //加入一個變數是為了看清楚class中的vfptr放在什麼位置public:virtual void bb(){};};class C : public  B//非虛繼承{char i[3];public:virtual void cc(){};};int main(int argc, char *argv[]){cout<<"sizeof(A)"<<sizeof(A)<<endl; cout<<"sizeof(B)"<<sizeof(B)<<endl; cout<<"sizeof(C)"<<sizeof(C)<<endl; return 0;}

運行結果為:sizeof(A)8; sizeof(B)12;sizeof(C)16

也就是說A中記憶體為4(A類的虛函數表指標)+3(char數組長度)=8(編譯器補齊)

B中記憶體為4(B類虛函數表指標)+3(A類中數組長度)+3(B類中數組長度)=12(編譯器補齊)

C中記憶體為4(C類虛函數表指標)+3(A類中數組長度)+3(B類中數組長度)+3(C類中數組長度)=16(編譯器補齊)

2當B虛繼承A類,C不虛繼承B類時

#include<iostream>#include<memory.h>#include<assert.h>using namespace std;class A{char k[3];public:virtual void aa(){};};class B : public virtual A//虛繼承{char j[3]; //加入一個變數是為了看清楚class中的vfptr放在什麼位置public:virtual void bb(){};};class C : public  B//非虛繼承{char i[3];public:virtual void cc(){};};int main(int argc, char *argv[]){cout<<"sizeof(A)"<<sizeof(A)<<endl; cout<<"sizeof(B)"<<sizeof(B)<<endl; cout<<"sizeof(C)"<<sizeof(C)<<endl; return 0;}

運行結果為:sizeof(A)8; sizeof(B)20;sizeof(C)24

也就是說A中記憶體為4(A類的虛函數表指標)+3(char數組長度)=8(編譯器補齊)

B中記憶體為4(B類虛函數表指標)+4(B虛繼承時指向基類A的指標)+3(B類中數組長度)+8(A類記憶體大小)=20(編譯器補齊)

C中記憶體為20(B類的記憶體大小,C不是虛繼承,可以與B類功用一個虛函數表)+4(C類中數組長度)=24(編譯器補齊)

3當B、C均為虛繼承時

#include<iostream>#include<memory.h>#include<assert.h>using namespace std;class A{char k[3];public:virtual void aa(){};};class B : public virtual A{char j[3]; //加入一個變數是為了看清楚class中的vfptr放在什麼位置public:virtual void bb(){};};class C : public virtual B{char i[3];public:virtual void cc(){};};int main(int argc, char *argv[]){cout<<"sizeof(A)"<<sizeof(A)<<endl; cout<<"sizeof(B)"<<sizeof(B)<<endl; cout<<"sizeof(C)"<<sizeof(C)<<endl; return 0;}

運行結果為:sizeof(A)8; sizeof(B)20;sizeof(C)32

也就是說A中記憶體為4(A類的虛函數表指標)+3(char數組長度)=8(編譯器補齊)

B中記憶體為4(B類虛函數表指標)+4(B虛繼承時的虛類指標指向A類)+3(B類中數組長度)+8(A類的記憶體大小)=20(編譯器補齊)

C中記憶體為4(C類虛函數表指標)+4(C虛繼承時的虛類指標指向B類)+3(C類中數組長度)+20(A類的記憶體大小)=32(編譯器補齊)

菜鳥起步,歡迎拍磚。。。。。。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.