188

基本クラス (またはインターフェイス クラス) を宣言し、そのパラメーターの 1 つ以上の既定値を指定する場合、派生クラスは同じ既定値を指定する必要がありますか? そうでない場合、どの既定値が派生クラスで明示されますか?

補遺: また、これがさまざまなコンパイラ間でどのように処理されるか、およびこのシナリオでの「推奨される」プラクティスに関する入力にも興味があります。

4

6 に答える 6

238

仮想にはデフォルトがある場合があります。基本クラスのデフォルトは、派生クラスには継承されません。

どちらのデフォルトが使用されるか (基本クラスまたは派生クラス) は、関数の呼び出しに使用される静的型によって決まります。基本クラスのオブジェクト、ポインター、または参照を介して呼び出す場合は、基本クラスで示されている既定値が使用されます。逆に、派生クラス オブジェクト、ポインター、または参照を介して呼び出す場合は、派生クラスで示されている既定値が使用されます。これを示す標準見積の下に例があります。

一部のコンパイラは異なる処理を行う場合がありますが、C++03 および C++11 標準では次のように規定されています。

8.3.6.10:

仮想関数呼び出し (10.3) は、オブジェクトを示すポインターまたは参照の静的型によって決定される仮想関数の宣言で、既定の引数を使用します。派生クラスのオーバーライド関数は、オーバーライドする関数から既定の引数を取得しません。例:

struct A {
  virtual void f(int a = 7);
};
struct B : public A {
  void f(int a);
};
void m()
{
  B* pb = new B;
  A* pa = pb;
  pa->f(); //OK, calls pa->B::f(7)
  pb->f(); //error: wrong number of arguments for B::f()
}

以下は、どのデフォルトが選択されるかを示すサンプル プログラムです。ここでは、簡潔にするために単に esではstructなく sを使用しています。デフォルトの可視性を除いて、ほぼすべての点でまったく同じです。classclassstruct

#include <string>
#include <sstream>
#include <iostream>
#include <iomanip>

using std::stringstream;
using std::string;
using std::cout;
using std::endl;

struct Base { virtual string Speak(int n = 42); };
struct Der : public Base { string Speak(int n = 84); };

string Base::Speak(int n) 
{ 
    stringstream ss;
    ss << "Base " << n;
    return ss.str();
}

string Der::Speak(int n)
{
    stringstream ss;
    ss << "Der " << n;
    return ss.str();
}

int main()
{
    Base b1;
    Der d1;

    Base *pb1 = &b1, *pb2 = &d1;
    Der *pd1 = &d1;
    cout << pb1->Speak() << "\n"    // Base 42
        << pb2->Speak() << "\n"     // Der 42
        << pd1->Speak() << "\n"     // Der 84
        << endl;
}

このプログラムの出力 (MSVC10 および GCC 4.4 上) は次のとおりです。

Base 42
Der 42
Der 84
于 2010-08-20T18:17:22.373 に答える
46

これは、Herb Sutter の初期のGuru of the Week の投稿の 1 つのトピックでした。

この件に関して彼が最初に言うことは、それをしないでください。

より詳細には、はい、さまざまなデフォルト パラメータを指定できます。仮想関数と同じようには機能しません。仮想関数はオブジェクトの動的タイプで呼び出されますが、デフォルトのパラメーター値は静的タイプに基づいています。

与えられた

class A {
    virtual void foo(int i = 1) { cout << "A::foo" << i << endl; }
};
class B: public A {
    virtual void foo(int i = 2) { cout << "B::foo" << i << endl; }
};
void test() {
A a;
B b;
A* ap = &b;
a.foo();
b.foo();
ap->foo();
}

A::foo1 B::foo2 B::foo1 を取得する必要があります

于 2010-08-20T18:17:52.420 に答える
9

他の回答が詳述しているように、それは悪い考えです。ただし、シンプルで効果的な解決策については誰も言及していないため、次のとおりです。パラメーターを構造体に変換すると、構造体メンバーにデフォルト値を設定できます!

その代わりに、

//bad idea
virtual method1(int x = 0, int y = 0, int z = 0)

これを行う、

//good idea
struct Param1 {
  int x = 0, y = 0, z = 0;
};
virtual method1(const Param1& p)
于 2017-08-05T02:51:04.763 に答える
6

他の回答からわかるように、これは複雑な問題です。これをやろうとしたり、それが何をするのかを理解しようとする代わりに (あなたが今質問しなければならないのであれば、メンテナーは今から 1 年後に質問するか調べる必要があります)。

代わりに、基本クラスに既定のパラメーターを使用してパブリックの非仮想関数を作成します。次に、既定のパラメーターを持たず、必要に応じて子クラスでオーバーライドされるプライベートまたは保護された仮想関数を呼び出します。そうすれば、それがどのように機能するかの詳細について心配する必要はなく、コードは非常に明白です。

于 2010-08-20T18:22:45.770 に答える
5

これは、おそらくテストによって十分に理解できるものです (つまり、言語の十分に主流な部分であり、ほとんどのコンパイラーがほぼ確実に正しく理解しているため、コンパイラー間の違いが見られない限り、それらの出力はかなり信頼できると見なすことができます)。

#include <iostream>

struct base { 
    virtual void x(int a=0) { std::cout << a; }
    virtual ~base() {}
};

struct derived1 : base { 
    void x(int a) { std:: cout << a; }
};

struct derived2 : base { 
    void x(int a = 1) { std::cout << a; }
};

int main() { 
    base *b[3];
    b[0] = new base;
    b[1] = new derived1;
    b[2] = new derived2;

    for (int i=0; i<3; i++) {
        b[i]->x();
        delete b[i];
    }

    derived1 d;
    // d.x();       // won't compile.
    derived2 d2;
    d2.x();
    return 0;
}
于 2010-08-20T18:14:11.813 に答える