C++でのオブジェクトのコピーについて話しましょう。
Test t;
は、整数の新しい配列を割り当てるデフォルトのコンストラクターを呼び出します。これは問題ありません、そしてあなたの期待される振る舞い。
t
を使用してキューにプッシュすると、問題が発生しますq.push(t)
。Java、C#、またはその他のほとんどすべてのオブジェクト指向言語に精通している場合は、以前に作成したオブジェクトがキューに追加されることを期待できますが、C++はそのようには機能しません。
std::queue::push
メソッドを見ると、キューに追加される要素が「xのコピーに初期化されている」ことがわかります。Test
これは実際には、コピーコンストラクターを使用して元のオブジェクトのすべてのメンバーを複製して新しいを作成するまったく新しいオブジェクトですTest
。
C ++コンパイラは、デフォルトでコピーコンストラクタを生成します。これは非常に便利ですが、ポインタメンバーに問題が発生します。int *myArray
あなたの例では、それは単なるメモリアドレスであることを忘れないでください。の値がmyArray
古いオブジェクトから新しいオブジェクトにコピーされると、メモリ内の同じ配列を指す2つのオブジェクトが作成されます。これは本質的に悪いことではありませんが、デストラクタは同じアレイを2回削除しようとするため、「ダブルフリーまたは破損」ランタイムエラーが発生します。
どうすれば修正できますか?
最初のステップは、あるオブジェクトから別のオブジェクトにデータを安全にコピーできるコピーコンストラクターを実装することです。簡単にするために、次のようになります。
Test(const Test& other){
myArray = new int[10];
memcpy( myArray, other.myArray, 10 );
}
これで、テストオブジェクトをコピーするときに、新しい配列が新しいオブジェクトに割り当てられ、配列の値もコピーされます。
しかし、私たちはまだ完全に問題を抱えているわけではありません。同様の問題を引き起こす可能性のあるコンパイラが生成する別の方法があります-割り当て。違いは、割り当てでは、メモリを適切に管理する必要がある既存のオブジェクトがすでに存在することです。基本的な代入演算子の実装は次のとおりです。
Test& operator= (const Test& other){
if (this != &other) {
memcpy( myArray, other.myArray, 10 );
}
return *this;
}
ここで重要なのは、他の配列からこのオブジェクトの配列にデータをコピーし、各オブジェクトのメモリを分離しておくことです。自己割り当てのチェックもあります。そうしないと、自分自身から自分自身にコピーすることになり、エラーがスローされる可能性があります(何をすべきかわからない)。より多くのメモリを削除して割り当てていた場合、自己割り当てチェックにより、コピーする必要のあるメモリを削除できなくなります。