我在大学学习c++的VTable。也许我错过了一些东西,但我看到了以下示例:
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();
}
这里我了解到b的VTable指针更改为a的VTable指针,结果打印“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();
}
为什么它打印“1”,我实际上想知道的是VTable指针会发生什么。 b的VTable指针不会变成a的VTable指针吗?为什么?
谢谢你。
我想知道为什么没人敢写答案:-)
我简单总结一下:
简短的回答是“虚函数表指针没有发生任何事情”。
每个具有虚方法的类或从具有虚方法的类派生的类都有自己的 vtable,并且该类的每个实例都有一个指向其自己类的 vtable 的指针。
实例内部的 vtable 指针永远不会改变。
关键要素是
Parent
和 Derived
的 vtable 看起来相同(它们指向 print
实现),但调用者不需要知道指针指向哪里。
在第一个示例中,您只有一个实例,但有两个指向同一实例的类型化指针。
类型化指针不知道目标实例的确切类型,但它们知道在实例 vtable 内部哪里可以找到指向正确
print
方法的指针。
在第二个示例中,您创建了两个实例,它从 b
的内容复制
构造
a
(仅 Parent
字段)。b
是 Parent
,因此该实例也会获得自己的 vtable,该 vtable 与 Parent
相匹配。
如果您从两个变量中
print
并让print
方法输出this
指针的地址,可能会更清楚
#include <iostream>
using namespace std;
class Parent{
public:
int x;
Parent(int _x) : x(_x){}
virtual void print() const{
cout << x << " at " << this << '\n';
}
};
class Derived : public Parent{
public:
int y;
Derived(int _x, int _y) : Parent(_x), y(_y){}
void print() const override{
cout << x << ' ' << y << " at " << this << '\n';
}
};
int main()
{
Derived *pa = new Derived(1, 2);
Parent *pb = pa;
Derived a(3, 4);
Parent b = a;
pa->print();
pb->print();
a.print();
b.print();
}
输出示例:
1 2 位于 0xd062b0
1 2 位于 0xd062b0
3 4 位于 0x7ffd557ea6a0
3 在 0x7ffd557ea690