根据我的理解,编译器验证指针赋值A *p_a = &b;
,知道它指向派生类对象。但为什么需要虚拟表来解析函数调用?
class A
{
public:
A():x(1){}
virtual void getX() {cout << x << endl;}
private:
int x;
};
class B : public A
{
public:
B() : A(),x(2){}
void getX() {cout << x << endl;}
private:
int x;
};
int main()
{
B b;
A *p_a = &b;
p_a->getX();
}
答案 0 :(得分:4)
在这种情况下,编译器理论上可以在编译时解析该调用。但是呢?
A* o;
if (userClicksWidgetA())
o = new A();
else
o = new B();
o->method();
在编译时无法解析该调用。或者单独编译的模块有一个方法采用A*
,但它对B
一无所知;或者包含你一无所知的子类C
,它会传回给你A*
?最终,vtable是对象类的唯一运行时表示。
答案 1 :(得分:2)
如果编译器足够智能,它可能能够直接调度调用,而无需通过虚拟表,因为它有足够的信息通过查看分配中的分配来了解final overrider
的确切类型。之前的那条线。
通常编译器不会有那么多信息,它只会有一个指向A
对象的指针,但不知道指向对象的派生类型是什么。那是 使用虚拟表的时候。基本上,存储在每个多态对象中的虚拟表指针包含最终对象的类型信息,特别是每个方法的final overrider
。
答案 2 :(得分:1)
每个对象都有一个指向虚拟表函数指针的指针
通过重定向虚拟表在对象上调用函数时,将调用相应的函数
即当你在代码中声明一个类型为A的对象但它确实指向一个B类型的对象时,应该在运行时调用类型B的函数实现。
但是应该调用哪个函数由vtable决定。如果没有vtable,就不可能确定是否应该调用A类的函数f()或者调用B()的f()来覆盖基类的相应函数。
这是关于多态性实现的C ++设计决策,没有其他运行时类型信息与对象关联(与java不同)
答案 3 :(得分:0)
因为这就是C ++实现多态的方式。
答案 4 :(得分:0)
因为确定在编译时调用的实际函数会使实现C ++编译器变得非常困难。