【发布时间】:2020-02-11 15:29:16
【问题描述】:
我正在学习C++11的继承,我发现如果派生类重新定义了一个虚函数名但原型不同,则分配了指向派生类的指针的基类指针只能访问函数的基类版本。无法访问派生版本功能。我想知道为什么会这样。
class Enemy {
public:
virtual void describe() { std::cout << "Enemy"; }
};
class Dragon : public Enemy {
public:
virtual void describe(int dummy) { std::cout << "Dragon"; }
};
在main,
Dragon foo;
Enemy* pe = &foo;
pe->describe(); // Enemy
foo.describe(1); // Dragon
pe->describe(1); // no matching function, candidate is Enemy::describe()
根据我对虚函数表的了解,pe 指向的派生对象(即foo)应该有一个指向Dragon 的vtable 的vpointer 成员。我也知道在派生类中重新定义函数名会隐藏基类中的所有同名函数。所以在 Dragon 的 vtable 中,describe 的地址应该是带有参数int dummy 的函数。
但事实证明pe 可以访问Enemy 的方法版本,应该是隐藏的。而pe 无法访问Dragon 的方法版本,该方法应该在pe 的vtable 中。它的执行就像使用了Enemy 的vtable。为什么会这样?
更新: 我想现在我或多或少地了解了它背后的机制。这是我的假设:
由于是指向Enemy的指针,所以程序会先在Enemy的作用域内查找方法名。如果未找到该名称,编译器将给出错误。如果它不是虚拟的,则调用它。如果是虚拟的,则在Enemy的vtable中记录方法的偏移量。然后程序使用这个偏移量来访问目标对象的vtable中正确的方法。
如果该方法被正确覆盖,目标对象的 vtable 中该偏移量处的函数地址将被更改。否则,它将与 Enemy 的 vtable 中的函数地址相同,如示例中所示。
由于Dragon的describe和int dummy是不同的原型,它被添加到Dragon的vtable中,在原来的describe继承自Enemy之后。但是int dummy 版本无法从Enemy* 访问,因为Enemy 的vtable 甚至没有那个偏移量。
这是正确的吗?
【问题讨论】:
-
pe正在使用EnemyAPI,并且被访问的方法不是该 API 的一部分。 -
我已经更新了我对示例中发生的情况的假设。
标签: c++ c++11 virtual-functions dynamic-binding method-hiding