环境是vs 2008
#include <iostream>
using namespace std;
class A {
public:
virtual ~A();
virtual void foo();
};
class B : public virtual A {
public:
virtual ~B();
virtual void foo();
};
class C : public virtual A {
public:
virtual ~C();
virtual void foo();
virtual void foobar();
};
class D : public B, public C {
public:
virtual ~D();
virtual void foo();
virtual void foobar();
};
int main()
{
cout<<"size of A "<<sizeof(A)<<endl;
cout<<"size of B "<<sizeof(B)<<endl;
cout<<"size of D "<<sizeof(D)<<endl;
cout<<"size of C "<<sizeof(C)<<endl;
}
结果是:
size of A 4
size of B 12
size of D 20
size of C 16
答案 0 :(得分:0)
标准中只定义了很少的关于对象内存布局的内容,如this SO question中所示。大多数布局都是实现定义的。在您参考MSVC实现时,为了好奇,让我们看一下这些(非便携式)实现细节。
简单继承:
我建议你从没有虚函数的简单非虚拟继承开始:
class X { }; // size of X 1
class Y : public X { }; // size of Y 1
所有对象必须具有非空大小(仍然是标准的)。这就是为什么我们看到1尽管X完全是空的。注意Y与X具有相同的大小,因为Y中没有添加任何内容(因此Y已经具有非空大小)。
简单继承,虚函数:
当您开始使用虚拟功能时,MSVC会在对象的开头添加指向vtable的指针。 This SO question在简单继承的情况下显示vtable的原理。
class XF // size of XF 4
{ public: virtual void foo(){} };
class YF : public XF // size of YF 4
{ public: virtual void bar(){} };
class YX : public X // size of YX 4
{ public: virtual void bar(){} };
你在这里看到vtable指针大小为4.你可以立即扣除我在32位模式下编译,因为在x64模式下你有8个。
顺便说一句,出于多态的目的,合并了单个继承类的vtable(在派生对象的开头只有一个vtable指针)。有关vtable的更多信息,请在 this very interesting DDJ article 中注明多重继承。
没有虚拟继承的多重继承:
多重继承组合了所有继承的类,因此它的大小至少与添加继承类的大小一样大。
但是由于对象要求它可能更大,并且在虚函数的情况下,还有一个额外的vtable指针(在单继承中不再可能合并)。这也在DDJ文章中有所解释。
虚拟继承:
让我们看看虚拟继承的效果(与虚函数无关):
class ZX : public virtual X { }; // size of ZX 4
class ZF : public virtual XF { }; // size of ZF 8
class ZZF: public virtual ZF { }; // size of ZZF 12
因此,我们看到无论何时进行虚拟继承,都会添加一个指针。这是什么目的?
没有虚拟的不间断,从派生对象向其基础的向上转换是直截了当的。
但是对于虚拟继承,这种向上转换是一场噩梦,因为几个类共享相同的基类。因此基类可能不再与派生类相邻。这就是虚拟继承需要指向虚拟基表的原因的原因。对于每个虚拟继承。