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

继承是C++作为OOD程序设计语言的三大特征(封装,继承,多态)之一,单一非多态继承是比较好理解的,本文主要讲解GCC环境下的多重继承和虚拟继承的对象内存布局。

一、多重继承

先看几个类的定义:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code

1  class Top
 2  {
 3 public:
 4       int a;
 5  };
 6 
 7  class Left : public Top
 8  {
 9 public:
10       int b;
11  };
12 
13  class Right : public Top
14  {
15 public:
16       int c;
17  };
18 
19  class Bottom : public Left, public Right
20  {
21 public:
22       int d;
23  };

不难想象,Left和Right类的内存布局如下图所示:

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

我们如下进行验证:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code

1 Left *left = new Left();
2 Top *top = left;
3 cout << left <<  't' << top << endl;//输出:0x902c008       0x902c008
4 Right *right = new Right();
5 top = right;
6 cout << right << 't' << top << endl;//输出:0x902c018       0x902c018

从输出结果可以看出,父类指针top指向子类对象left和right的起始地址,与上述内存布局吻合。

在非虚拟多重继承的情况下,子类的内存布局是什么样子的呢?如下所示:

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

可以看出,Bottom类由于继承了Left和Right,而Left和Right又分别继承了Top。因此,Bottom包含了Top两次!

下面进行验证:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code

1 Bottom *bottom = new Bottom();   
2 //  top = bottom;     //error: ‘Top’ is an ambiguous base of ‘Bottom’   
3 top = (Left *)bottom;
4 left = bottom;
5 cout << bottom << 't' << top << 't' << left << endl;//输出:0x9930028 0x9930028 0x9930028
6 top = (Right *)bottom;
7 right = bottom;
8 cout << bottom << 't' << top << 't' << right << endl;//输出:0x9930028 0x9930030 0x9930030

从输出结果可以看出,left指针和right指针分别指向了bottom对象中它们所处的位置:

浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局
由于bottom对象中存在两部分top对象,因此不能直接用top指针指向bottom对象,因为编译器不知道你的意图到底是指向left中的bottom部分,还是right中的bottom部分。需要进行转换才可以。如果需要通过bottom指针分别访问left和right中的top部分,可以如下: bottom->Left::a, bottom->Right::a。
好了,到这里讲完了非虚拟继承下的多重继承的内存布局情况,相信大家应该有一个比较清晰的认识了。最重要的一点是:多重继承时,父类共同继承的祖父类会在子类中有多份存在。

二、虚拟继承

平时讨论的最多的是虚函数,很少涉及到虚拟继承的情况。那么,虚拟继承到底是一个什么概念呢?

先来看一个例子:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code
对,你没有看错,类的大小输出不是4 8,而是4 12。虚拟继承时,编译器会在子类中安插上一个虚表指针。

从输出的对象成员地址来看,我们可以得到Child类的如下内存布局:

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

现在我们对多重继承的例子进行改造:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code
把Left和Right改成了虚拟继承Top。

从上面验证简单虚拟继承时,编译器安插虚表指针的例子,我们可以想象出此时Bottom类的对象内存布局如下:

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

对,你没有看错!虚拟继承时,子类只有父类共同继承的祖父类的一份存在。这其实也就是虚拟继承的最大用途。此时,Top,Left,Right和Bottom对象的大小分别为:4 ,12 ,12 ,24。

既然有虚表指针了,那么Bottom的虚表是什么样的呢?请看:

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

有了虚表,内存布局情况一目了然。下面我们进行验证:
浅析GCC下C++多重继承 & 虚拟继承的对象内存布局浅析GCC下C++多重继承 & 虚拟继承的对象内存布局View Code
根据输出结果,我们可以知道指针的指向情况:

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

由于引入了虚指针和虚表,left指针和right指针可以根据虚表提供的偏移量信息,轻松访问到Top::a。

到此为止,已经讨论清楚了多重继承和虚拟继承下的对象内存布局情况。总结下:非虚拟多重继承时,子类会有父类共同继承祖父类的多份存在;虚拟继承时,子类会被安插一个虚拟指针;多重虚拟继承时,子类只有父类共同继承祖父类的一份存在。通过父类的虚拟指针,可以正确地访问祖父类中的成员。



参考文献:

1.http://www.tbdata.org/archives/878

  1. 《深度探索C++对象模型》
    原文链接: https://www.cnblogs.com/pathenon/archive/2012/07/17/2595824.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/55588

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午6:40
下一篇 2023年2月9日 上午6:41

相关推荐