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

当基类含虚函数时,派生类转基类指针出现意外结果的咨询

为什么注释掉Base2的虚函数后程序输出会有差异?

这事儿核心原因在于虚函数会改变类对象的内存布局,咱们分两种场景拆解一下:

场景1:Base2包含虚函数foo()

当一个类拥有虚函数时,编译器会自动给这个类的每个对象添加一个虚函数表指针(vptr)——它是一个指向虚函数表(vtable)的指针,用来实现运行时多态。在大多数编译器(比如GCC、Clang、MSVC)的默认布局里,这个vptr会放在对象内存的最开头。

咱们来算Derived对象的内存布局(假设int占4字节,64位系统下指针占8字节):

  • 首先是Base1的成员:int b1[100],总共100*4=400字节
  • 然后是Base2的子对象:先放vptr(8字节),再放int b2(4字节)
  • 最后是Derived自己的成员:int d(4字节)

所以sizeof(D)的结果是400+8+4+4=416字节。

再看指针转换:

  • 当把Derived*转成Base2*时,指针会自动偏移到Base2子对象的起始位置——也就是跳过Base1的400字节,指向vptr的地址。所以p_B2的地址会比&D大400字节。
  • &(D.b2)的地址是&D + 400 + 8 = &D + 408,而p_B2->b2的地址是p_B2 + 8,两者完全一致。
  • sizeof(*p_B2)是Base2的大小:8(vptr)+4(b2)=12字节。

场景2:注释掉Base2的虚函数

此时Base2没有虚函数,编译器就不会生成vptr了,Base2的内存布局里只有int b2这4字节。

Derived的内存布局变成:

  • Base1的b1[100]:400字节
  • Base2的b2:4字节
  • Derived的d:4字节

所以sizeof(D)的结果是400+4+4=408字节。

指针转换的变化:

  • 把Derived*转成Base2*时,指针偏移到Base2子对象的起始位置——也就是&D + 400字节,这个位置正好就是b2的地址,所以p_B2的地址和&(D.b2)完全相同。
  • sizeof(*p_B2)是Base2的大小:4字节。
  • &(p_B2->b2)就是p_B2本身的地址,自然和&(D.b2)一致。

总结

本质区别就是虚函数是否存在决定了类对象是否包含虚函数表指针,这直接改变了基类子对象在派生类中的内存位置、类的大小,进而导致指针地址、sizeof输出的差异。

内容的提问来源于stack exchange,提问作者AdyAdy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:26:36