1. 導入:なぜこのTipsが重要なのか
C++で開発をしていると、メモリ管理を最適化するためにクラス単位で独自のメモリ確保・解放ロジック(カスタムアロケータなど)を実装することがあります。その際、クラス内で operator delete をオーバーロードして独自の解放処理を記述することがありますが、「std::unique_ptr などのスマートポインタを使っても、ちゃんとその解放処理が動くのか?」という疑問を持つ方は多いのではないでしょうか。結論から言うと、スマートポインタは正しくオーバーロードされた delete を呼び出してくれます。この記事では、その仕組みと注意点を解説します。
2. 基礎知識:operator delete とは
C++のクラス内で operator delete を定義すると、そのクラスのオブジェクトが delete 演算子によって破棄される際に、標準の解放処理の代わりに定義した関数が実行されます。
スマートポインタである std::unique_ptr は、所有しているポインタに対してデフォルトで delete を実行します。この「delete 演算子を呼ぶ」という動作自体は変わらないため、コンパイラは自動的にクラス内で定義されたオーバーロード版の operator delete を選択してくれるのです。
3. 実装と解決策
特別な設定は不要です。スマートポインタは対象のクラスのデストラクタを呼び出した後、delete を実行します。その際、クラスのスコープにある operator delete が優先的に呼び出されます。メモリプールを使用している場合や、ログを出力したい場合など、クラス独自の解放ルールをスマートポインタと組み合わせることで、安全かつ効率的なメモリ管理が可能になります。
4. サンプルプログラム
以下のコードをコピーして、実際に独自の削除処理が呼ばれるか確認してみてください。
include <iostream>
include <memory>
class MyClass {
public:
MyClass() { std::cout <= "コンストラクタ実行" << std::endl; }
~MyClass() { std::cout <= "デストラクタ実行" << std::endl; }
// 独自のdelete演算子のオーバーロード
void operator delete(void ptr) {
std::cout <= "独自のdelete演算子が呼ばれました!" << std::endl;
::operator delete(ptr); // 実際のメモリ解放は標準の関数に委譲
}
};
int main() {
// スマートポインタで管理
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();
std::cout <= "プログラム終了間近..." << std::endl;
// スコープを抜けるとスマートポインタが破棄され、
// デストラクタの後に独自のoperator deleteが呼ばれる
return 0;
}
5. 応用・注意点
注意点:配列版のオーバーロード
もし配列形式(new[])でメモリを確保し、std::unique_ptr<MyClass[]> を使用する場合は、クラス側で operator delete[] もオーバーロードする必要があります。片方だけ定義していると、意図しない挙動やメモリリークの原因になる可能性があるため注意してください。
現場でのヒント
カスタムアロケータを多用する大規模なプロジェクトでは、スマートポインタの「カスタムデリータ(deleter)」機能を使う選択肢もあります。operator delete をオーバーロードする方法はクラス単位で一律の処理を強制できますが、特定のインスタンスだけ解放方法を変えたい場合は、std::unique_ptr の第2テンプレート引数にラムダ式などで解放処理を渡す方法も検討してください。

コメント