C++虚函数_虚继承
¶1 说明
通过一个菱形继承的例子,说明大部分的情况。
代码如下:
1 | class A { |
调试环境 VS 2019 ,使用VS提供的 cl
工具查看类内存结构;命令如下:
1 | cl filename.cpp /d1reportSingleClassLayoutXXX # XXX 为类名 |
¶2 A 类情况
1 | class A { |
内存布局如下:
数据成员 a 四个字节;
虚函数指针 vfptr 四个字节,vfptr 指向 vftable,虚函数表中依次存 A::print1, A::print2, A::print3
¶3 B1 类情况
1 | class B1 : virtual public A { |
内存布局如下:
自身数据成员 X 四个字节,继承自 A 数据成员 a 四个字节;(八个字节)
虚基指针 vbptr 四个字节,指向 vbtable,虚基类表内存距离 A 数据的偏移,与A相差 8 个字节;
虚函数指针 vfptr 四个字节, 指向 vftable,虚函数表中依次存 B1::print1, A::print2, A::print3
;注意覆写了 B1::print1
¶4 B2 类情况
1 | class B2 : virtual public A { |
内存布局如下:
自身数据成员 y 四个字节,继承自 A 数据成员 a 四个字节;(八个字节)
虚基指针 vbptr 四个字节,指向 vbtable,虚基类表内存距离 A 数据的偏移,与 A 相差 8 个字节,第一个字段 -4 说明该 vbptr 到 B2 对象首地址的偏移(因为有虚函数指针的存在);
两个虚函数指针:八个字节
- B2 中 vfptr,原因在于 B2 自己定义了一个新的虚函数
B2::print4
- A 中 vfptr 指向 vftable,虚函数表中依次存
A::print1, B2::print2, A::print3
;注意覆写了B2::print2
¶5 C 类情况
1 | class C : public B1, public B2 { |
内存布局:
自身数据成员 z 四个字节,继承自 A 数据成员 a 四个字节,继承自 B1 数据成员 x 四个字节,继承自 B2 数据成员 y 四个字节;(十六个字节)
两个虚基指针:八个字节,分别位于 B1,与 B2 部分,分析同上;
两个虚函数指针:八个字节
- B2 中 vfptr,原因在于 B2 自己定义了一个新的虚函数
B2::print4
- A 中 vfptr 指向 vftable,虚函数表中依次存
C::print1, B2::print2, A::print3
;注意覆写了C::print1, B2::print2
¶6 补充
一模一样的例子,但是不用虚继承!!!
内存分布如下:(上图为无虚继承,下图为有虚继承)
A 无任何区别
B1 少了一个虚基指针,大小少了 4 字节;
- B2 少了一个虚基指针,一个虚函数指针,B2 自己定义的虚函数,直接加载虚函数表第四项,而不是像虚继承那样重新生成一个虚函数指针(主要是B2中的print4不是共用的)。故大小少了 8 字节。
C 中表象如下:少了两个虚基指针,多了一个重复的 a 数据成员,所以大小少了 4 字节;
C 对象内部的 B1 与 B2 部分来自于继承关系,并且对于 B1 与 B2 部分中的虚函数 print1 进行了覆写。