C ++重载parsing

给出下面的例子,为什么我必须明确地使用语句b->A::DoSomething()而不仅仅是b->DoSomething()

编译器的重载决议不应该找出我正在谈论的方法吗?

我正在使用Microsoft VS 2005.(注意:在这种情况下使用虚拟不起作用。)

 class A { public: int DoSomething() {return 0;}; }; class B : public A { public: int DoSomething(int x) {return 1;}; }; int main() { B* b = new B(); b->A::DoSomething(); //Why this? //b->DoSomething(); //Why not this? (Gives compiler error.) delete b; return 0; } 

两个“超载”不在同一个范围内。 默认情况下,编译器只考虑尽可能小的名称范围,直到find名称匹配。 之后完成参数匹配。 在你的情况下,这意味着编译器看到B::DoSomething 。 然后它试图匹配失败的参数列表。

一个解决办法就是把超载从A降到B的范围:

 class B : public A { public: using A::DoSomething; // … } 

重载parsing是C ++中最丑陋的部分之一

基本上,编译器在B的范围内find名称匹配的“DoSomething(int)”,看到参数不匹配,并停止并出现错误。

它可以通过使用B类中的A :: DoSomething来克服

 class A { public: int DoSomething() {return 0;} }; class B : public A { public: using A::DoSomething; int DoSomething(int x) {return 1;} }; int main(int argc, char** argv) { B* b = new B(); // b->A::DoSomething(); // still works, but... b->DoSomething(); // works now too delete b; return 0; } 

不,这种行为是为了确保你不会被错误地从远处的基类inheritance而来。

为了解决这个问题,你需要通过在B类中放置一个使用的A :: DoSomething来告诉编译器你想调用哪个方法。

请参阅这篇文章,以便快速简单地了解这种行为。

派生类中存在的方法隐藏了基类中所有具有相同名称(不pipe参数)的方法。 这样做是为了避免这样的问题:

 class A {} ; class B :public A { void DoSomething(long) {...} } B b; b.DoSomething(1); // calls B::DoSomething((long)1)); 

后来有人改变了A类:

 class A { void DoSomething(int ) {...} } 

现在突然:

 B b; b.DoSomething(1); // calls A::DoSomething(1); 

换句话说,如果它不是这样工作的,那么一个你无法控制的类(A)中的一个无关的变化可能会默默地影响你的代码的工作方式。

这与名称parsing的工作方式有关。 基本上,我们首先find名称的范围,然后我们收集该范围内的所有重载名称。 然而,在你的情况下作用域是B类,而在B类,B :: DoSomething 隐藏 A :: DOSomething:

3.3.7名称隐藏[basic.scope.hiding]

… [剪断] …

3在成员函数定义中,本地名称的声明隐藏了具有相同名称的类的成员的声明; 请参阅basic.scope.class 。 派生类( class.derived )中的成员声明隐藏了同名基类成员的声明; 见class.member.lookup

由于名称隐藏,A :: DoSomething甚至不考虑重载parsing

当你在一个派生类中定义一个函数时,它将隐藏所有在基类中具有这个名字的函数。 如果基类函数是虚拟的并且具有兼容的签名,那么派生类函数也会覆盖基类函数。 但是,这并不影响可见性。

您可以使用using声明使基类函数可见:

 class B : public A { public: int DoSomething(int x) {return 1;}; using A::DoSomething; }; 

这不是超载! 这是隐藏的!

当在inheritance树中search要使用的函数时,C ++使用没有参数的名称,一旦find它停止的定义,然后检查参数。 在给出的例子中,它在类B中停止。为了能够做到以后的事情,类B应该像这样定义:

 class B : public A { public: using A::DoSomething; int DoSomething(int x) {return 1;}; }; 

该函数被隐藏在子类中具有相同名称的函数(但具有不同的签名)。 您可以使用using语句来取消隐藏,如使用A :: DoSomething();