7

参考另一个so问题

考虑代码:

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){

    // 1)       
    Derived * obj = new Derived ; 
    obj->gogo(7);  // this is illegal because of name hiding


    // 2)      
    Base* obj = new Derived ;
    obj->gogo(7); // this is legal
}

对于案例 2)

调用obj->gogo(7)在运行时解决。

既然obj->gogo(7)是合法的。这似乎暗示 vtable of包含应该隐藏的Derivedptr 。virtual void gogo(int a)

我的困惑是,由于名称隐藏导致案例 1) 是非法的,那么如何在运行时解决 2) 中的调用

a) Derived 的 vtable 是否包含指向 gogo(int) 的指针。

b) 如果 a) 不为真,对虚函数的调用解析是否继续到基类的 vtable。

4

4 回答 4

5

您正在混淆虚拟函数调用和重载解析。

所有派生类都有包含所有虚函数的 vtable,来自基类和任何其他自己的虚函数。这用于在运行时解析调用,例如您的案例 2)。

在情况 1) 中,您在编译时从重载解析中得到一个错误。由于名称隐藏,类Derived只有一个可调用函数。您唯一的选择是使用int*.

于 2012-04-16T11:48:25.727 回答
2

您想覆盖重载函数,但隐藏规则不能这样工作。

“隐藏规则说,内部范围内的实体将具有相同名称的事物隐藏在外部范围内。”

请注意,它具有不同的签名是无关紧要的,gogo(int* a)即将从基础隐藏所有gogo(whatever)功能。仅当您的函数具有相同的名称、相同的签名和虚拟时才会发生覆盖(因此,只会gogo(int* a)被覆盖)。

C++FAQ 书建议使用“调用非重载虚拟的非虚拟重载”。(第 29.05 章)。基本上,您在基类中创建非虚拟重载函数:

gogo(int a) and gogo(int* a)

它将分别调用虚函数:

虚拟 gogo_i(int a) 和虚拟 gogo_pi(int* a)

并在派生类中覆盖这个虚拟。

于 2012-04-16T13:03:54.823 回答
1

基本上,函数重载只有在同名函数定义在同一个范围内时才会发生。现在,基类有自己的范围,派生类有自己的范围。

因此,当您不在派生类中重新定义函数并调用该函数时,编译器会检查派生的范围,发现其中没有定义这样的函数。然后它检查基类的范围,发现函数并相应地将函数调用绑定到这个特定的定义。

但是,当您重新定义具有不同签名的函数时,编译器会将调用与该函数匹配,看到不一致并简单地抱怨。

您可以通过在派生类定义中添加“using Base::gogo;”来更改此行为。我希望这能解释。

于 2012-04-16T12:13:25.407 回答
0

由于您将第二个声明obj为 a Base*,因此 vtable 为其提供了Base. 虽然对于被 覆盖的虚方法,被覆盖的Derived版本被调用,但其他方法(或方法重载)仍然是在 中声明的那些Base

但是,如果您将指针声明为Derived*,则 vtable 将为它提供 的方法Derived,将具有相同名称的方法隐藏在Base. 因此,obj->gogo(7);将不起作用。同样,这也是非法的:

Base* obj = new Derived();

// legal, since obj is a pointer to Base, it contains the gogo(int) method.
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden.
(reinterpret_cast<Derived*>(obj))->gogo(7);

这是合法的:

Derived* obj = new Derived ;
obj->Base::gogo(7); // legal.

这里

于 2012-04-16T11:35:47.717 回答