向上转型非指针对象时,vtable指针会发生什么变化?
关于C++对象切片与虚表指针的问题解答
先看第一个指针转型的示例代码:
class Parent{ public: int x; Parent(int _x) : x(_x){} virtual void print() const{ cout << x << '\n'; } }; class Derived : public Parent{ public: int y; Derived(int _x, int _y) : Parent(_x), y(_y){} void print() const override{ cout << x << ' ' << y << '\n'; } }; int main() { Derived *a = new Derived(1, 2); Parent *b = a; b->print(); }
这里Parent* b = a是指针的向上转型,b只是指向了Derived对象a的基类部分,但整个对象的本质还是Derived,所以b间接指向的虚表指针依然属于Derived类,调用print()时会触发多态,输出1 2,这符合预期。
再看第二个非指针对象的示例:
class Parent{ public: int x; Parent(int _x) : x(_x){} virtual void print() const{ cout << x << '\n'; } }; class Derived : public Parent{ public: int y; Derived(int _x, int _y) : Parent(_x), y(_y){} void print() const override{ cout << x << ' ' << y << '\n'; } }; int main() { Derived a(1, 2); Parent b = a; b.print(); }
这里执行Parent b = a时,发生了对象切片(Object Slicing),具体细节如下:
- 当把Derived对象赋值给Parent对象时,编译器会调用Parent的默认拷贝构造函数,只把a对象中属于Parent类的成员(也就是
x)拷贝到b里,Derived类独有的成员y会被直接丢弃。 - b是一个独立的、纯粹的Parent类实例,它的虚表指针会被初始化为指向Parent类自己的虚表,和a的Derived虚表没有关联。
- 所以调用
b.print()时,会直接调用Parent类的print()函数,输出1。
针对你的疑问逐一明确:
- b的虚表指针为何没指向a的?因为b本身就是Parent类的实例,它的虚表指针从创建时就指向Parent的虚表,和a的Derived虚表无关。
- 虚表指针的变化:a的虚表指针始终指向Derived类的虚表;而b在初始化时,虚表指针被设置为Parent类的虚表,没有继承a的虚表指针。
- 根本原因:这是C++的对象切片机制导致的——基类对象的内存空间只能容纳自身定义的成员,无法承载派生类的额外内容,因此无法维持派生类的多态特性。
内容的提问来源于stack exchange,提问作者dohyunii
相关产品推荐
相关产品推荐

