You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多继承下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:56:52