为什么在编译时选择此虚拟方法的实现
Why is this virtual method's implementation chosen during compile time?
我尝试运行以下内容:
struct B;
struct C;
struct A{
A() { f(this);}
virtual A* f(A* a) {
cout << " A::f(A)" <<endl;
return a;
}
void h() { cout << typeid(*this).name() << endl;}
void g(B* b);
};
struct B:A{
B() { f(this); }
virtual A* f(A* a) {
cout << "B::f(A)" << endl;
return a;
}
virtual B* f(B* b) {
cout << "B::f(B)" << endl;
return b;
}
virtual C* f(C* c) {
cout << "B::f(C)" << endl;
return c;
}
};
struct C: B{};
void A::g(B* b) { cout << typeid(*this).name() << endl; f(b);};
int main(){
B* b = new B();
cout << "------" << endl;
C* c = new C();
cout << "------" << endl;
c->g(b);
return 0;
}
请注意,g() 是非虚拟的,因此它是在编译期间选择的。
运行这个时,我得到以下输出:
A::f(A)
B::f(B)
------
A::f(A)
B::f(B)
------
1C
B::f(A) <----- Notice this
请注意,最后一行似乎调用了 f(),就好像它是动态绑定的,但只调用了 A 知道的方法 f()(我认为这与 g() 是静态绑定的事实有关)。我期望发生的是得到B::f(B)。
为什么 f() 在 g() 中的调用是在编译时计算的?
A::g
不知道B
又引入了一个重载f
。事实上,它选择对f(A*)
进行虚拟呼叫,因为它是那个地方唯一已知的f
。
虚拟调度仅由(不可见的)-1st 参数 (this
完成),而不是由任何其他参数完成。因此,B::f(B*)
的功能不参与虚拟链。因此,选择了实际f(A*)
,即B::f(A*)
。
调用虚函数并不意味着在运行时选择最佳匹配签名,只有实际的类才是。签名是在编译时选择的(嗯,除了返回类型)。
重载与虚多态无关。只有A::f(A*)
是虚拟的,并且是动态调度的。函数B::f(B*)
完全无关。
您依赖于基类中的派生类。 由于A::g
是A
的成员,所以它是使用A
的vtable编译的。 它不是在编译时"计算"的。 您创建的依赖项使其查看 vtable 的错误部分。 澄清一下,A
只定义了f(A*)
。 它对f(B*)
一无所知,所以它没有办法称呼它。
如果你在实际代码中遇到这种情况,你真的需要考虑你的设计。
C++11 添加了 override
关键字来帮助您处理此处的问题。
当您认为virtual
方法覆盖基本方法virtual
方法时,请向其追加关键字override
。
在这种情况下,当您将B
更改为包含时:
virtual B* f(B* b) override {
cout << "B::f(B)" << endl;
return b;
}
编译器会抱怨并告诉您B* f(B*)
不会覆盖其父级的任何方法。 您在这里所做的是引入一个新的重载,一个完全不同的函数,恰好与方法A* f(A*)
同名。
同名方法参与重载解析,但不会virtual
相互覆盖。
因为它有相同的名称,你以为它是一个override
- 但实际上,它不是。 一旦你意识到B* f(B*)
与A* f(A*)
无关,发生的所有其他事情都是完全有意义的。
A
中的重载分辨率检查它对f(b)
的选项,只看到一个选项,确定它匹配(因为A*
参数与B*
兼容),然后调用它。 此时检查virtual
函数表,并找到正确的override
(这是B::f(A*)
,唯一的覆盖),然后调用该表。
- 在模板基类中为继承类中的可选重写生成虚拟方法
- 跨 DLL 边界访问虚拟方法是否安全/可能?
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 如何编写 operator= 用于使用虚拟方法与非平凡成员的匿名联合
- 让编译器告诉什么确切的纯虚拟方法使结构抽象?
- 使用模板而不是虚拟方法的管道模式
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 为什么调用没有正文的纯虚拟方法不会导致链接器错误?
- 出于什么目的,非虚拟方法将与C++一起使用?
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 如何重写继承的嵌套类中存在的虚拟方法
- 私有虚拟方法有什么用?
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 确保模拟的 GTest 方法覆盖虚拟方法
- CPP 继承虚拟方法解析顺序
- 我是否应该在包含虚拟方法的类上使用'memcpy'?如果没有,如何替换它?
- 用c++中的纯虚拟方法抽象模板类
- 解决虚拟方法的歧义继承的两种方法
- 没有针对完全专用模板类的外联虚拟方法定义