Shallow copy(浅いコピー) – オブジェクトのフィールドの値、ポインタをコピーしますが、動的に確保されたメモリは複製しません。その結果、両方のオブジェクトは同じメモリアドレスを指します。データの変更やメモリの解放時にエラーを引き起こす可能性があります。
Deep copy(深いコピー) – オブジェクトが参照するすべてのリソースの完全なコピーを作成し、各オブジェクトが自分のデータのコピーを持つようにします。
Deep copyが必要なとき:
Deep copyの実装例:
class ArrayHolder { public: ArrayHolder(size_t size) : sz(size), data(new int[size]) {} ~ArrayHolder() { delete[] data; } // 深いコピーコンストラクタ ArrayHolder(const ArrayHolder& other) : sz(other.sz), data(new int[other.sz]) { std::copy(other.data, other.data + other.sz, data); } // 深いコピー代入演算子 ArrayHolder& operator=(const ArrayHolder& other) { if (this != &other) { delete[] data; sz = other.sz; data = new int[sz]; std::copy(other.data, other.data + sz, data); } return *this; } private: size_t sz; int* data; };
質問: ポインタの値を単にコピーするだけのコピーコンストラクタは正しく機能しますか?
答え: いいえ、これは両方のオブジェクトの破壊時に二重解放を引き起こします。リソースを適切に管理するためには、深いコピーを実装する必要があります。
物語
プロジェクトの1つでは、モジュール間で配列を渡すために、デフォルトのコピーコンストラクタがポインタのみをコピーするクラスのオブジェクトが使用されました(shallow copy)。その結果、一時オブジェクトがスコープを終了すると、配列は破壊され、メインオブジェクトは「ぶら下がった」ポインタを持ち、未定義の動作とアプリのクラッシュを引き起こしました。
物語
画像処理ライブラリでは、動的に割り当てられたバッファを含むオブジェクトを繰り返しコピーしていました。deep copyがなかったため、一方のオブジェクトのメモリが解放された後、もう一方は解放された領域にアクセスしていました。データの破損やオブジェクトの削除時のクラッシュが発生しました。
物語
旧式のコードを現代の標準に統合する際、古いクラスにコピーコンストラクタも代入演算子も実装されていませんでした。これらのクラスを標準ライブラリのコンテナで使用すると、コンテナがオブジェクトを浅くコピーするため、予期しないクラッシュが発生しました。