C ++重载解析

时间:2020-03-05 18:55:52  来源:igfitidea点击:

给定以下示例,为什么我必须显式地使用语句b-> A :: DoSomething()而不是仅仅使用b-> DoSomething()

编译器的重载决议不应该弄清楚我在说哪种方法?

我使用的是Microsoft VS2005. (注意:在这种情况下,使用虚拟无助。)

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的范围内:

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

回答

重载解析是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;
}

回答

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

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

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

回答

在继承树中查找要使用的函数时,C ++使用不带参数的名称,一旦发现任何定义,它将停止,然后检查参数。在给定的示例中,它停止在B类中。为了能够执行我们想做的事情,应按以下方式定义B类:

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

回答

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

回答

不,存在此行为是为了确保我们不会被错误地从远程基类继承而陷入困境。

为了解决这个问题,我们需要通过在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进行重载解析

回答

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

回答

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

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