サブクラスのスーパークラス・コンストラクターからスーパークラス・コンストラクターを呼び出すためのC ++の規則は何ですか?
たとえば、Javaでは、サブクラスコンストラクタの最初の行として実行する必要があります(そうでない場合は、引数なしのスーパーコンストラクタへの暗黙の呼び出しが想定されます。欠落している場合はコンパイルエラーが発生します)。 。
引数がない場合は、基本クラスのコンストラクタが自動的に呼び出されます。スーパークラスのコンストラクタを引数で呼び出したい場合は、サブクラスのコンストラクタ初期化リストを使用しなければなりません。 Javaとは異なり、C ++は多重継承をサポートしているため(良くも悪くも)、基本クラスは "super()"ではなく名前で参照する必要があります。
class SuperClass
{
public:
SuperClass(int foo)
{
// do something with foo
}
};
class SubClass : public SuperClass
{
public:
SubClass(int foo, int bar)
: SuperClass(foo) // Call the superclass constructor in the subclass' initialization list.
{
// do something with bar
}
};
SubClass anObject(1,2)
します、1
に渡されるSuperClass(foo)
(paramaterの議論になるfoo
)?私はドキュメントの高値と安値を検索してきましたが、SubClassコンストラクタへの引数をSuperClassコンストラクタへの引数として渡すことができるとは明確に述べていません。 - LazerSharks: SuperClass(foo)
部分。foo
はスーパークラスのコンストラクタに明示的に渡されています。 - luke
C ++では、すべてのスーパークラスとメンバ変数の引数を持たないコンストラクタが、コンストラクタを入力する前に呼び出されます。引数を渡したい場合は、「コンストラクタチェーン」と呼ばれるこれとは別の構文があります。これは次のようになります。
class Sub : public Base
{
Sub(int x, int y)
: Base(x), member(y)
{
}
Type member;
};
この時点で何かが実行された場合、以前に構築を完了した拠点/メンバーにデストラクタが呼び出され、例外は呼び出し元に返されます。連鎖中に例外をキャッチしたい場合は、関数try blockを使用する必要があります。
class Sub : public Base
{
Sub(int x, int y)
try : Base(x), member(y)
{
// function body goes here
} catch(const ExceptionType &e) {
throw kaboom();
}
Type member;
};
この形式では、tryブロックがです関数の本体の内側ではなく、関数の本体。これにより、関数の本体中だけでなく、暗黙的または明示的なメンバーおよび基本クラスの初期化によってスローされた例外をキャッチすることができます。ただし、関数catchブロックが別の例外をスローしない場合、ランタイムは元のエラーを再スローします。初期化中の例外できない無視してください。
C ++には、コンストラクタの初期化リストという概念があります。これは、基本クラスのコンストラクタを呼び出すことができる場所と呼び出す場所、およびデータメンバを初期化する場所です。初期化リストは、コロンに続くコンストラクタシグネチャの後、およびコンストラクタ本体の前にあります。クラスAがあるとしましょう。
class A : public B
{
public:
A(int a, int b, int c);
private:
int b_, c_;
};
次に、Bがintをとるコンストラクタを持つと仮定すると、Aのコンストラクタは次のようになります。
A::A(int a, int b, int c)
: B(a), b_(b), c_(c) // initialization list
{
// do something
}
ご覧のとおり、基本クラスのコンストラクターは初期化リストで呼び出されます。ちなみに、初期化リストのデータメンバーを初期化することは、追加のコストを節約するため、コンストラクタの本体内でb_とc_の値を割り当てるよりも望ましい方法です。
データメンバは、初期化リストの順序に関係なく、常にクラス定義で宣言されている順序で初期化されることに注意してください。データメンバーが互いに依存している場合に発生する可能性がある奇妙なバグを避けるために、メンバーの順序が初期化リストとクラス定義で同じであることを常に確認する必要があります。同じ理由で、基本クラスのコンストラクタは初期化リストの最初の項目でなければなりません。省略した場合は、基本クラスのデフォルトコンストラクタが自動的に呼び出されます。その場合、基本クラスにデフォルトコンストラクタがないと、コンパイラエラーが発生します。
誰もが初期化リストを通してコンストラクタを呼び出すことについて言及しました、しかし誰も親クラスのコンストラクタが派生メンバのコンストラクタの本体から明示的に呼ばれることができるとは言いませんでした。質問を見るサブクラスから基本クラスのコンストラクタを呼び出す'コンストラクタ本体、 例えば。 ポイントは、派生クラスの本体で親クラスまたはスーパークラスのコンストラクタへの明示的な呼び出しを使用する場合、実際には単に親クラスのインスタンスを作成することであり、派生オブジェクトの親クラスのコンストラクタを呼び出すことではありません。 。派生クラスのオブジェクトで親クラスまたはスーパークラスのコンストラクタを呼び出す唯一の方法は、初期化リストを使用することであり、派生クラスのコンストラクタ本体にはありません。したがって、「スーパークラスコンストラクター呼び出し」と呼ばれるべきではありません。誰かが(私がしたように)混乱するかもしれないので、私はこの答えをここに置いた。
値を親コンストラクタに渡す唯一の方法は、初期化リストを使用することです。初期化リストは:で実装され、次にクラスのリストとそのクラスコンストラクタに渡される値が実装されます。
Class2::Class2(string id) : Class1(id) {
....
}
また、親クラスでパラメータを受け取らないコンストラクタがある場合は、子コンストラクタが実行される前に自動的に呼び出されることにも注意してください。
引数のないコンストラクタがある場合は、派生クラスのコンストラクタが実行される前に呼び出されます。
引数付きでベースコンストラクタを呼び出したい場合は、このように派生コンストラクタに明示的に記述する必要があります。
class base
{
public:
base (int arg)
{
}
};
class derived : public base
{
public:
derived () : base (number)
{
}
};
C ++でparentコンストラクタを呼び出さずに派生クラスを構築することはできません。それが引数でないC'torであるなら、それは自動的に起こる、それはあなたが上で示されるように直接派生コンストラクタを呼ぶか、またはあなたのコードがコンパイルしないならば起こる。
CDerived::CDerived()
: CBase(...), iCount(0) //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
{
//construct body
}
基本コンストラクタにデフォルトパラメータがある場合は、基本クラスが自動的に呼び出されます。
using namespace std;
class Base
{
public:
Base(int a=1) : _a(a) {}
protected:
int _a;
};
class Derived : public Base
{
public:
Derived() {}
void printit() { cout << _a << endl; }
};
int main()
{
Derived d;
d.printit();
return 0;
}
出力は次のとおりです。1
Base()
と同じボディを持っていますBase(int)
ゴールと暗黙の初期化: _a{1}
。さんですBase()
init-listに特定の基本コンストラクタが連鎖していない場合は常に呼び出されます。そして、他の場所で述べたように、C ++ 11の委譲コンストラクタと中括弧または同等の初期化は、デフォルト引数をかなり必要としなくします(多くの例ですでにコード臭いの場合)。 - underscore_d
クラスが複数のクラスから派生する場合のコンストラクタ呼び出しの順序については、誰も言及しませんでした。順序は、クラスを派生させるときに説明したとおりです。