我尝试过运行以下内容:
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()?
答案 0 :(得分:2)
A::g
不知道B
又引入了f
的重载。事实上,它选择对f(A*)
进行虚拟通话,因为它是该地区唯一已知的f
。
虚拟调度仅由(不可见)-1st参数(this
)完成,而不是由任何其他参数完成。因此函数B::f(B*)
不参与虚拟链。因此,选择实际的f(A*)
- 即B::f(A*)
。
调用虚函数并不意味着最佳匹配签名是在运行时选择的,而只是实际的类。签名是在编译时选择的(除了返回类型)。
答案 1 :(得分:1)
重载与虚拟多态无关。只有A::f(A*)
是虚拟的并且是动态分派的。函数B::f(B*)
完全不相关。
答案 2 :(得分:0)
您对基类中的派生类有依赖关系。由于A::g
是A
的成员,因此使用A
的vtable进行编译。它不是在编译时“计算”的。您创建的依赖项使其查看vtable的错误部分。为了澄清,A
只定义了f(A*)
。它对f(B*)
一无所知,因此无法对其进行调用。
如果你在实际代码中遇到这种情况,你真的需要考虑你的设计。
答案 3 :(得分:0)
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*)
,唯一的覆盖),然后调用它。