60

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

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

今私がこれを行うと:

Foo f1;
Foo f2(f1);

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

4

5 に答える 5

83
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、もう 1 つは に使用され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
  • コンストラクター (コピーを含む) が宣言されている場合、既定のコンストラクターはコンパイラーによって実装されません。
  • コピー コンストラクターが宣言されている場合、コンパイラはそれを生成しません。
  • 代入演算子が宣言されている場合、コンパイラは代入演算子を生成しません。
  • デストラクタが宣言されている場合、コンパイラはデストラクタを生成しません。

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

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)
{}
于 2009-11-27T19:26:20.707 に答える
13

自分で宣言しない限り(注:定義しないでください) 、コンパイラはコピー コンストラクターを提供します。コンパイラによって生成されたコピー コンストラクターは、クラス (および各基底クラス) の各メンバーのコピー コンストラクターを呼び出すだけです。

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

于 2009-11-27T18:45:08.417 に答える
2

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

于 2009-11-27T18:47:04.387 に答える
1

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

于 2009-11-27T18:58:32.790 に答える
0

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

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

あなたの例を使用して:

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

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

于 2009-11-27T18:47:24.077 に答える