关于虚函数表,可见博客 传送 ,但是要注意,一般被继承的第一个基类的虚函数表合并至子类的虚函数表,(基类的虚函数在前,除非有虚函数覆盖,子类的虚函数在后)
主要讲了在无虚继承情况下各种情况的虚函数表的情况,
因此,就牵扯到了类的大小的问题:
1)对于一个空类,sizeof=1;
2)静态成员不属于类大小中,且成员函数也不属于类大小中
3)类与继承情况下类的大小:
情况一: 无虚函数继承,但有虚函数的情况
class A
{
public:
char a[3];
virtual void fa() {};
};
class B : public A
{
public:
char b[3];
virtual void fa() {};
};
class C : public A
{
public:
char c[3];
virtual void bb() {};
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
}
结果是:VS2015 debug x86
sizeof(A) = 8
= A的虚函数表指针的4字节+char对齐的4字节
sizeof(B) = 12
= B的虚函数表指针的4字节 + 类B中char对齐的4字节+类A中char对齐的4字节
sizeof(C) = 12
= C的虚函数表指针的4字节 + 类C中char对齐的4字节+类A中char对齐的4字节
情况二: 虚函数继承,有虚函数的情况
class A
{
public:
char a[3];
virtual void fa() {};
};
class B : virtual public A
{
public:
char b[3];
virtual void fa() {}; //虚函数覆盖
};
class C : virtual public A
{
public:
char c[3];
virtual void bb() {}; //无虚函数覆盖
};
class D :public B, public C
{
public:
char d[3];
};
int main() {
cout << sizeof(A) << endl;
cout << sizeof(B) << endl;
cout << sizeof(C) << endl;
cout << sizeof(D) << endl;
}
虚继承:虚继承的提出是为了解决多重继承时,可能会保存两份副本的问题,也就是说用了虚继承就只保留了一份副本,但是这个副本是被多重继承的基类所共享的。
虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。
因此 :要考虑虚函数表指针和虚基类指针
结果是:VS2015 debug x86
sizeof(A) = 8
= A的虚函数表指针的4字节+char对齐的4字节
sizeof(B) = 16
= 指向A的虚基类指针4字节+类B中char对齐的4字节 + 类A的虚函数表指针4字节(覆盖的虚函数)+ 类A中char对齐的4字节
sizeof(C) = 20
= C的虚函数表指针的4字节(没有覆盖的虚函数) +指向A的虚基类指针4字节+类C中char对齐的4字节 + 类A的虚函数表指针4字节 + 类A中char对齐的4字节
sizeof(D) = 32
= 类C的虚函数表指针的4字节(有虚函数表指针,可以合并类D的虚函数)+类C中的指向A的虚基类指针4字节 +类C中char对齐的4字节+类B中的指向A的虚基类指针4字节 +类B中char对齐的4字节+类D中char对齐的4字节 + 共享类A中虚函数表指针4字节+共享类A中char对齐的4字节
注意:若类B中有虚函数表指针,则类D中类B应该在类C的前边,并且类B的虚函数表应该合并类D的虚函数表
由上可见,虚基类的内存都在最后。。