三の法則: クラスがリソース(例えばメモリ)を管理する場合、次のいずれかのメソッドを明示的に実装する場合、残りも実装する必要があります:
C++11: 五の法則 – 移動操作が追加されました:
このルールを破ると、ダブル削除やメモリリークなどのリソース管理のエラーが発生する可能性があります。
class Buffer { char* data; public: Buffer(size_t sz) : data(new char[sz]) {} ~Buffer() { delete[] data; } Buffer(const Buffer& other) : data(new char[strlen(other.data)+1]) { strcpy(data, other.data); } Buffer& operator=(const Buffer& other) { if (&other != this) { delete[] data; data = new char[strlen(other.data)+1]; strcpy(data, other.data); } return *this; } // C++11以降のムーブセマンティクス: Buffer(Buffer&& other) noexcept : data(other.data) { other.data = nullptr; } Buffer& operator=(Buffer&& other) noexcept { if (&other != this) { delete[] data; data = other.data; other.data = nullptr; } return *this; } };
クラスがポインタを管理する場合、デストラクタだけを実装すれば十分ですか?
いいえ。コピーと移動の操作がない場合、コピー時にメモリの二重削除が発生します。例:
Buffer a(10); Buffer b = a; // bとaは同じポインタを削除します!
ストーリー
通信データ集約プラットフォームでは、すべてのクラスでデストラクタのみを実装していました。構造のリファクタリング後、ダブルフリーによる大規模なクラッシュが発生しました: オブジェクトのコピーがランダムな動作を引き起こしました。
ストーリー
モバイルゲームプロジェクトでは、バッファのコンテナクラスにムーブコンストラクタを実装し忘れました。移動時にオブジェクトがコピーされ、データの余分なコピーとパフォーマンスの低下が発生しました。
ストーリー
データ構造のシリアライゼーションライブラリでは、関数からオブジェクトを返す際に、一時オブジェクトが返され、コピーコンストラクタがポインタの浅いコピーを作成していました。何ヶ月も経ってから現れた多くのメモリリークが発生しました。