object size with virtual inheritance
拥有一个虚函数的类对象
class Base1
{
public:
int base1_1;
int base1_2;
virtual void foo(){}
};
int main()
{
Base1 obj;
cout << sizeof(Base1) << endl;
cout << offsetof(Base1, base1_1) << endl;
cout << offsetof(Base1, base1_2) << endl;
}
输出:
12
4
8
通过在
VS项目属性命令行添加:/d1reportSingleClassLayoutBase1
可以看到
1> class Base1 size(12):
1> +---
1> 0 | {vfptr}
1> 4 | base1_1
1> 8 | base1_2
1> +---
1>
1> Base1::$vftable@:
1> | &Base1_meta
1> | 0
1> 0 | &Base1::foo
1>
1> Base1::foo this adjustor: 0
base1_1前面多了一个变量 __vfptr , 其类型为void** ,指向相关的virtual table(图中为vftable)(常说的虚函数表vtable指针), 这说明它是一个void* 指针(注意:不是数组).
其对应的元素[0],类型为void*,值为Base1::foo()这个函数的地址.
则__vfptr的定义伪代码大概如下
void* __fun[1]={&Base1::base1_foo};
const void** __vfptr = &__fun[0]
### 拥有多个虚函数的类对象
“`
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
“`
1> class Base1 size(12):
1> +---
1> 0 | {vfptr}
1> 4 | base1_1
1> 8 | base1_2
1> +---
1>
1> Base1::$vftable@:
1> | &Base1_meta
1> | 0
1> 0 | &Base1::foo
1> 1 | &Base1::foo2
1>
1> Base1::foo this adjustor: 0
1> Base1::foo2 this adjustor: 0
1>
Base1多了一个虚函数, 类对象大小却依然是12个字节!
同样,__vptr指向的函数指针数组中出现了第二个元素,其值为第二个虚函数foo2()的函数地址。
于是现在虚函数指针和虚函数表的伪代码如下:
void* __fun[]={&Base1::foo,&Base1::foo2};
const void** __vfptr = &__fun[0];
通过以上的图表分析:
更加肯定前面我们所描述的:
1. __vfptr只是一个指针, 她指向一个函数指针数组(即: 虚函数表)
2. 增加一个虚函数, 只是简单地向该类对应的虚函数表中增加一项而已, 并不会影响到类对象的大小以及布局情况
此时,如果新增一个类的变量,定义2个类变量,
- obj1和obj2是类的两个变量, 理所当然, 它们的地址是不同的(见 &obj1 和 &obj2).
- 虽然obj1和obj2是类的两个变量, 但是:它们的__vfptr的指向却是同一个虚函数表.
于是可以得出结论:
同一个类的不同实例共用同一份虚函数表, 它们都通过一个所谓的虚函数表指针__vfptr(定义为void**类型)指向该虚函数表.
根据以上表示的类对象的内存布局情况:
这个清晰的表示出来了虚函数表的关系,
1. 虚函数表是在编译时期为我们创建好的,只存在一份;
2. 定义类对象时,编译器会自动将类对象的__vptr指向这个虚函数表。
单继承且本身不存在虚函数的继承类的内存布局
//定义一个简单继承类
class Base1
{
public:
int base1_1;
int base1_2;
virtual void base1_fun1() {}
virtual void base1_fun2() {}
};
class Derive1 : public Base1
{
public:
int derive1_1;
int derive1_2;
};
1> class Base1 size(12):
1> +---
1> 0 | {vfptr}
1> 4 | base1_1
1> 8 | base1_2
1> +---
1>
1> Base1::$vftable@:
1> | &Base1_meta
1> | 0
1> 0 | &Base1::base1_fun1
1> 1 | &Base1::base1_fun2
1>
1> Base1::base1_fun1 this adjustor: 0
1> Base1::base1_fun2 this adjustor: 0
1>
1> class Derive1 size(20):
1> +---
1> | +--- (base class Base1)
1> 0 | | {vfptr}
1> 4 | | base1_1
1> 8 | | base1_2
1> | +---
1> 12 | derive1_1
1> 16 | derive1_2
1> +---
1>
1> Derive1::$vftable@:
1> | &Derive1_meta
1> | 0
1> 0 | &Base1::base1_fun1
1> 1 | &Base1::base1_fun2
如图,基类位于上面,继承类的成员依次在下面定义。
__vfptr展开后,完全是Base1基类的内容: 虚函数表指针+成员变量定义.
并且根据下图可知d1对象内Base1的虚函数表[0][1]的值就是本身base1_fun1和base1_fun2的地址。
对看完inside C++ Object model第四章对于你理解虚函数虚继承有很大帮助