1. 程式人生 > >C++物件模型之記憶體佈局三(虛繼承)

C++物件模型之記憶體佈局三(虛繼承)

經過兩天的摸索,今天終於搞清楚C++物件模型.前兩篇已經講解了單繼承,多重繼承和多繼承的物件模型.今天講解菱形繼承,雖然過程艱難,但是收穫豐富.

簡單虛繼承物件模型

首先編寫如下的測試程式:


      
       1
      
      
       2
      
      
       3
      
      
       4
      
      
       5
      
      
       6
      
      
       7
      
      
       8
      
      
       9
      
      
       10
      
      
       11
      
      
       12
      
      
       13
      
      
       14
      
      
       15
      
      
       16
      
      
       17
      
      
       18
      
      
       19
      
      
       20
      
      
       21
      
      
       22
      
      
       23
      
      
       24
      
      
       25
      
      
       26
      
      
       27
      
      
       28
      
      
       29
      
      
       30
      
      
       31
      
      
       32
      
      
       33
      
      
       34
      
      
       35
      
      
       36
      
      
       37
      
      
       38
      
      
       39
      
      
       40
      
      
       41
      
      
       42
      
      
       43
      
      
       44
      
      
       45
      
      
       46
       

      
       #include <iostream>
      
      
       using 
       namespace 
       std;
      
      
       class A
      
      
       {
      
      
       public:
      
      
        A(
       int a1=
       0,
       int a2=
       0):a1(a1),a2(a2){}
      
       
       virtual
void f()
{ cout<< "A::f()"<< endl;} virtual void af(){ cout<< "A::af()"<< endl;} int a1; int a2; }; class C: virtual
public A { public: C( int a1= 1, int a2= 2, int c1= 4):A(a1,a2),c1(c1){ } virtual void f1(){ cout<< "C::f1()"<< endl;} virtual void cf(){ cout<< "C::cf()"<< endl;} int c1; }; typedef void (*pfun)(); int main(void) { C *cp= new C; pfun fun= NULL; cout<< "The C's virtual table->"<< endl; for( int i= 0;i< 2;i++) { fun=(pfun)*(( long*)*( long*)cp+i); fun(); } cout<< "c1="<<*(( int*)cp+ 2)<< endl; cout<< "The A's virtual table->"<< endl; long* p=( long*)cp+ 2; for( int i= 0;i< 2;i++) { fun=(pfun)*(( long*)*( long*)p+i); fun(); } cout<< "a1="<<*(( int*)p+ 2)<< endl; cout<< "a2="<<*(( int*)p+ 3)<< endl; return 0; }

上述程式的輸出如下:簡單虛基類驗證程式輸出簡單解釋下:

  1. 當存在虛基類時,先是子類的成員,然後才是虛基類的成員.

以下是C物件的物件模型:簡單虛繼承物件模型

通過在gdb下,輸入指令:


      
       1
      
      
       2
      
      
       3
       

      
       set p obj 
       on
      
      
       set p pretty 
       on 
      
      
       p *this(要執行到成員函式裡面)
       

也可以輸出C物件的物件模型.截圖如下:通過gdb顯示C物件模型

我在理解這個的時候,有分析過c物件呼叫虛基類的成員方法.通過反彙編程式碼,我發現當cp呼叫A中方法時,它先從C類的虛擬函式表首地址-24位元組處獲取A子物件相對於cp的偏移量16.所以C的虛擬函式表首地址負方向的空間還是有研究的地方。.

當我把C物件的函式f1改成f時,即重寫A中的f方法,這時cp中A的子物件中f方法將被C的f方法替換,但是程式輸出有錯,原因不明。如下:C重寫A的f方法

菱形繼承下的物件模型

編寫如下程式:


      
       1
      
      
       2
      
      
       3
      
      
       4
      
      
       5
      
      
       6
      
      
       7
      
      
       8
      
      
       9
      
      
       10
      
      
       11
      
      
       12
      
      
       13
      
      
       14
      
      
       15
      
      
       16
      
      
       17
      
      
       18
      
      
       19
      
      
       20
      
      
       21
      
      
       22
      
      
       23
      
      
       24
      
      
       25
      
      
       26
      
      
       27
      
      
       28
      
      
       29
      
      
       30
      
      
       31
      
      
       32
      
      
       33
      
      
       34
      
      
       35
      
      
       36
      
      
       37
      
      
       38
      
      
       39
      
      
       40
      
      
       41
      
      
       42
      
      
       43
      
      
       44
      
      
       45
      
      
       46
      
      
       47
      
      
       48
      
      
       49
      
      
       50
      
      
       51
      
      
       52
      
      
       53
      
      
       54
      
      
       55
      
      
       56
      
      
       57
      
      
       58
      
      
       59
      
      
       60
      
      
       61
      
      
       62
      
      
       63
      
      
       64
      
      
       65
      
      
       66
      
      
       67
      
      
       68
      
      
       69
      
      
       70
       

      
       #include <iostream>
      
      
       using 
       namespace 
       std;
      
      
       class A
      
      
       {
      
      
       public:
      
      
        A(
       int a1=
       0,
       int a2=
       0):a1(a1),a2(a2){}
      
       
       virtual void f(){
       cout<<
       "A::f()"<<
       endl;}
      
       
       virtual void Af(){
       cout<<
       "A::Af()"<<
       endl;}
      
       
       int a1;
      
       
       int a2;
      
      
       };
      
      
       class B1:
       virtual 
       public A
      
      
       {
      
      
       public:
      
      
        B1(
       int a1=
       0,
       int a2=
       0,
       int b1=
       0):A(a1,a2),b1(b1){}
      
       
       virtual void f(){
       cout<<
       "B1::f()"<<
       endl;}
      
       
       virtual void f1(){
       cout<<
       "B1::f1()"<<
       endl;}
      
       
       virtual void Bf1(){
       cout<<
       "B1::Bf1()"<<
       endl;}
      
       
       int b1;
      
      
       };
      
      
       class B2:
       virtual 
       public A
      
      
       {
      
      
       public:
      
      
        B2(
       int b2=
       0):b2(b2){}
      
       
       virtual void f(){
       cout<<
       "B2::f()"<<
       endl;}
      
       
       virtual void f2(){
       cout<<
       "B2::f2()"<<
       endl;}
      
       
       virtual void Bf2(){
       cout<<
       "B2::Bf2()"<<
       endl;}
      
       
       int b2;
      
      
       };
      
      
      
      
       class C: 
       public B1,
       public B2
      
      
       {
      
      
       public:
      
      
        C(
       int a1=
       0,
       int a2=
       0,
       int b1=
       0,
       int b2=
       0,
       int c1=
       0):B1(
       1,
       2,
       3),B2(
       6),c1(
       7){}
      
       
       virtual void f(){
       cout<<
       "C::f()"<<
       endl;}
      
       
       virtual void f1(){
       cout<<
       "C::f1()"<<
       endl;}
      
       
       virtual void f2(){
       cout<<
       "C::f2()"<<
       endl;}
      
       
       virtual void Cf(){
       cout<<
       "C::Cf()"<<
       endl;}
      
       
       int c1;
      
      
       };
      
      
      
       typedef void (*pfun)();
      
      
       int main(void)
      
      
       {
      
      
        C *cp=
       new C;
      
       
       cout<<
       sizeof(*bp)<<
       endl;
      
      
        pfun fun=
       NULL;
      
       
       cout<<
       "The B1's virtual table->"<<
       endl;
      
       
       for(
       int i=
       0;i<
       5;i++)
      
      
        {
      
      
        fun=(pfun)*((
       long*)*(
       long*)cp+i);
      
      
        fun();
      
      
        }
      
       
       long* p=(
       long*)cp+
       2;
      
       
       cout<<
       "The B2's virtual table->"<<
       endl;
      
       
       for(
       int i=
       0;i<
       3;i++)
      
      
        {
      
      
        fun=(pfun)*((
       long*)*(
       long*)p+i);
      
      
        fun();
      
      
        }
      
       
       cout<<
       "The A2's virtual table->"<<
       endl;
      
      
        A *ap=
       reinterpret_cast<A*>((
       long*)cp+
       4);
      
       
       for(
       int i=
       0;i<
       2;i++)
      
      
        {
      
      
        fun=(pfun)*((
       long*)*(
       long*)ap+i);
      
      
        fun();
      
      
        }
      
       
       return 
       0;
      
      
       }
       

此時程式輸出仍然有錯,因為c重寫了A中的方法。原因不明。c物件模型為:菱形虛繼承的物件模型

如果c不重寫A的f方法,即將A的f方法改為f0,則程式輸出如下:將A的f方法改為f0

轉載:http://luodw.cc/2015/10/08/Cplus3/