C++ コンパイラがクラスのコピー コンストラクタを作成することは知っています。どのような場合に、ユーザー定義のコピー コンストラクターを作成する必要がありますか? いくつか例を挙げていただけますか?
7 に答える
コンパイラーによって生成されたコピーコンストラクターは、メンバーごとのコピーを行います。時にはそれだけでは不十分です。例えば:
class Class {
public:
Class( const char* str );
~Class();
private:
char* stored;
};
Class::Class( const char* str )
{
stored = new char[srtlen( str ) + 1 ];
strcpy( stored, str );
}
Class::~Class()
{
delete[] stored;
}
この場合、メンバーごとのstored
メンバーのコピーはバッファーを複製しないため(ポインターのみがコピーされます)、バッファーを共有する最初の破棄されたコピーはdelete[]
正常に呼び出され、2番目は未定義の動作に遭遇します。ディープコピーコピーコンストラクター(および代入演算子)が必要です。
Class::Class( const Class& another )
{
stored = new char[strlen(another.stored) + 1];
strcpy( stored, another.stored );
}
void Class::operator = ( const Class& another )
{
char* temp = new char[strlen(another.stored) + 1];
strcpy( temp, another.stored);
delete[] stored;
stored = temp;
}
Rule of Five
のルールが引用されていないことに少し腹を立てています。
このルールは非常に単純です。
5 つのルール:
デストラクタ、コピー コンストラクタ、コピー代入演算子、ムーブ コンストラクタ、またはムーブ代入演算子のいずれかを記述する場合は、他の 4 つを記述する必要があります。
ただし、例外セーフなコードを記述する必要性から派生した、従うべきより一般的なガイドラインがあります。
各リソースは専用のオブジェクトで管理する必要があります
ここで@sharptooth
のコードは (ほとんど) 問題ありませんが、クラスに 2 番目の属性を追加すると、問題が発生します。次のクラスを検討してください。
class Erroneous
{
public:
Erroneous();
// ... others
private:
Foo* mFoo;
Bar* mBar;
};
Erroneous::Erroneous(): mFoo(new Foo()), mBar(new Bar()) {}
new Bar
スローするとどうなりますか?が指すオブジェクトをどのように削除しますmFoo
か? 解決策(関数レベルのtry / catch ...)がありますが、スケーリングしません。
この状況に対処する適切な方法は、生のポインターの代わりに適切なクラスを使用することです。
class Righteous
{
public:
private:
std::unique_ptr<Foo> mFoo;
std::unique_ptr<Bar> mBar;
};
同じコンストラクターの実装 (または実際には を使用make_unique
) を使用すると、無料で例外の安全性が得られます!!! わくわくしませんか?そして何よりも、適切なデストラクタについて心配する必要がなくなりました! 私は自分自身を書く必要がありCopy Constructor
ますが、これらの操作を定義していないため...しかし、ここでは問題ではありません; Assignment Operator
)unique_ptr
したがって、sharptooth
のクラスを再訪します。
class Class
{
public:
Class(char const* str): mData(str) {}
private:
std::string mData;
};
あなたのことはわかりませんが、私のほうが簡単だと思います;)
コンテンツを動的に割り当てたクラスがある場合。たとえば、本のタイトルをchar *として保存し、タイトルをnewに設定すると、コピーは機能しません。
その後、を実行するコピーコンストラクターを作成する必要がtitle = new char[length+1]
ありstrcpy(title, titleIn)
ます。コピーコンストラクタは、「浅い」コピーを実行するだけです。
コピーコンストラクタは、オブジェクトが値によって渡されるか、値によって返されるか、または明示的にコピーされるときに呼び出されます。コピーコンストラクターがない場合、c++は浅いコピーを作成するデフォルトのコピーコンストラクターを作成します。オブジェクトに動的に割り当てられたメモリへのポインタがない場合は、シャローコピーで十分です。
クラスで特に必要でない限り、copy ctor と operator= を無効にすることをお勧めします。これにより、参照が意図されている場合に引数を値で渡すなどの非効率性を防ぐことができます。また、コンパイラによって生成されたメソッドが無効である可能性があります。
以下のコード スニペットを考えてみましょう。
class base{
int a, *p;
public:
base(){
p = new int;
}
void SetData(int, int);
void ShowData();
base(const base& old_ref){
//No coding present.
}
};
void base :: ShowData(){
cout<<this->a<<" "<<*(this->p)<<endl;
}
void base :: SetData(int a, int b){
this->a = a;
*(this->p) = b;
}
int main(void)
{
base b1;
b1.SetData(2, 3);
b1.ShowData();
base b2 = b1; //!! Copy constructor called.
b2.ShowData();
return 0;
}
Output:
2 3 //b1.ShowData();
1996774332 1205913761 //b2.ShowData();
b2.ShowData();
データを明示的にコピーするためのコードを書かずに作成されたユーザー定義のコピー コンストラクターがあるため、ジャンク出力が得られます。したがって、コンパイラは同じものを作成しません。
ほとんどの人はすでに知っていますが、この知識を皆さんと共有することを考えました.
乾杯...ハッピーコーディング!!!