Skip to content
第08回 画像リソースの管理

2 / 4 画像ハンドルの所有権とRAIIパターン ​

このステップの目的

リソース管理の中心となる所有権とRAIIを理解し、メモリリークと二重解放を防ぐ仕組みを学びます。

画像ハンドルをManagerが一元所有する ​

DXライブラリのLoadGraph()が返すグラフィックハンドル(整数値)は、DirectXやOSのVRAM上に確保されたテクスチャリソースを指し示す識別番号です。

ImageManagerは、これらのハンドルを内部の配列handlesに格納して一元所有します。

ImageManager.hcpp
private:
    // 各画像IDに対応するDXライブラリのグラフィックハンドル(未読み込み時は -1)
    std::array<int, static_cast<std::size_t>(Id::Count)> handles = {};

自機や敵のクラスは、ハンドルを持ちません。そのため、自機が撃破されたときや敵が全滅したときに、画像を解放してよいかを判断する必要はありません。キャラクターの出現・消滅と、画像を保持する期間を分けて管理します。

RAII(デストラクタによる自動解放)の実装 ​

確保したメモリ、ファイル、DirectXのテクスチャなどは、使い終わったら解放します。ゲーム終了時にRelease()を呼ぶだけでは、途中のエラー終了や呼び忘れで解放漏れが起きます。

C++では、オブジェクトの構築時にリソースを確保し、破棄時にデストラクタで自動解放する方法が広く使われています。この方法をRAII(Resource Acquisition Is Initialization)と呼びます。

ImageManager.cppcpp
ImageManager::ImageManager()
{
    // 初期状態はすべて「未取得」を表す -1 で埋める
    handles.fill(-1);
}

ImageManager::~ImageManager()
{
    // デストラクタ:ImageManager が破棄される際に自動的に実行される
    for (int handle : handles)
    {
        // 正常に取得されているハンドルのみを解放する
        if (handle != -1)
            DeleteGraph(handle);
    }
}

ImageManagerのインスタンスが破棄されると、デストラクタが取得済みのハンドルを解放します。呼び出し側は、同じハンドルを重ねて解放しないようにします。

コピー禁止による二重解放(Double Free)の防止 ​

リソースを所有するクラスでは、コピーを禁止する点を確認します。

ImageManager.hcpp
public:
    // コピーコンストラクタとコピー代入演算子を明示的に削除(禁止)する
    ImageManager(const ImageManager&) = delete;
    ImageManager& operator=(const ImageManager&) = delete;

ImageManagerをコピーすると、同じハンドルを持つManagerが2つできます。たとえば、両方がハンドル番号10を持つ状態になります。
両方が破棄されると、同じハンドル10に対してDeleteGraph()を2回実行してしまいます。これが二重解放(Double Free)です。DirectXのエラーや突然のクラッシュにつながる危険なバグです。

= deleteでコピーを禁止すると、誤ったコピーをコンパイル時に検出して防げます。

チェックリスト
  • 画像ハンドルの所有権がImageManagerに集約されている理由を説明できる
  • RAIIパターンの概念と、デストラクタで自動解放する利点を理解している
  • 読み込みに失敗した(-1の)ハンドルを誤って解放しないようガードされている
  • リソース所有クラスでコピーコンストラクタを削除(= delete)する理由を説明できる