スーパークラスのコンストラクターを呼び出すためのルールは何ですか?
-
02-07-2019 - |
質問
サブクラスのコンストラクターからスーパークラスのコンストラクターを呼び出すための 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_ の値を割り当てるよりも推奨されます。割り当てにかかる余分なコストが節約できるからです。
データ メンバーは、初期化リスト内の順序に関係なく、常にクラス定義で宣言された順序で初期化されることに注意してください。データ メンバーが相互に依存している場合に発生する可能性のある奇妙なバグを回避するには、初期化リストとクラス定義でのメンバーの順序が同じであることを常に確認する必要があります。同じ理由で、基本クラスのコンストラクターは初期化リストの最初の項目でなければなりません。完全に省略すると、基本クラスのデフォルトのコンストラクターが自動的に呼び出されます。この場合、基本クラスにデフォルトのコンストラクターがないと、コンパイラ エラーが発生します。
初期化リストを介したコンストラクターの呼び出しについては誰もが言及しましたが、親クラスのコンストラクターを派生メンバーのコンストラクターの本体から明示的に呼び出すことができるとは誰も言いませんでした。質問を参照してください サブクラスのコンストラクター本体から基本クラスのコンストラクターを呼び出す, 、 例えば。重要なのは、派生クラスの本体で親クラスまたはスーパークラスのコンストラクターへの明示的な呼び出しを使用する場合、これは実際には親クラスのインスタンスを作成するだけであり、派生オブジェクトで親クラスのコンストラクターを呼び出すわけではないということです。 。派生クラスのオブジェクトで親クラスまたはスーパークラスのコンストラクターを呼び出す唯一の方法は、派生クラスのコンストラクター本体ではなく、初期化リストを使用することです。したがって、これを「スーパークラス コンストラクター呼び出し」と呼ぶべきではないかもしれません。誰かが(私と同じように)混乱する可能性があるため、この回答をここに置きました。
親コンストラクターに値を渡す唯一の方法は、初期化リストを使用することです。初期化リストは次のように実装されます。次に、クラスのリストと、そのクラスのコンストラクターに渡される値です。
Class2::Class2(string id) : Class1(id) {
....
}
また、親クラスでパラメーターを受け取らないコンストラクターがある場合、そのコンストラクターは、子コンストラクターが実行される前に自動的に呼び出されることにも注意してください。
引数のないコンストラクターがある場合、派生クラスのコンストラクターが実行される前に呼び出されます。
引数を指定して基本コンストラクターを呼び出したい場合は、次のように派生コンストラクターでそれを明示的に記述する必要があります。
class base
{
public:
base (int arg)
{
}
};
class derived : public base
{
public:
derived () : base (number)
{
}
};
C++ では、親コンストラクターを呼び出さずに派生クラスを構築することはできません。これは、非引数 C'tor の場合は自動的に発生するか、上記のように派生コンストラクターを直接呼び出した場合に発生します。そうしないとコードがコンパイルされません。
基本コンストラクターにデフォルトのパラメーターがある場合、基本クラスが自動的に呼び出されます。
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
}
クラスが複数のクラスから派生する場合のコンストラクター呼び出しのシーケンスについては誰も言及しませんでした。シーケンスはクラスの導出時に説明したとおりです。