重载虚拟函数调用解析 请考虑以下代码: 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++通过将对象的这个指针向基类上行,而不是向上增加()> /的参数来选择在最后一行上解析函数调用。有什么方法可以让我得到我想要的行为吗?b1->f(static_cast(a2)); b1->f(static_cast<A1*>(a2));

重载虚拟函数调用解析 请考虑以下代码: 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++通过将对象的这个指针向基类上行,而不是向上增加()> /的参数来选择在最后一行上解析函数调用。有什么方法可以让我得到我想要的行为吗?b1->f(static_cast(a2)); b1->f(static_cast<A1*>(a2));,c++,overloading,virtual-functions,overload-resolution,C++,Overloading,Virtual Functions,Overload Resolution,这将迫使编译器对A1类型的参数使用重载方法。这称为名称隐藏。在一个派生类中声明的每个f都会隐藏其任何基类中的每个可能的f 使用对基类的强制转换来获得所需的行为 重写虚拟函数时,不会重写同名的重载函数。它们是不同的函数(在vtable中有不同的条目)。通过查看参数的编译时类型来选择调用哪个版本的f。此名称解析不考虑运行时类型。由于b1属于Bbase*类型,因此考虑所有Bbase的成员;采用A2*的函数是最佳匹配,因此这就是被调用的函数。“…选择通过将对象的this指针向上投射到基类来解决最后一行的

这将迫使编译器对A1类型的参数使用重载方法。

这称为名称隐藏。在一个派生类中声明的每个f都会隐藏其任何基类中的每个可能的f

使用对基类的强制转换来获得所需的行为


重写虚拟函数时,不会重写同名的重载函数。它们是不同的函数(在vtable中有不同的条目)。

通过查看参数的编译时类型来选择调用哪个版本的
f
。此名称解析不考虑运行时类型。由于
b1
属于
Bbase*
类型,因此考虑所有
Bbase
的成员;采用
A2*
的函数是最佳匹配,因此这就是被调用的函数。

“…选择通过将对象的this指针向上投射到基类来解决最后一行的函数调用…”。你在说什么?在所有调用中,对象指针类型都是
Bbase*
,调用解析为属于
Bbase
或其后代的函数。编译器从不执行任何向上转换来解析调用。事实上,前两个调用需要向下转换才能调用适当的重写器,因为重写器属于层次结构中较低的类。至于最后两个调用,它们通过
Bbase*
类型的指针被分派到
Bbase
类中。类型完全匹配,没有任何类型的铸造发生


至于过载解决方案。。。重载解析是一个编译时过程,它基于参数的静态类型和可能转换的等级。您提供了一个
A2*
类型的参数。
f(A2*)
候选者精确地匹配了你的论点。
f(A1*)
候选者需要从
A2*
A1*
的额外转换。完全匹配的候选者被认为是更好的候选者,因此它赢得了重载解决方案。简单。

您在Bbase for Abase和A2中的重载隐藏在B1中。 也许你可以这样解决这个问题:

class Bbase{  
public:
    inline void f(Abase* a) { f_(a); }
    inline void f(A1* a) { f_(a); } 
    inline void f(A2* a) { f_(a); } 
protected:
    virtual void f_(Abase* a);  
    virtual void f_(A1* a);  
    virtual void f_(A2* a);  
};

class B1:public Bbase{  
protected:
    void f_(A1* a);  
};

class B2:public Bbase{  
protected:
    void f_(A2* a);
}; 
或使用Bbase中的模板:

class Bbase{  
public:
    template<class myA>
    inline void f(myA* a) { f_(a); }
protected:
    virtual void f_(Abase* a);  
    virtual void f_(A1* a);  
    virtual void f_(A2* a);  
};
类Bbase{
公众:
模板
内联空位f(myA*a){f_u(a);}
受保护的:
虚空f_2;(Abase*a);
虚空f_(A1*a);
虚空f_2;(A2*a);
};

不,这里的问题恰恰相反。如果你通过一个指针调用一个更派生的类,它会隐藏基类中的方法,这就是名称隐藏。我认为动态转换更合适。@Jason:a
static\u cast
到基类是可以的。我认为静态转换是可以的,因为我们a)知道转换是正确的,b)它是向上转换,这是安全的。事实上,你也可以对派生类进行静态强制转换。@Jason,@inflagranti:当强制转换为向上转换时,
dynamic\u cast
不能提供任何“有意义的信息”。事实上,
dynamic\u cast
static\u cast
在向上广播方面绝对没有区别。上行不能失败。它的有效性在编译时已知。它要么无法编译,要么工作成功。使用
dynamic\u cast
进行上传绝对没有意义。此外,这会产生误导
dynamic_cast
应该只保留用于downcast。谢谢-据我所知,关键是要调用哪个虚拟f()的解析发生在编译时,基于提供给f()的参数。在最后一行,编译器已经决定调用f(A2*)。然后调用的f(A2*)的版本取决于指向的运行时类型。在这里,由于B1类没有覆盖f(A2*),所以称为基类版本。对不起,我用错了术语“向上投射”。我的意思是为什么这个对象在这里被视为一个BBase。答案(如您所说)是重载解析发生在编译时,而在编译时,对象是BBase,因此编译器选择f(A2*)。