シャロウコピー(浅いコピー)は、クラスメンバーの値をコピーするだけであり、ポインタを含むが、これらのポインタのデータをコピーしません。ディープコピー(深いコピー)は、ポインタによって指し示されるデータの新しいコピーを作成し、メモリの共有所有と二重解放を防ぎます。
例:
class Buffer { public: Buffer(size_t size) : size(size), data(new int[size]) {} // シャロウコピー(間違いです) Buffer(const Buffer& other) : size(other.size), data(other.data) {} // ディープコピー(正しいです) Buffer& operator=(const Buffer& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; std::copy(other.data, other.data + size, data); } return *this; } ~Buffer() { delete[] data; } private: size_t size; int* data; };
クラスが動的配列へのポインタを含む場合、なぜデフォルトのコピーコンストラクタとoperator=をコピーペーストするだけでは不十分ですか?
正しい答え:
デフォルトのコピーは浅いコピーを実装しています。2つのオブジェクトが同じ配列を指し示し、破棄時に同じメモリを2回解放しようとすると(ダブルフリー)、クラッシュが発生します。
物語 大規模なサーバーアプリケーションでは、ピクセルの動的バッファを管理する独自のImageクラスが実装されました。開発者は深いコピーを実装せず、スレッド間でImageオブジェクトを値で渡すとメモリの二重解放が発生し、数日に一度の頻度でクラッシュが発生しました。
物語 学生プロジェクトでは、動的に割り当てられたセルの配列へのポインタを含むFieldコンテナが使用されました。配列のサイズを変更する際にコピーコンストラクタはポインタのみをコピーし、両方のコンテナが同じメモリ領域を操作することになり、データの不整合と発見困難なバグを引き起こしました。
物語 古いC++コードのグラフィックスエンジンで、独自のデストラクタと代入演算子なしでTextureクラスが実装され、コピーと一時オブジェクトの代入時にメモリリークが発生しました。