推荐另一个so question
考虑代码:
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)
合法。这似乎意味着Derived
的vtable包含
ptr到
应该隐藏virtual void gogo(int a)
。
我的困惑是,因为名称隐藏导致案例1)非法,那么2)中的调用如何在运行时解决
a)Derived的vtable是否包含指向gogo(int)的指针。
b)如果a)不为True,虚函数的调用解析是否会进入基类的vtable。
答案 0 :(得分:5)
您正在混淆虚函数调用和重载解析。
所有派生类都包含包含所有虚函数的vtable,包括基类和任何其他自己的虚函数。这用于解决运行时的调用,如您的情况2)。
在情况1)中,您在编译时时从重载解析中收到错误。由于名称隐藏,类Derived
只有一个可调用函数。您唯一的选择是使用int*
调用该函数。
答案 1 :(得分:2)
您想要覆盖重载的功能,但隐藏规则不会像这样工作。
“隐藏规则说明内部范围内的实体会在外部范围内隐藏具有相同名称的内容。”
注意,它具有不同的签名是无关紧要的,即gogo(int* a)
将隐藏Base中的所有gogo(whatever)
函数。仅当您的函数具有相同的名称,相同的签名和虚拟时才会进行覆盖(因此,仅覆盖gogo(int* a)
)。
C ++ FAQ书建议使用“调用非重载虚拟的非虚拟重载”。 (第29.05章)。基本上,您在基类中创建非虚拟重载函数:
gogo(int a) and gogo(int* a)
将分别调用虚函数:
虚拟gogo_i(int a)和虚拟gogo_pi(int * a)
并在Derived类中覆盖此虚拟内容。
答案 2 :(得分:1)
基本上,只有在相同范围内定义同名函数时才会发生函数重载。现在,Base类有自己的作用域,派生类也有自己的作用。
因此,当你不重新定义派生类中的函数并调用该函数时,编译器会检查派生的范围,发现那里没有定义这样的函数。然后它检查基类的范围,发现函数并相应地将函数调用绑定到这个特定的定义。
但是,当你用不同的签名重新定义函数时,编译器会将调用与此函数匹配,看到不一致并且只是抱怨。
您可以通过在派生类defenision中添加“using Base :: gogo;”来更改此行为。 我希望这可以解释。
答案 3 :(得分:0)
由于您将第二个obj
声明为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.
请参阅here。