web-dev-qa-db-ja.com

他のオブジェクトを含むクラスのC ++暗黙的コピーコンストラクター

自分で実装しないと、コンパイラがデフォルトのコピーコンストラクタを提供する場合があることを知っています。私はこのコンストラクターが正確に何をするかについて混乱しています。宣言されたコピーコンストラクターを持たない他のオブジェクトを含むクラスがある場合、動作はどうなりますか?たとえば、次のようなクラス:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

今私がこれを行うと:

Foo f1;
Foo f2(f1);

デフォルトのコピーコンストラクタは何をしますか? Fooのコンパイラー生成コピーコンストラクターは、Barのコンパイラー生成コンストラクターを呼び出してbarをコピーし、Bazでコンパイラー生成コピーコンストラクターを呼び出しますか$ var] _?

47
jergason
Foo f1;
Foo f2(f1);

はい、これはあなたが期待することをします:
f2コピーコンストラクターFoo :: Foo(Foo const&)が呼び出されます。
このコピーは、基本クラスを構築し、次に各メンバーを(再帰的に)構築します

このようなクラスを定義する場合:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

次のメソッドは、コンパイラによって定義されます。

  • コンストラクター(デフォルト)(2バージョン)
  • コンストラクター(コピー)
  • デストラクタ(デフォルト)
  • 代入演算子

コンストラクター:デフォルト:

実際には2つのデフォルトコンストラクターがあります。
1つはzero-initialization他方はvalue-initialization。使用されるかどうかは、()初期化中かどうか。

// Zero-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
    ,m_a(0)             // Default construction of basic PODS zeros them
    ,m_b(0)             // 
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
{
}

注:基本クラスまたはメンバーに有効な表示可能なデフォルトコンストラクターがない場合、デフォルトコンストラクターを生成できません。コードがデフォルトのコンストラクタを使用しようとしない限り、これはエラーではありません(コンパイル時エラーのみ)。

コンストラクター(コピー)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

注:基本クラスまたはメンバーに有効な表示可能なコピーコンストラクターがない場合、コピーコンストラクターを生成できません。コードがコピーコンストラクターを使用しようとしない限り、これはエラーではありません(コンパイル時エラーのみ)。

割り当て演算子

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

注:基本クラスまたはメンバーに有効な実行可能な代入演算子がない場合、代入演算子を生成できません。コードが代入演算子を使用しようとしない限り、これはエラーではありません(コンパイル時エラーのみ)。

デストラクタ

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • any constructor(コピーを含む)が宣言されている場合、デフォルトのコンストラクターはコンパイラーによって実装されません。
  • コピーコンストラクタが宣言されている場合、コンパイラは生成しません。
  • 代入演算子が宣言されている場合、コンパイラは生成しません。
  • デストラクタが宣言されている場合、コンパイラはデストラクタを生成しません。

コードを見ると、次のコピーコンストラクターが生成されます。

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}
68
Martin York

コンパイラは、自分でdeclare(注:defineではない)でない限り、コピーコンストラクタを提供します。コンパイラが生成したコピーコンストラクターは、クラスの各メンバー(および各基本クラス)のコピーコンストラクターを呼び出すだけです。

割り当て演算子とデストラクタ(BTW)についてもまったく同じことが言えます。ただし、デフォルトのコンストラクターとは異なります。他のコンストラクターを自分で宣言しない場合にのみ、コンパイラーによって提供されます。

11
sbi

はい。コンパイラが生成したコピーコンストラクターは、メンバーを含むクラスでメンバーが宣言されている順序で、メンバーごとのコピーを実行します。いずれかのメンバータイプ自体がコピーコンストラクターを提供しない場合、含まれるクラスのコピーコンストラクターを生成できません。おそらく、他のコンストラクターの1つを使用して、コピー構築できないメンバーの値を初期化するための適切な手段を決定できる場合、手動で1つを記述することも可能です。

2
seh

C++ デフォルトのコピーコンストラクター は、 shallow コピーを作成します。浅いコピーでは、元のオブジェクトが参照する可能性のあるオブジェクトの新しいコピーは作成されません。古いオブジェクトと新しいオブジェクトには、同じメモリ位置への個別のポインタが含まれるだけです。

1
Phil

コンパイラーが必要なコンストラクターを生成します。

ただし、コピーコンストラクターを自分で定義するとすぐに、コンパイラーはそのクラスに対して何かを生成することを断念し、適切なコンストラクターが定義されていない場合はエラーを出します。

あなたの例を使用して:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Bazはコピー構築可能ではなく、コンパイラはデフォルトを生成できず、Fooのコンストラクタをコピーできないため、Fooをデフォルトのインスタンス化またはコピー構築しようとするとエラーがスローされます。

0
Coincoin