重载虚函数调用解析
请考虑以下代码:
class Abase{};
class A1:public Abase{};
class A2:public A1{};
//etc
class Bbase{
public:
virtual void f(Abase* a);
virtual void f(A1* a);
virtual void f(A2* a);
};
class B1:public Bbase{
public:
void f(A1* a);
};
class B2:public Bbase{
public:
void f(A2* a);
};
int main(){
A1* a1=new A1();
A2* a2=new A2();
Bbase* b1=new B1();
Bbase* b2=new B2();
b1->f(a1); // calls B1::f(A1*), ok
b2->f(a2); // calls B2::f(A2*), ok
b2->f(a1); // calls Bbase::f(A1*), ok
b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)!
}
我很想知道为什么 C++ 选择通过将对象的 this
指针向上转换为基类来解析最后一行的函数调用,而不是将f()
?有什么办法可以得到我想要的行为吗?
Please consider the following code:
class Abase{};
class A1:public Abase{};
class A2:public A1{};
//etc
class Bbase{
public:
virtual void f(Abase* a);
virtual void f(A1* a);
virtual void f(A2* a);
};
class B1:public Bbase{
public:
void f(A1* a);
};
class B2:public Bbase{
public:
void f(A2* a);
};
int main(){
A1* a1=new A1();
A2* a2=new A2();
Bbase* b1=new B1();
Bbase* b2=new B2();
b1->f(a1); // calls B1::f(A1*), ok
b2->f(a2); // calls B2::f(A2*), ok
b2->f(a1); // calls Bbase::f(A1*), ok
b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)!
}
I'm interested to know why C++ chooses to resolve the function call on the last line by upcasting the this
pointer of the object to the base class, rather than upcasting the argument of f()
? Is there any way that I can get the behaviour I want?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
通过查看参数的编译时类型来选择要调用的
f
版本。此名称解析不考虑运行时类型。由于b1
的类型为Bbase*
,因此考虑Bbase
的所有成员;接受A2*
的那个是最佳匹配,所以这就是被调用的那个。The choice of which version of
f
to call is made by looking at the compile-time type of the parameter. The run-time type isn't considered for this name resolution. Sinceb1
is of typeBbase*
, all ofBbase
's members are considered; the one that takes anA2*
is the best match, so that's the one that gets called.“...选择通过将对象的 this 指针向上转换为基类来解析最后一行的函数调用...”。你在说什么?在所有调用中,对象指针类型为
Bbase *
,并且调用解析的函数属于Bbase
或其后代。编译器从不执行任何向上转换来解析您的调用。事实上,前两个调用需要向下转型才能调用正确的重写器,因为重写器属于层次结构中更靠下的类。至于最后两个调用 - 它们通过Bbase *
类型的指针分派到Bbase
类中。类型完全匹配,不会发生任何类型的转换。至于重载决议...重载决议是一个编译时过程,它基于参数的静态类型和可能的转换的等级。您提供了
A2 *
类型的参数。f(A2 *)
候选者与您的论点完全匹配。f(A1 *)
候选者需要从A2 *
到A1 *
进行额外转换。完全匹配的候选者被认为是更好的候选者,因此它赢得了重载决策。简单的。"...chooses to resolve the function call on the last line by upcasting the this pointer of the object to the base class...". What are you talking about? In all of your calls, the object pointer type is
Bbase *
and the functions the calls resolve to belong to eitherBbase
or its descendants. The compiler never does any upcasting in order to resolve your calls. In fact, the first two calls require downcasting in order to call the proper overrider, since the overrider belongs to the class located further down in the hierarchy. As for the last two calls - they are dispatched into theBbase
class through a pointer ofBbase *
type. The types match exactly, no casting of any kind takes place.As for the overload resolution... Overload resolution is a compile time process, which is based on the static types of the arguments and the ranks of possible conversions. You supplied an argument of
A2 *
type. Thef(A2 *)
candidate matched your argument precisely. Thef(A1 *)
candidate requires a extra conversion fromA2 *
toA1 *
. The candidate that matches exactly is considered a better one, so it wins the overload resolution. Simple.这应该强制编译器使用参数类型为 A1 的重载方法。
This should force the compiler to use the overload method with parameter of type A1.
这就是所谓的名字隐藏。您在派生类中声明的每个 f 都会隐藏其任何基类中的每个可能的 f。使用对基类的强制转换来获得所需的行为。当您重写虚函数时,您不会覆盖具有相同名称的重载函数。它们是不同的函数(并且在 vtable 中具有不同的条目)。
It's called name hiding. Every f you declare in one derived class shadows every possible f in any of its base classes.Use a cast to the base class to get the desired behaviour.When you override a virtual function, you don't override overloaded functions with the same name. They are different functions (and have different entries in the vtable).
Bbase 中 Abase 和 A2 的重载隐藏在 B1 中。
也许你可以像这样解决这个问题:
或者使用 Bbase 中的模板:
Your overloads in Bbase for Abase and A2 are hidden in B1.
Maybe you can work around that problem like this:
or with a template in Bbase: