1. 導入:なぜカスタムデリータが必要なのか?
C++のスマートポインタであるstd::shared_ptrは、メモリ管理を自動化し、メモリリークを劇的に減らしてくれます。しかし、実務では「単にdeleteするだけでは不十分なリソース」を扱うケースが多々あります。例えば、C言語のライブラリで確保したメモリの解放(free)や、ファイルハンドルのクローズ、あるいは特定のAPI経由でのリソース破棄などです。これらを適切に管理するために「カスタムデリータ」の知識は不可欠です。
2. 基礎知識:スマートポインタとカスタムデリータの仕組み
std::shared_ptrは、ポインタが指すオブジェクトが不要になった際、デフォルトではdelete演算子を呼び出します。カスタムデリータとは、この「破棄のタイミングで実行される処理」をユーザーが自由に定義できる機能です。
std::unique_ptrとの最大の違いは、型の一部にならないという点です。std::unique_ptrの場合、デリータの型をテンプレート引数に含める必要がありますが、std::shared_ptrは実行時にデリータを関数オブジェクトとして保持するため、異なるデリータを持つshared_ptr同士でも同じ型(std::shared_ptr
3. 実装・解決策
std::shared_ptrのコンストラクタの第2引数に、ラムダ式や関数ポインタを渡すことでデリータを指定します。これにより、オブジェクトの寿命管理とリソースの解放を一元化できます。
4. サンプルプログラム
以下は、C言語の標準ライブラリ関数(malloc/free)で確保したメモリを管理する実用的な例です。
include
include
include
int main() {
// 1. mallocで確保したメモリをshared_ptrで管理する
// デフォルトのdeleteでは解放できないため、freeを呼ぶデリータを渡す
std::shared_ptr
static_cast
[](int p) {
std::cout << "カスタムデリータ実行: メモリを解放します" << std::endl;
std::free(p);
}
);
// 2. 正常に利用可能
ptr = 100;
std::cout << "値: " << ptr << std::endl;
// 3. スコープを抜けると自動的にカスタムデリータが呼ばれる
return 0;
}
5. 応用・注意点
実務で利用する際に気をつけるべきポイントを挙げます。
・循環参照の回避
カスタムデリータを使用しても、std::shared_ptr特有の循環参照の問題は発生します。相互参照が発生するような構造では、適宜std::weak_ptrを併用してください。
・デリータ内での例外送出
デリータ内から例外を投げることは避けてください。デリータは通常、デストラクタやスタックアンワインド中に呼ばれるため、例外が外に漏れるとプログラムがstd::terminateで即座に停止します。
・メモリ以外のリソース管理
データベースのコネクションや、RAIIに対応していないレガシーなAPIのハンドル管理にも応用できます。特に、複数の箇所でリソースを共有しつつ、最後の1つが破棄されたタイミングでクローズ処理を行いたい場合に、std::shared_ptrのカスタムデリータは非常に強力な武器となります。

コメント