派生クラスから基本クラスのコンストラクターを呼び出すための C++ の規則は何ですか?
たとえば、Java では、サブクラス コンストラクターの最初の行としてそれを行う必要があることを知っています (そうしないと、引数のないスーパー コンストラクターへの暗黙的な呼び出しが想定されます。それがない場合はコンパイル エラーが発生します)。 .
派生クラスから基本クラスのコンストラクターを呼び出すための 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
}
};
C ++では、コンストラクターに入る前に、すべてのスーパークラスとメンバー変数の引数なしのコンストラクターが呼び出されます。それらに引数を渡したい場合は、「コンストラクターチェーン」と呼ばれる別の構文があります。これは次のようになります。
class Sub : public Base
{
Sub(int x, int y)
: Base(x), member(y)
{
}
Type member;
};
この時点で何かがスローされた場合、以前に建設を完了した基地/メンバーはデストラクタを呼び出し、例外は呼び出し元に再スローされます。チェーン中に例外をキャッチしたい場合は、関数tryブロックを使用する必要があります。
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_ の値を代入するよりも望ましい方法です。
データ メンバーは、初期化リスト内の順序に関係なく、クラス定義で宣言された順序で常に初期化されることに注意してください。データメンバーが相互に依存している場合に発生する可能性のある奇妙なバグを回避するには、初期化リストとクラス定義でメンバーの順序が同じであることを常に確認する必要があります。同じ理由で、基本クラスのコンストラクターは初期化リストの最初の項目でなければなりません。完全に省略すると、基本クラスのデフォルトのコンストラクターが自動的に呼び出されます。その場合、基本クラスに既定のコンストラクターがない場合は、コンパイラ エラーが発生します。
引数のないコンストラクターがある場合、派生クラスのコンストラクターが実行される前に呼び出されます。
引数を指定して base-constructor を呼び出したい場合は、次のように派生コンストラクターに明示的に記述する必要があります。
class base
{
public:
base (int arg)
{
}
};
class derived : public base
{
public:
derived () : base (number)
{
}
};
C++ では、親コンストラクターを呼び出さずに派生クラスを構築することはできません。これは、引数のない C'tor の場合は自動的に発生するか、上記のように派生コンストラクターを直接呼び出すか、コードがコンパイルされない場合に発生します。
親コンストラクターに値を渡す唯一の方法は、初期化リストを使用することです。初期化リストは、:を使用して実装され、次にクラスのリストとそのクラスコンストラクターに渡される値が含まれます。
Class2::Class2(string id) : Class1(id) {
....
}
また、親クラスでパラメーターを受け取らないコンストラクターがある場合は、子コンストラクターが実行される前に自動的に呼び出されることにも注意してください。
基本コンストラクターにデフォルトのパラメーターがある場合、基本クラスは自動的に呼び出されます。
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
CDerived::CDerived()
: CBase(...), iCount(0) //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
{
//construct body
}