重载虚函数调用解析

发布于 2024-09-06 17:47:38 字数 777 浏览 7 评论 0原文

请考虑以下代码:

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

合久必婚 2024-09-13 17:47:38

通过查看参数的编译时类型来选择要调用的 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. Since b1 is of type Bbase*, all of Bbase's members are considered; the one that takes an A2* is the best match, so that's the one that gets called.

盛夏已如深秋| 2024-09-13 17:47:38

“...选择通过将对象的 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 either Bbase 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 the Bbase class through a pointer of Bbase * 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. The f(A2 *) candidate matched your argument precisely. The f(A1 *) candidate requires a extra conversion from A2 * to A1 *. The candidate that matches exactly is considered a better one, so it wins the overload resolution. Simple.

把回忆走一遍 2024-09-13 17:47:38
b1->f(static_cast<A1*>(a2));

这应该强制编译器使用参数类型为 A1 的重载方法。

b1->f(static_cast<A1*>(a2));

This should force the compiler to use the overload method with parameter of type A1.

柠栀 2024-09-13 17:47:38

这就是所谓的名字隐藏。您在派生类中声明的每个 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).

白衬杉格子梦 2024-09-13 17:47:38

Bbase 中 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);  
};

Your overloads in Bbase for Abase and A2 are hidden in B1.
Maybe you can work around that problem like this:

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);
}; 

or with a template in 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);  
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文