【C++学習|初心者向け】カスタムデリータでスマートポインタを賢く使う!状態を持たせるテクニック

1. 導入:なぜカスタムデリータが必要なのか?

C++のスマートポインタ(std::unique_ptrなど)は、メモリ管理を自動化してくれる非常に強力なツールです。通常、ポインタが不要になると自動的にdeleteが呼ばれますが、「単にメモリを解放するだけでなく、解放時にログを出したり、特定のクリーンアップ処理を行いたい」というケースは現場でよくあります。
そんな時、デフォルトの挙動をカスタマイズできる「カスタムデリータ」を知っていると、リソース管理の柔軟性が格段に向上します。

2. 基礎知識:デリータとファンクタとは

スマートポインタの「デリータ」とは、ポインタが寿命を迎えた時に実行される「後始末役」のことです。
通常はデフォルトのdeleteが使われますが、ここに「関数オブジェクト(ファンクタ)」を渡すことができます。ファンクタとは、関数のように呼び出せるクラスのことで、内部に「状態(変数)」を持たせることが可能です。これにより、解放処理の際に「誰が」「いつ」解放したのかといった情報を保持・活用できるようになります。

3. 実装:カスタムデリータの仕組み

std::unique_ptrのテンプレート引数にデリータの型を指定します。
std::unique_ptr p(new T, DeleterType(状態));
このように記述することで、pが破棄されるタイミングで、DeleterTypeのインスタンスが持っている処理が自動的に実行されます。

4. サンプルプログラム

以下のコードは、オブジェクトが解放される際に、保持していたIDとともにログを出力する簡単な例です。そのままコピーして実行してみてください。

include <iostream>
include <memory>
include <string>

// 解放時にログを出力するためのカスタムデリータ(ファンクタ)
struct MyDeleter {
    std::string id;
    
    // コンストラクタで識別子を受け取る
    MyDeleter(std::string name) : id(name) {}

    // 実際に呼ばれる解放処理
    void operator()(int ptr) const {
        std::cout << "リソース " << id << " を解放します。" << std::endl;
        delete ptr;
    }
};

int main() {
    // カスタムデリータを指定したunique_ptrの作成
    // 第2引数でデリータに状態(ID)を渡している
    std::unique_ptr<int, MyDeleter> p(new int(10), MyDeleter("Resource_001"));

    std::cout << "ポインタの値: " << p << std::endl;
    
    // スコープを抜けると、MyDeleterが実行される
    return 0;
}

5. 応用・注意点:現場で陥りやすい罠

カスタムデリータを使う際、以下の点に注意してください。

サイズへの影響
カスタムデリータをunique_ptrに持たせると、ポインタ自体のサイズが大きくなる場合があります。ラムダ式をデリータに使うことも可能ですが、その際、空のラムダ式を使うとサイズを抑えられるという最適化の恩恵を受けられることがあります。

デリータの持ちすぎに注意
デリータに巨大なデータを持たせると、スマートポインタ自体が重くなります。あくまで「解放時に必要な情報」に絞り込み、複雑すぎる処理は関数として切り出すのがクリーンなコードの秘訣です。

このテクニックをマスターすれば、ファイルハンドルのクローズやネットワーク接続の切断など、メモリ以外のリソース管理もスマートに行えるようになりますよ!

コメント

タイトルとURLをコピーしました