多继承下Vtable与Vptr内存疑问:为何D类大小为8而非4?
嘿,这个问题其实涉及到C++多继承中的对象内存布局、虚函数表(vtable)处理,以及内存对齐规则,我来给你拆解清楚:
先明确空类的大小规则
C++标准要求:空类(没有非静态成员变量、没有虚函数)的实例大小不能为0,编译器会给它分配1字节的占位符,用来区分不同的对象实例。所以你的两个例子里,B和C的sizeof都是1,这部分是符合标准的。
第一个例子:为什么D的大小是8?
看这段代码:
class B { public: void f1() {} }; class C { public: void f2() {} }; class D : public B, public C { public: virtual void f3() {} };
这里D是多继承自两个无虚函数的基类,同时自己拥有虚函数f3()。因为B和C都没有虚表指针(vptr),编译器必须给D单独分配一个vptr(你的系统是4字节)。
现在D的内存组成是:
B的1字节占位C的1字节占位D的vptr(4字节)
但这里还有个关键规则:内存对齐。你的系统是4字节对齐(指针大小为4),对象总大小必须是4的倍数。1+1+4=6,向上对齐到最近的4的倍数就是8,所以sizeof(D)=8。(不同编译器可能调整vptr的位置,比如放到对象开头,但最终对齐后的总大小都是8)
第二个例子:为什么D的大小是4?
再看这段代码:
class B { public: virtual void f1() {} }; class C { public: void f2() {} }; class D : public B, public C { public: virtual void f3() {} };
这里B有了虚函数,所以sizeof(B)=4(就是vptr的大小)。当D继承B时,编译器会复用B的vptr:D的虚函数f3()会被直接添加到B的虚表里,不需要给D再新增一个vptr。
那D的内存布局是B的子对象(4字节,包含共享的vptr)加上C的1字节占位。为什么总大小是4?这是编译器的优化:C的1字节占位可以被放在B的vptr所在4字节块的填充空间里——毕竟4字节的vptr只需要占用实际的指针空间,剩余的位置可以用来存放C的占位字节,这样整个对象大小刚好是4字节,满足对齐要求,不需要额外填充。
补充说明
不同编译器的内存布局细节可能略有差异,但核心逻辑是一致的:
- 当派生类有虚函数但所有基类都无虚函数时,必须新增vptr,加上基类占位字节后对齐到指针大小倍数,导致总Size变大。
- 当派生类继承自已有vptr的基类时,会复用该vptr扩展虚表,空基类的占位字节可被优化到已有空间,从而减少总Size。
内容的提问来源于stack exchange,提问作者Shawn

