シングルトンパターンの概要と実装方法

概要

シングルトンパターンは、特定のクラスに対してメモリ上に唯一のインスタンスを保持する設計パターンです。同一のクラスを複数回インスタンス化する必要がない場合、リソースの浪費を防ぐためにこのパターンが採用されます。

実装方法

エイジャーモード(急な初期化)

クラスがロードされるタイミングでインスタンスを生成します。この方法は線程安全ですが、未使用でもメモリを確保するため、リソース効率が悪い場合があります。

#include <cstdio>
#include <mutex>

class ResourceManager {
public:
    static ResourceManager* getInstance();
private:
    ResourceManager();
    static ResourceManager* instance_;
};

ResourceManager* ResourceManager::instance_ = new ResourceManager();

ResourceManager* ResourceManager::getInstance() {
    return instance_;
}

ResourceManager::ResourceManager() {}

int main() {
    ResourceManager* r1 = ResourceManager::getInstance();
    ResourceManager* r2 = ResourceManager::getInstance();
    printf("r1 = %p, r2 = %p\n", r1, r2);
    return 0;
}

レジャーモード(遅延初期化)

非同期セーフな実装

#include <cstdio>
#include <mutex>

class ResourceManager {
public:
    static ResourceManager* getInstance();
private:
    ResourceManager();
    static ResourceManager* instance_;
};

ResourceManager* ResourceManager::instance_ = nullptr;

ResourceManager* ResourceManager::getInstance() {
    if (!instance_) {
        instance_ = new ResourceManager();
    }
    return instance_;
}

ResourceManager::ResourceManager() {}

int main() {
    ResourceManager* r1 = ResourceManager::getInstance();
    ResourceManager* r2 = ResourceManager::getInstance();
    printf("r1 = %p, r2 = %p\n", r1, r2);
    return 0;
}

同期セーフな実装(ダブルチェック)

#include <cstdio>
#include <mutex>

class ResourceManager {
public:
    static ResourceManager* getInstance();
private:
    ResourceManager();
    static ResourceManager* instance_;
    static std::mutex mtx_;
};

ResourceManager* ResourceManager::instance_ = nullptr;
std::mutex ResourceManager::mtx_;

ResourceManager* ResourceManager::getInstance() {
    if (!instance_) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (!instance_) {
            instance_ = new ResourceManager();
        }
    }
    return instance_;
}

ResourceManager::ResourceManager() {}

int main() {
    ResourceManager* r1 = ResourceManager::getInstance();
    ResourceManager* r2 = ResourceManager::getInstance();
    printf("r1 = %p, r2 = %p\n", r1, r2);
    return 0;
}

エイジャーモードとレジャーモードの比較

初期化タイミング

  • エイジャーモード:クラスロード時に即座に初期化
  • レジャーモード:初めてgetInstance()が呼ばれた時のみ初期化

スレッドセーフ性

  • エイジャーモード:デフォルトでスレッドセーフ
  • レジャーモード:同期処理が必要

メモリ使用量

  • エイジャーモード:常にメモリを確保
  • レジャーモード:必要に応じて確保

パフォーマンス

  • エイジャーモード:初期化が早い
  • レジャーモード:初回呼び出しが遅い

タグ: C++ シングルトン スレッドセーフ デザインパターン

6月14日 00:55 投稿