他のオブジェクトを含むクラスのC ++暗黙的コピーコンストラクター
-
05-07-2019 - |
質問
自分で実装しないと、コンパイラがデフォルトのコピーコンストラクターを提供する場合があることを知っています。私はこのコンストラクターが正確に何をするかについて混乱しています。宣言されたコピーコンストラクターを持たない他のオブジェクトを含むクラスがある場合、動作はどうなりますか?たとえば、次のようなクラス:
class Foo {
Bar bar;
};
class Bar {
int i;
Baz baz;
};
class Baz {
int j;
};
今これを行うと:
Foo f1;
Foo f2(f1);
デフォルトのコピーコンストラクターは何をしますか? Foo
のコンパイラ生成のコピーコンストラクタは、 Bar
のコンパイラ生成のコンストラクタを呼び出して bar
をコピーします。 Baz
でコピーコンストラクタを生成しますか?
解決
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
- any コンストラクター(コピーを含む)が宣言されている場合、デフォルトのコンストラクターはコンパイラーによって実装されません。
- コピーコンストラクタが宣言されている場合、コンパイラは生成しません。
- 代入演算子が宣言されている場合、コンパイラは生成しません。
- デストラクタが宣言されている場合、コンパイラはデストラクタを生成しません。
コードを見ると、次のコピーコンストラクターが生成されます:
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)
{}
他のヒント
自分で宣言(注:定義ではない)しない限り、コンパイラはコピーコンストラクタを提供します。コンパイラによって生成されたコピーコンストラクターは、クラスの各メンバー(および各基本クラス)のコピーコンストラクターを呼び出すだけです。
代入演算子とデストラクタ(BTW)についてもまったく同じことが言えます。ただし、デフォルトのコンストラクターとは異なります。他のコンストラクターを自分で宣言しない場合にのみ、コンパイラーによって提供されます。
はい、コンパイラが生成したコピーコンストラクターは、包含クラスでメンバーが宣言されている順序で、メンバーごとのコピーを実行します。いずれかのメンバータイプ自体がコピーコンストラクターを提供しない場合、含まれるクラスのコピーコンストラクターを生成できません。おそらく、他のコンストラクタのいずれかを使用して、コピー構築できないメンバーの値を初期化するための適切な手段を決定できる場合、手動で作成することも可能です。
C ++ デフォルトコピーコンストラクタは、浅いコピー。浅いコピーでは、元のオブジェクトが参照する可能性のあるオブジェクトの新しいコピーは作成されません。古いオブジェクトと新しいオブジェクトには、同じメモリ位置への個別のポインタが含まれているだけです。
コンパイラは必要なコンストラクタを生成します。
ただし、コピーコンストラクタを自分で定義するとすぐに、コンパイラはそのクラスの生成を断念し、適切なコンストラクタが定義されていない場合はエラーとエラーを返します。
例を使用:
class Baz {
Baz(const Baz& b) {}
int j;
};
class Bar {
int i;
Baz baz;
};
class Foo {
Bar bar;
};
デフォルトのインスタンス化またはコピー構築Fooを試行すると、Bazはコピー構築可能ではなく、コンパイラはデフォルトを生成できず、Fooのコンストラクタをコピーできないため、エラーがスローされます。