web-dev-qa-db-ja.com

スーパークラスのコンストラクタを呼び出すための規則は何ですか?

サブクラスからスーパークラスのコンストラクタを呼び出すためのC++の規則は何ですか?

例えば、私はJavaで知っています、あなたはそれをサブクラスコンストラクタの最初の行としてやらなければなりません(もしそうでなければ、引数なしのスーパーコンストラクタへの暗黙の呼び出しが想定されます。 。

620
levik

引数がない場合、基本クラスのコンストラクタは自動的に呼び出されます。スーパークラスのコンストラクタを引数で呼び出したい場合は、サブクラスのコンストラクタ初期化リストを使用しなければなりません。 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
        }
};

コンストラクタの初期化リストの詳細情報 here および here

841
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ブロックisが関数本体の内側ではなく、関数本体の本体になっていることに注意してください。これにより、関数の本体中だけでなく、暗黙的または明示的なメンバーおよび基本クラスの初期化によってスローされた例外をキャッチすることができます。ただし、function catchブロックが別の例外をスローしない場合、ランタイムは元のエラーを再スローします。初期化中の例外cannotは無視されます。

217
puetzk

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_の値を割り当てるよりも望ましい方法です。

データメンバは、初期化リストの順序に関係なく、常にクラス定義で宣言されている順序で初期化されることに注意してください。データメンバーが互いに依存している場合に発生する可能性がある奇妙なバグを回避するには、メンバーの順序が初期化リストとクラス定義で同じであることを常に確認する必要があります。同じ理由で、基本クラスのコンストラクタは初期化リストの最初の項目でなければなりません。省略した場合は、基本クラスのデフォルトのコンストラクタが自動的に呼び出されます。その場合、基本クラスにデフォルトコンストラクタがないと、コンパイラエラーが発生します。

48
Dima

誰もが初期化リストを通してコンストラクタを呼び出すことについて言及しました、しかし誰も親クラスのコンストラクタが派生メンバのコンストラクタの本体から明示的に呼ばれることができるとは言いませんでした。たとえば、質問{ サブクラスのコンストラクタ本体から基本クラスのコンストラクタを呼び出す を参照してください。ポイントは、派生クラスの本体で親クラスまたはスーパークラスのコンストラクタへの明示的な呼び出しを使用する場合、実際には単に親クラスのインスタンスを作成することであり、派生オブジェクトの親クラスのコンストラクタを呼び出すことではありません。 。派生クラスのオブジェクトで親クラスまたはスーパークラスのコンストラクタを呼び出す唯一の方法は、初期化リストを使用することであり、派生クラスのコンストラクタ本体にはありません。したがって、「スーパークラスコンストラクター呼び出し」と呼ばれるべきではありません。誰かが(私がしたように)混乱するかもしれないので、私はこの答えをここに置いた。

20
TT_

引数のないコンストラクタがある場合は、派生クラスのコンストラクタが実行される前に呼び出されます。

引数を付けて基本コンストラクタを呼び出したい場合は、次のように派生コンストラクタに明示的に記述する必要があります。

class base
{
  public:
  base (int arg)
  {
  }
};

class derived : public base
{
  public:
  derived () : base (number)
  {
  }
};

C++でparentコンストラクタを呼び出さずに派生クラスを構築することはできません。それが引数でないC'torの場合は自動的に行われます。上記のように派生コンストラクタを直接呼び出すとコードがコンパイルされない場合に起こります。

19

親コンストラクタに値を渡す唯一の方法は、初期化リストを使用することです。初期化リストは:で実装され、次にクラスのリストとそのクラスコンストラクタに渡される値が実装されます。

Class2::Class2(string id) : Class1(id) {
....
}

また、親クラスでパラメータを受け取らないコンストラクタがある場合、子コンストラクタが実行される前に自動的に呼び出されることにも注意してください。

19
CR.

基本コンストラクタにデフォルトパラメータがある場合は、基本クラスが自動的に呼び出されます。

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

11
edW
CDerived::CDerived()
: CBase(...), iCount(0)  //this is the initialisation list. You can initialise member variables here too. (e.g. iCount := 0)
    {
    //construct body
    }
9
Dynite

クラスが複数のクラスから派生する場合のコンストラクタ呼び出しの順序については、誰も言及しませんでした。順序は、クラスを派生させるときに説明したとおりです。

6
darth_coder