详谈C++中虚基类在派生类中的内存布局

2020-01-06 16:07:23刘景俊

那就是指针vb_ptr的妙用了。原理如下:(其实g++3.4.3的实现更加复杂,我不知道是出于什么考虑,而我这里只说原理,所以把过程和内容简单化了)

首先,vb_ptr指向一个整数的地址,里面放的整数是那个int a的距离dD开始处的位移(在这里vb_ptr指向的地址里面放的是20,以字节为单位)。编译器是这样做的:

首先,找到vb_ptr(这个不用找,因为在g++中,vb_ptr就是B*中的第一项,呵呵),然后取得vb_ptr指向的地址的内容(这个例子是20),最后把这个内容与指针pb相加,就得到pb->a的地址了。

所以说这种时候,用指针转换多了两个中间层才能找到基类的成员,而且是运行期间。

由此也可以推知dD中的vb_ptr和vc_ptr的内容都是一样的,都是指向同一个地址,该地址就放20(在本例中)

如下的语句呢:


A *pa = &dD;
pa->a = 4;

这个语句不用转换了,因为编译器在编译期间就知道他把A中的成员插在dD中的那个地方了(在本例中是末尾),所以这个语句中的运行效率和dD.a是一样的(至少也是差不多的)

这就是虚基类实现的基本原理。

注意的是:那些指针的位置和基类成员在派生类成员中的内存布局是不确定的,也就是说标准里面没有规定inta必须要放在最后,只不过g++编译器的实现而已。c++标准大概只规定了这套机制的原理,至于具体的实现,比如各成员的排放顺序和优化,由各个编译器厂商自己定~

非虚拟继承:

在派生类对象里,按照继承声明顺序依次分布基类对象,最后是派生类数据成员。

若基类声明了虚函数,则基类对象头部有一个虚函数表指针,然后是基类数据成员。

在基类虚函数表中,依次是基类的虚函数,若某个函数被派生类override,则替换为派生类的函数。

派生类独有的虚函数被加在第一个基类的虚函数表后面。

虚拟继承:

在派生类对象里,按照继承声明顺序依次分布非虚基类对象,然后是派生类数据成员,最后是虚基类对象。

若基类声明了虚函数,则基类对象头部有一个虚函数表指针,然后是基类数据成员。

在基类虚函数表中,依次是基类的虚函数,若某个函数被派生类override,则替换为派生类的函数。

若直接从虚基类派生的类没有非虚父类,且声明了新的虚函数,则该派生类有自己的虚函数表,在该派生类头部;否则派生类独有的虚函数被加在第一个非虚基类的虚函数表后面。

直接从虚基类派生的类内部还有一个虚基类表指针(一个隐藏的“虚基类表指针”成员,指向一个虚基类表),在数据成员之前,非虚基类对象之后(若有的话)。