基本クラス(またはインターフェイスクラス)を宣言し、そのパラメーターの1つ以上にデフォルト値を指定する場合、派生クラスは同じデフォルトを指定する必要がありますか?そうでない場合、どのデフォルトが派生クラスに現れますか?
補遺:私はまた、これがさまざまなコンパイラでどのように処理されるか、およびこのシナリオでの「推奨される」実践に関する入力に興味があります。
基本クラス(またはインターフェイスクラス)を宣言し、そのパラメーターの1つ以上にデフォルト値を指定する場合、派生クラスは同じデフォルトを指定する必要がありますか?そうでない場合、どのデフォルトが派生クラスに現れますか?
補遺:私はまた、これがさまざまなコンパイラでどのように処理されるか、およびこのシナリオでの「推奨される」実践に関する入力に興味があります。
回答:
仮想にはデフォルトがある場合があります。基本クラスのデフォルトは、派生クラスによって継承されません。
どのデフォルトが使用されるか(つまり、基本クラスまたは派生クラス)は、関数の呼び出しに使用される静的型によって決まります。基本クラスのオブジェクト、ポインター、または参照を介して呼び出す場合、基本クラスで示されているデフォルトが使用されます。逆に、派生クラスオブジェクト、ポインター、または参照を介して呼び出す場合は、派生クラスで示されるデフォルトが使用されます。これを示す標準的な引用の下に例があります。
一部のコンパイラは異なる動作をする場合がありますが、これは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() }
以下は、どのデフォルトが選択されるかを示すサンプルプログラムです。私が使用しているstruct
のではなく、ここでSをclass
単に簡潔にするために、ES - class
とstruct
、デフォルトの可視性を除いて、ほぼすべての方法でまったく同じです。
#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
これは、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を取得する必要があります
取得されるデフォルトの引数はオブジェクトの静的な型に依存するため、これは悪い考えです。virtual
ディスパッチされる関数は動的な型に依存します。
つまり、デフォルトの引数で関数を呼び出すと、関数がそうであるかどうかに関係なく、コンパイル時にデフォルトの引数が代入されますvirtual
。
@cppcoderは、[クローズド]の質問で次の例を提供しました。
struct A {
virtual void display(int i = 5) { std::cout << "Base::" << i << "\n"; }
};
struct B : public A {
virtual void display(int i = 9) override { std::cout << "Derived::" << i << "\n"; }
};
int main()
{
A * a = new B();
a->display();
A* aa = new A();
aa->display();
B* bb = new B();
bb->display();
}
次の出力が生成されます。
Derived::5
Base::5
Derived::9
上記の説明を参考にすれば、その理由が簡単にわかります。コンパイラーは、コンパイル時に、ポインターの静的型のメンバー関数からのデフォルト引数を代用し、main
関数を以下と同等にします。
A * a = new B();
a->display(5);
A* aa = new A();
aa->display(5);
B* bb = new B();
bb->display(9);
他の答えからわかるように、これは複雑なテーマです。これを行うか、それが何をするかを理解しようとする代わりに(もしあなたが今尋ねなければならないなら、メンテナは今から1年それを尋ねるか調べる必要があります)。
代わりに、デフォルトのパラメーターを使用して、基本クラスにパブリック非仮想関数を作成します。次に、デフォルトのパラメーターを持たず、必要に応じて子クラスでオーバーライドされるプライベートまたは保護された仮想関数を呼び出します。そうすれば、それがどのように機能するかの詳細について心配する必要はなく、コードは非常に明白です。
これはおそらくテストによってかなり適切に理解できるものです(つまり、ほとんどのコンパイラーがほぼ間違いなく正しく理解する言語の十分に主流の部分であり、コンパイラー間の違いが見られない限り、それらの出力はかなり信頼できると見なすことができます)。
#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;
}
他の答えが詳しく述べているように、その悪い考え。ただし、簡単で効果的なソリューションについては誰も触れていないため、ここで説明します。パラメーターを構造体に変換すると、構造体のメンバーにデフォルト値を設定できます。
代わりに、
//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)