当基类含虚函数时,派生类转基类指针出现意外结果的咨询
为什么注释掉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
相关产品推荐
相关产品推荐

