Abe
问问题
12425 次
41

鉴于以下示例,为什么我必须明确使用该语句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;
}
4

9 回答 9

43

这两个“重载”不在同一个范围内。默认情况下,编译器只考虑可能的最小名称范围,直到找到名称匹配。参数匹配是在之后完成的。在您的情况下,这意味着编译器会看到B::DoSomething. 然后它尝试匹配参数列表,但失败了。

一种解决方案是将重载从AintoB的范围内拉下来:

class B : public A {
public:
    using A::DoSomething;
    // …
}
于 2008-09-16T13:18:27.153 回答
14

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

基本上,编译器在 B 的范围内找到一个名称匹配“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;
}
于 2008-09-16T13:18:43.907 回答
5

不,这种行为的存在是为了确保您不会被错误地从远处的基类中继承。

要绕过它,您需要通过在 B 类中放置 using A::DoSomething 来告诉编译器您要调用哪个方法。

有关此行为的快速简便的概述,请参阅本文

于 2008-09-16T13:27:02.017 回答
5

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

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) 中的不相关更改可能会默默地影响你的代码的工作方式。

于 2008-09-16T17:21:01.343 回答
3

这与名称解析的工作方式有关。基本上,我们首先找到名称来自的范围,然后我们收集该范围内该名称的所有重载。但是,您的情况的范围是 B 类,而在 B 类中,B::DoSomething隐藏了 A::DOSomething:

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

...[剪辑]...

3 在成员函数定义中,局部名称的声明隐藏了类同名成员的声明;见 basic.scope.class。派生类(class.derived)中成员的声明隐藏了同名基类成员的声明;见class.member.lookup

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

于 2008-09-16T13:30:17.360 回答
2

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

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

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  
于 2008-09-16T13:19:23.720 回答
2

那不是超载!那就是隐藏!

于 2008-09-16T13:57:25.437 回答
1

在搜索要使用的函数的继承树时,C++ 使用不带参数的名称,一旦找到任何定义,它就会停止,然后检查参数。在给出的示例中,它在 B 类中停止。为了能够做你想做的事情,B 类应该这样定义:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 
于 2008-09-16T13:19:24.857 回答
1

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

于 2008-09-16T13:20:37.663 回答