web-dev-qa-db-ja.com

C++で他のコンストラクタからコンストラクタを呼び出すことはできますか(コンストラクタチェーン)。

C# 開発者として、私はコンストラクタを実行するのに慣れています。

class Test {
    public Test() {
        DoSomething();
    }

    public Test(int count) : this() {
        DoSomethingWithCount(count);
    }

    public Test(int count, string name) : this(count) {
        DoSomethingWithName(name);
    }
}

C + +でこれを行う方法はありますか?

クラス名を呼び出して「this」キーワードを使用しようとしましたが、どちらも失敗しました。

833
Stormenet

C++ 11:はい。

C++ 11以降もこれと同じ機能を持ちます( 委任コンストラクタ と呼ばれます)。

構文はC#とは若干異なります。

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {}
};

C++ 03:いいえ

残念ながら、これをC++ 03で行う方法はありませんが、これをシミュレートする方法は2つあります。

  1. デフォルトのパラメータを使って2つ(またはそれ以上)のコンストラクタを組み合わせることができます。

    class Foo {
    public:
      Foo(char x, int y=0);  // combines two constructors (char) and (char, int)
      // ...
    };
    
  2. Initメソッドを使用して共通のコードを共有します。

    class Foo {
    public:
      Foo(char x);
      Foo(char x, int y);
      // ...
    private:
      void init(char x, int y);
    };
    
    Foo::Foo(char x)
    {
      init(x, int(x) + 7);
      // ...
    }
    
    Foo::Foo(char x, int y)
    {
      init(x, y);
      // ...
    }
    
    void Foo::init(char x, int y)
    {
      // ...
    }
    

C++ FAQエントリ を参照してください。

1118
JohnIdol

いいえ、C++ 03では1つのコンストラクタを別のコンストラクタから呼び出すことはできません(デリゲートコンストラクタと呼ばれます)。

これはC++ 11(別名C++ 0x)で変更され、次の構文のサポートが追加されました。
ウィキペディア からの例)

class SomeType
{
  int number;

public:
  SomeType(int newNumber) : number(newNumber) {}
  SomeType() : SomeType(42) {}
};
107
Cyrille Ka

コンストラクタからコンストラクタを呼び出すことができると思います。コンパイルして実行します。私は最近誰かがこれをやっているのを見ました、そしてそれはWindowsとLinuxの両方で動きました。

それはあなたが望むことをしないだけです。内側のコンストラクタは、外側のコンストラクタが戻ったときに削除される一時的なローカルオブジェクトを作成します。それらは異なるコンストラクタでなければならないでしょうか、またはあなたは再帰的な呼び出しを作成するでしょう。

参照: https://isocpp.org/wiki/faq/ctors#init-methods

40
ohlemacher

あなたが can あなたのコンストラクタの中で親クラスのコンストラクタを呼び出すことができることを指摘する価値があります。

class A { /* ... */ };

class B : public A
{
    B() : A()
    {
        // ...
    }
};

しかし、いいえ、あなたは同じクラスの別のコンストラクタを呼び出すことはできません。

21
kchoose2

C++ 11 では、 コンストラクタは別のコンストラクタをオーバーロードできます

class Foo  {
     int d;         
public:
    Foo  (int i) : d(i) {}
    Foo  () : Foo(42) {} //New to C++11
};

さらに、メンバーもこのように初期化できます。

class Foo  {
     int d = 5;         
public:
    Foo  (int i) : d(i) {}
};

これにより、初期化ヘルパーメソッドを作成する必要がなくなります。初期化されていない可能性のあるメンバを使用しないようにするために、コンストラクタまたはデストラクタで仮想関数を呼び出さないことをお勧めします。

18
Ben L

あなたが悪になりたいのであれば、インプレースの "new"演算子を使うことができます。

class Foo() {
    Foo() { /* default constructor deliciousness */ }
    Foo(Bar myParam) {
      new (this) Foo();
      /* bar your param all night long */
    } 
};

私のために働くようだ。

編集

@ElvedinHamzagicが指摘するように、Fooがメモリを割り当てたオブジェクトを含んでいた場合、そのオブジェクトは解放されないかもしれません。これは事態をさらに複雑にします。

より一般的な例:

class Foo() {
private:
  std::vector<int> Stuff;
public:
    Foo()
      : Stuff(42)
    {
      /* default constructor deliciousness */
    }

    Foo(Bar myParam)
    {
      this->~Foo();
      new (this) Foo();
      /* bar your param all night long */
    } 
};

確かに、もう少しエレガントではありません。 @ JohnIdolの解決策ははるかに優れています。

12
lyngvi

いいえ、C++ではコンストラクタからコンストラクタを呼び出すことはできません。ウォーレンが指摘したように、あなたができることは:

  • 異なるシグネチャを使用してコンストラクタをオーバーロードします
  • 「より単純な」バージョンを利用可能にするために、引数にデフォルト値を使用する

前者の場合、あるコンストラクタを別のコンストラクタから呼び出すことによってコードの重複を減らすことはできません。もちろん、すべての初期化を行う別のprivate/protectedメソッドを用意して、コンストラクタに主に引数処理を任せることもできます。

8
unwind

Visual C++では、コンストラクター内でこの表記を使用することもできます。this-> Classname :: Classname(別のコンストラクターのパラメーター)。下記の例をご覧ください。

class Vertex
{
 private:
  int x, y;
 public:
  Vertex(int xCoo, int yCoo): x(xCoo), y(yCoo) {}
  Vertex()
  {
   this->Vertex::Vertex(-1, -1);
  }
};

それがどこか他で動くかどうかはわかりません、私はそれをVisual C++ 2003と2008でテストしただけです。あなたは複数のコンストラクタをこの方法で呼ぶこともできます。

P。S:率直に言って、これが以前に言及されていないことに驚きました。

5
izogfif

簡単に言えば、C++ 11より前にすることはできません。

C++ 11では デリゲートコンストラクター が導入されました。

委任コンストラクター

クラス自体の名前がメンバー初期化子リストにクラスまたは識別子として現れる場合、リストはその1つのメンバー初期化子だけで構成されている必要があります。このようなコンストラクタは委任コンストラクタとして知られており、初期化子リストの唯一のメンバによって選択されたコンストラクタがターゲットコンストラクタです。

この場合、ターゲットコンストラクタはオーバーロード解決によって選択されて最初に実行され、次に制御は委任コンストラクタに戻り、その本体が実行されます。

委任コンストラクターは再帰的にはできません。

class Foo {
public: 
  Foo(char x, int y) {}
  Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int)
};

委任コンストラクタは、オールオアナッシングの提案です。コンストラクターが別のコンストラクターに委任した場合、呼び出し元のコンストラクターは初期化リストに他のメンバーを含めることはできません。これは、const/referenceメンバを一度だけ、そして一度だけ初期化することを考えれば意味があります。

3
user8683714

まだ表示されていないもう一つのオプションはあなたが探している効果を達成するためにあなたの元のクラスの周りに軽量のインターフェースクラスをラップすることであなたのクラスを二つに分割することです:

class Test_Base {
    public Test_Base() {
        DoSomething();
    }
};

class Test : public Test_Base {
    public Test() : Test_Base() {
    }

    public Test(int count) : Test_Base() {
        DoSomethingWithCount(count);
    }
};

「次のレベルアップ」のカウンターパートと呼ばなければならないコンストラクタがたくさんある場合、これは面倒になるかもしれませんが、ほんの一握りのコンストラクタでは、それは実行可能であるべきです。

3
e.James

私はコンストラクタのアプリケーションロジックを実装し、さまざまなコンストラクタによって呼び出されるprivate friendメソッドの使用を提案します。これが一例です。

プライベートフィールドを含むStreamArrayReaderというクラスがあるとします。

private:
    istream * in;
      // More private fields

そして2つのコンストラクタを定義します。

public:
    StreamArrayReader(istream * in_stream);
    StreamArrayReader(char * filepath);
    // More constructors...

2番目のものが単に最初のものを利用するところでは(そしてもちろん我々は前者の実装を複製したくありません)。理想的には、次のようなことをしたいのです。

StreamArrayReader::StreamArrayReader(istream * in_stream){
    // Implementation
}

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    StreamArrayReader(&instream);
    instream.close();
}

ただし、これはC++では許可されていません。そのため、最初のコンストラクタが行うことを想定しているものを実装する、プライベートのフレンドメソッドを次のように定義することができます。

private:
  friend void init_stream_array_reader(StreamArrayReader *o, istream * is);

このメソッドは(友人だから)oのプライベートフィールドにアクセスできます。次に、最初のコンストラクタは次のようになります。

StreamArrayReader::StreamArrayReader(istream * is) {
    init_stream_array_reader(this, is);
}

これによって、新しく作成されたコピーに対して複数のコピーが作成されることはありません。 2番目のものは次のようになります。

StreamArrayReader::StreamArrayReader(char * filepath) {
    ifstream instream;
    instream.open(filepath);
    init_stream_array_reader(this, &instream);
    instream.close();
}

つまり、 あるコンストラクタが別のコンストラクタを呼び出すのではなく、どちらもプライベートの友達を呼び出すということです。

2

この方法はある種のクラスではうまくいくかもしれません(代入演算子が 'うまく'振舞うとき):

Foo::Foo()
{
    // do what every Foo is needing
    ...
}

Foo::Foo(char x)
{
    *this = Foo();

    // do the special things for a Foo with char
    ...
}
2
V15I0N

私があなたの質問を正しく理解したならば、あなたはあなたがC++で複数のコンストラクタを呼び出せるかどうかを尋ねていますか?

それがあなたが探しているものなら、それからいいえ - それは不可能です。

確かに、それぞれが一意の引数シグネチャを持つ複数のコンストラクタを持つことができ、その後、新しいオブジェクトをインスタンス化するときに必要なコンストラクタを呼び出すことができます。

最後にデフォルトの引数を持つ1つのコンストラクタを持つことさえできます。

しかし、あなたは複数のコンストラクタを持っていないかもしれません、そしてそれからそれらのそれぞれを別々に呼び出します。

1
warren

コンストラクターを呼び出すとき、スタックまたはヒープから実際にメモリーを割り当てます。そのため、別のコンストラクタでコンストラクタを呼び出すと、ローカルコピーが作成されます。だから私たちは焦点を当てているものではなく、別のオブジェクトを修正しています。

1
XPD

:)決めるよりもテストするほうが簡単でしょう。

#include <iostream>

class A {
public:
    A( int a) : m_a(a) {
        std::cout << "A::Ctor" << std::endl;    
    }
    ~A() {
        std::cout << "A::dtor" << std::endl;    
    }
public:
    int m_a;
};

class B : public A {
public:
    B( int a, int b) : m_b(b), A(a) {}
public:
    int m_b;
};

int main() {
    B b(9, 6);
    std::cout << "Test constructor delegation a = " << b.m_a << "; b = " << b.m_b << std::endl;    
    return 0;
}

そして、98 stdでコンパイルします。g ++ main.cpp -std = c ++ 98 -o test_1

あなたは見るでしょう:

A::Ctor
Test constructor delegation a = 9; b = 6
A::dtor

そう :)

0
gyula