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

2 つの「オーバーロード」は同じスコープにありません。デフォルトでは、コンパイラは、一致する名前が見つかるまで、可能な限り小さい名前スコープのみを考慮します。引数の一致は後で行われます。あなたの場合、これはコンパイラがB::DoSomething. 次に、引数リストを照合しようとしますが、失敗します。

A1 つの解決策は、オーバーロードをBのスコープにプルダウンすることです。

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

オーバーロードの解決は、C++ の最も醜い部分の 1 つです。

基本的に、コンパイラは 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

派生クラスにメソッドが存在すると、基本クラスでは (パラメーターに関係なく) 同じ名前のすべてのメソッドが非表示になります。これは、次のような問題を回避するために行われます。

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 に答える
5

いいえ、この動作は、誤って離れた基本クラスから継承していることに気付かないようにするために存在します。

これを回避するには、B クラスに using A::DoSomething を配置して、呼び出したいメソッドをコンパイラに伝える必要があります。

この動作の簡単な概要については、この記事を参照してください。

于 2008-09-16T13:27:02.017 に答える
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

それは過負荷ではありません!それは隠れています!

于 2008-09-16T13:57:25.437 に答える
2

派生クラスで関数を定義すると、基本クラスでその名前を持つすべての関数が非表示になります。基本クラス関数が仮想で、互換性のある署名がある場合、派生クラス関数も基本クラス関数をオーバーライドします。ただし、それは視認性には影響しません。

using 宣言を使用して、基底クラスの関数を可視にすることができます。

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  
于 2008-09-16T13:19:23.720 に答える
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

関数は、サブクラス内の同じ名前の関数によって隠されています (ただし、署名は異なります)。A::DoSomething(); を使用する場合のように、using ステートメントを使用して非表示を解除できます。

于 2008-09-16T13:20:37.663 に答える