本问题已经有最佳答案,请猛点这里访问。
鉴于以下示例,为什么我必须显式使用语句b->A::DoSomething()而不仅仅是b->DoSomething()?
编译器的重载决议不应该弄清楚我在谈论哪种方法?
我正在使用Microsoft VS 2005.(注意:在这种情况下使用虚拟无效。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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;
} |
两个"重载"不在同一范围内。默认情况下,编译器仅在找到名称匹配项之前才考虑可能的最小名称范围。之后进行参数匹配。在您的情况下,这意味着编译器看到B::DoSomething。然后它尝试匹配参数列表,该列表失败。
一种解决方案是将重载从A下拉到B的范围:
1 2 3 4 5
| class B : public A {
public:
using A::DoSomething;
// …
} |
重载分辨率是C ++中最丑陋的部分之一
基本上,编译器在B的范围内找到名称匹配"DoSomething(int)",看到参数不匹配,并且因错误而停止。
可以通过使用B类中的A :: DoSomething来克服它
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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;
} |
派生类中方法的存在会隐藏基类中具有相同名称(无论参数)的所有方法。这样做是为了避免这样的问题:
1 2 3 4 5 6 7 8
| class A {} ;
class B :public A
{
void DoSomething(long) {...}
}
B b;
b.DoSomething(1); // calls B::DoSomething((long)1)); |
比以后有人改变了A类:
1 2 3 4
| class A
{
void DoSomething(int ) {...}
} |
现在突然:
1 2
| B b;
b.DoSomething(1); // calls A::DoSomething(1); |
换句话说,如果它不能像这样工作,那么你不能控制的类(A)中的无关变化可能会默默地影响代码的工作方式。
不,这种行为是存在的,以确保您不会被错误地从远程基类继承。
要绕过它,您需要通过在B类中放置一个使用A :: DoSomething来告诉编译器您要调用哪个方法。
有关此行为的快速简单概述,请参阅此文章。
这与名称解析的工作方式有关。基本上,我们首先找到名称来自的范围,然后我们收集该范围内该名称的所有重载。但是,您的案例范围是B类,而在B类中,B :: DoSomething隐藏A :: DOSomething:
3.3.7名称隐藏[basic.scope.hiding]
... [剪断] ...
3在成员函数定义中,隐藏本地名称的声明
同一名成员的声明;看到
basic.scope.class。派生类中成员的声明
(class.derived)隐藏基类成员的声明
同名;见class.member.lookup。
由于名称隐藏,A :: DoSomething甚至不考虑重载解析
那不是超载!那是在隐藏!
在派生类中定义函数时,它会在基类中隐藏具有该名称的所有函数。如果基类函数是虚函数并且具有兼容签名,则派生类函数也会覆盖基类函数。但是,这不会影响可见性。
您可以使用using声明使基类函数可见:
1 2 3 4 5 6
| class B : public A
{
public:
int DoSomething(int x) {return 1;};
using A::DoSomething;
}; |
该函数由子类中具有相同名称的函数隐藏(但具有不同的签名)。您可以使用using语句取消隐藏它,就像使用A :: DoSomething();
在向继承树中搜索要使用的函数时,C ++使用不带参数的名称,一旦找到它停止的任何定义,然后检查参数。在给出的示例中,它在B类中停止。为了能够完成您的工作,B类应该像这样定义:
1 2 3 4 5 6
| class B : public A
{
public:
using A::DoSomething;
int DoSomething(int x) {return 1;};
}; |