跟我一起学习C++虚函数--第四篇

 在前一篇,我们讨论了在多继承情况下,具有虚函数的类的内存布局情况。本篇将进一步探索在多重继承,即具有重复继承的情况下的内存布局情况。在阅读本篇和下一篇之前,建议先阅读本博客另一篇博文《浅析GCC下C++多重继承 & 虚拟继承的对象内存布局》。

       先说一点题外话,细心的读者可能会发现,我们在探索不同情况下类的内存布局时,总是先通过查看类的大小以及其中各个成员变量的地址来进行分析,然后再具体定位某一位置的值。从最原始的内存中的对象分布,我们可以获得最深入最有效的理解。

      OK,请看例子:

01 #include <iostream>
02 using namespace std;
03  
04 class Top
05 {
06 public:
07     virtual void x(){cout << "top x" << endl;}
08     virtual void print0(){cout << "top print" << endl;}
09 public:
10     int a;
11 };
12  
13 class Left:public Top
14 {
15 public:
16     virtual void y(){cout << "left y" << endl;}
17     virtual void print1(){cout << "left print" << endl;}
18 public:
19     int b;
20 };
21  
22 class Right:public Top
23 {
24 public:
25     virtual void z(){cout << "right z" << endl;}
26     virtual void print2(){cout << "right print" << endl;}
27 public:
28     int c;
29 };
30  
31 class Bottom : public Left, public Right
32 {
33 public:
34     virtual void y(){cout << "bottom y" << endl;}
35     virtual void z(){cout << "bottom z" << endl;}
36     virtual void print3(){cout << "bottom print" << endl;}
37 public:
38     int d;
39 };
40  
41  int main()
42  {
43     /*first part*/
44     cout << sizeof(Top) << "\t" << sizeof(Left) << "\t" << sizeof(Right) << "\t" <<sizeof(Bottom) << endl;
45     //输出:8       12      12      28
46     Bottom *b = new Bottom();
47     cout << b << " " << &b->Left::a << " " << &b->b << " " << &b->Right::a << " " << &b->c << " " << &b->d << endl;
48   //输出:0x8c0f008 0x8c0f00c 0x8c0f010 0x8c0f018 0x8c0f01c 0x8c0f020
49      
50     /*second part*/
51     typedef void (*Func)(void);
52     Func pFunc;
53     pFunc = (Func)*((int *)*(int *)(b));
54     pFunc();//输出:top x
55     pFunc = (Func)*((int *)*(int *)(b)+1);
56     pFunc();//输出:top print
57     pFunc = (Func)*((int *)*(int *)(b)+2);
58     pFunc();//输出:bottom y
59     pFunc = (Func)*((int *)*(int *)(b)+3);
60     pFunc();//输出:left print
61     pFunc = (Func)*((int *)*(int *)(b)+4);
62     pFunc();//输出:bottom z
63     pFunc = (Func)*((int *)*(int *)(b)+5);
64     pFunc();//输出:bottom print
65     //pFunc = (Func)*((int *)*(int *)(b)+6);
66     //pFunc();//段错误
67  
68     /*third part*/
69     pFunc = (Func)*((int *)*((int *)(b)+3));
70     pFunc();//输出:top x
71     pFunc = (Func)*((int *)*((int *)(b)+3)+1);
72     pFunc();//输出:top print
73     pFunc = (Func)*((int *)*((int *)(b)+3)+2);
74     pFunc();//输出:bottom z
75     pFunc = (Func)*((int *)*((int *)(b)+3)+3);
76     pFunc();//输出:right print
77     //pFunc = (Func)*((int *)*((int *)(b)+3)+4);
78     //pFunc();//段错误
79  
80     delete b;
81     return 0;
82  }

 对于上面的例子,我们分为三部分进行讲解。

第一部分:多重继承情况下,对象本身(除虚函数表外)的内存布局。

        从代码中first part的输出情况来看,Top、Left和Right的大小很容易理解,至于Bottom类的大小,如果你看过参考文献一,那么也很容易理解。Bottom类包含了两次Top类中的a成员变量,因此总共有5个int成员变量,为20字节。再加上两个虚指针,即为28字节。

       从输出的Bottom对象中成员变量地址情况,我们可以用如下图来表示内存布局:

        

        简单地说,多重继承时,子类会有多个祖父类的存在。

第二部分:多重继承情况下,主要虚函数表的内存布局。

        从代码的second part的输出情况来看,我们可以用下图来表示主要虚函数表的内存布局:

       

第三部分:多重继承情况下,次要虚函数表的内存布局。

        从代码的third part的输出情况来看,我们可以用下图来表示次要虚函数表的内存布局:

       

        

         对比前一篇多继承情况下类的内存布局,我们可以发现:在多重继承情况下,不仅祖父类的成员变量在子类中会有多份存在,祖父类的虚函数同样会在子类的虚函数表中有多份存在,分别位于主要虚函数表和次要虚函数表中。

 

参考文献:

          1.《浅析GCC下C++多重继承 & 虚拟继承的对象内存布局

          2. 《深度探索C++对象模型》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值