c++ - 为什么在这个例子中需要后期绑定?
问题描述
我理解为什么动态创建子类的对象时需要覆盖虚拟关键字,但是在下面的示例中,为什么需要后期绑定(虚拟关键字)来覆盖?编译器不能在编译时告诉 pa 指向派生类吗?
class A {
public: int f() { return 'A';}
};
class B : public A {
public: int f() { return 'B';}
};
int main() {
//I understand this case, since pa is pointing to
//something created at runtime, virtual keyword is needed
//to return 66
A* pa;
pa = new B;
cout << pa->f() << endl; //returns 65
//But why in this case where b2 is created during
//compile time, compiler doesn't know pa is pointing
//to a derived class?
B b2;
pa = &b2;
cout << pa->f() << endl; //returns 65
}
解决方案
这个问题实际上并不围绕编译器是否可以“知道”对象pa
所指的精确类型。它围绕 C++ 的语义展开。
当您声明一个方法f
时,您是在告诉编译器您希望如何f
处理调用。在A::f
未声明的情况下virtual
,您是说 ifpa->f()
被调用并pa
具有声明的类型A*
,您希望编译器使用A::f
. 当然,*pa
是一个类型的对象A
,即使它也可能是某个派生类型的对象A
。
另一方面,如果您声明f
为virtual
,则您告诉编译器您希望它引用当前指向的对象的最派生类型pa
,并使用f
来自该类型(或其适当的超类型)的定义。
C++ 的语义需要是确定性的。也就是说,作为程序员,您需要能够预测f
在任何给定情况下将使用哪个定义。如果您考虑一下,用一种规则编写语言真的很困难,该规则规定“B::f
如果您碰巧能够找出pa
指向类型的对象,则B
使用,但A::f
如果您不确定pa
指向的对象,请使用”。编译器应该如何努力找出pa
指向什么?如果将来编译器团队中的某个人想出如何做出更准确的判断,你的程序的语义是否应该神奇地改变?你会为此感到高兴吗?
请注意,在您呈现的两个片段中,编译器实际上很有可能找出指向的对象的类型是什么pa
。所以即使f
是virtual
,优化编译器也可以省略在 vtable 中查找正确方法的代码,而直接调用正确的方法。C++ 标准中没有任何内容禁止这种优化,我相信它很常见。因此,如果将来编译器团队中的某个人找到了一种更好的方法来确定变量的类型,它不会改变程序的语义——它仍然会调用相同的方法。但这可能会导致您的程序更快地调用您的方法。这是一个更有可能让你未来快乐的结果。
推荐阅读
- django - 当我尝试在本地服务器的 admin/message/post/add/ 中保存某些内容时,我得到“没有这样的表:main.auth_user__old”
- dfa - 两种简单语言的 DFA,然后是这两种语言的产品构造
- sql - 在我的 SQL 查询中使用 Power BI 中的时间选择器作为参数
- arrays - AWK 在一个文件中的记录中搜索另一个文件中的条目
- c# - 如何在 C# 中获取 System.Numerics.Vector 的元素?
- javascript - 使用 Javascript 更改 wordpress 简码
- security - Keycloak 是否允许通过 Web 界面获取 id 令牌
- javascript - id 值作为变量 vs document.getElementById
- c# - 具有非多态子类和抽象的多态基类
- javascript - 使用 jQuery 创建一个多层次的 div 元素结构