589

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

たとえば、Javaでは、サブクラスコンストラクタの最初の行として実行する必要があります(そうでない場合は、引数なしのスーパーコンストラクタへの暗黙の呼び出しが想定されます。欠落している場合はコンパイルエラーが発生します)。 。


  • ただの一言:「スーパークラス」はありません。 C ++では、実際、標準ではまったく言及されていません。この表現は(おそらく)Javaに由来します。 「基本クラス」を使用してください。 C ++では。私はそれを推測スーパーC ++は多重継承を許可していますが、単一の親を意味します。 - andreee

9 답변


801

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

コンストラクタの初期化リストの詳細ここにそしてここに


  • 明示的な'を削除しましたSuperClassコンストラクタから。引数が1つのコンストラクタにとってはベストプラクティスであるにもかかわらず、今の議論とは密接な関係がありませんでした。明示的なキーワードの詳細については、以下を参照してください。weblogs.asp.net/kennykerr/archive/2004/08/31/… - luke
  • 子クラスのコンストラクタをインスタンス化する前にスーパークラスのコンストラクタを呼び出すために使用したコロン:演算子。これはメソッドにも当てはまると思いますか。 - ha9u63ar
  • @hagubear、コンストラクタにのみ有効、AFAIK - luke
  • SubClassオブジェクトを次のようにインスタンス化すると、SubClass anObject(1,2)します、1に渡されるSuperClass(foo)(paramaterの議論になるfoo)?私はドキュメントの高値と安値を検索してきましたが、SubClassコンストラクタへの引数をSuperClassコンストラクタへの引数として渡すことができるとは明確に述べていません。 - LazerSharks
  • @Gnuey、: SuperClass(foo)部分。fooはスーパークラスのコンストラクタに明示的に渡されています。 - luke

206

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ブロックが別の例外をスローしない場合、ランタイムは元のエラーを再スローします。初期化中の例外できない無視してください。


  • 2番目の例の構文が理解できないのですが... try / catchはコンストラクタ本体の代わりになりますか。 - levik
  • はい。セクションを書き換えて、間違いを修正しました(tryキーワードは初期化リストの前にあります)。メモリから書き込むのではなく、検索する必要があります。よく使用されるものではありません。 - puetzk
  • イニシャライザにtry / catch構文を含めてくれてありがとう。私はC ++を10年間使用してきましたが、これが初めてのことです。 - jakar
  • 私は長い間C ++を使ってきたことを認めざるを得なくなりました、そしてそれが私がコンストラクタリストで/ catcnを試みるのを初めて見たのです。 - Cameron

45

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

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


  • ちょっと待ってください...イニシャライザは割り当てのコストを節約すると言います。しかし、電話がかかってきても同じ割り当てが行われることはありませんか。 - levik
  • いや。初期化と割り当ては別物です。コンストラクターが呼び出されると、デフォルト値と思われるものですべてのデータメンバーを初期化しようとします。 initリストではデフォルト値を指定するようになっています。どちらの場合も初期化コストがかかります。 - Dima
  • そして、あなたが身体の中で代入を使うならば、あなたはとにかく初期化コストとそれに加えて代入のコストを被る。 - Dima
  • この回答は、ヘッダーとソースファイルがあり、ヘッダーに初期化リストが必要ない構文のバリエーションを示しているので便利でした。大変参考になりました。 - Benjamin
  • 残念、私はこれに対してただ1つの支持を与えることができます!優秀な答え - Rajesh

18

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


  • この答えは、私が何度か読み終えて質問にリンクされている部分を見ても、やや混乱します。私が言っているのは、派生クラスの本体で親クラスまたはスーパークラスのコンストラクタへの明示的な呼び出しを使用する場合、これは実際には親クラスのインスタンスを作成することであり、親クラスを呼び出さないことです。派生オブジェクトのコンストラクタ。派生クラスで親クラスまたはスーパークラスのコンストラクタを呼び出す唯一の方法objectは初期化リストを介しており、派生クラスのコンストラクタ本体にはありません。 - Richard Chambers
  • @Richard Chambers英語は私の第一言語ではないので混乱するかもしれませんが、あなたは私が言おうとしたことを正確に説明しました。 - TT_

17

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

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

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


17

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

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

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

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

C ++でparentコンストラクタを呼び出さずに派生クラスを構築することはできません。それが引数でないC'torであるなら、それは自動的に起こる、それはあなたが上で示されるように直接派生コンストラクタを呼ぶか、またはあなたのコードがコンパイルしないならば起こる。


7

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


9

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

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

4

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


  • 誰もそれについて話していないならば、それはどこで言及されましたか? - user207421
  • 質問はルールの呼び出しに関するものなので、@ EJPは答えの中で呼び出しの順序を述べる価値があります。 - darth_coder

リンクされた質問


関連する質問

最近の質問