S SmartDocs
シリーズ: C++ cpp 375 行 · 更新日 2026-04-03

singleton.cpp

C++/Part5_進階主題/Ch24_設計模式與最佳實踐/singleton.cpp

// singleton.cpp
// 編譯指令:g++ -std=c++17 -Wall singleton.cpp -o singleton
//
// 本程式示範 Singleton 模式:從 naive 實作到執行緒安全的 Meyers' Singleton

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <mutex>
#include <vector>
#include <chrono>
#include <iomanip>
#include <map>

// ============================================================
// 第一部分:Naive Singleton(有問題的版本)
// ============================================================

// 展示錯誤的做法,僅用於教學
namespace naive {

class Singleton {
    static Singleton* instance_;
    std::string data_;

    Singleton() : data_("預設資料") {
        std::cout << "  [naive] 建構 Singleton\n";
    }

public:
    // 問題 1:不是執行緒安全的
    // 問題 2:記憶體可能洩漏(沒有 delete)
    // 問題 3:可以被複製
    static Singleton* get_instance() {
        if (instance_ == nullptr) {       // 多執行緒下可能同時通過
            instance_ = new Singleton();  // 可能建立多個實例
        }
        return instance_;
    }

    void set_data(const std::string& d) { data_ = d; }
    const std::string& get_data() const { return data_; }
};

Singleton* Singleton::instance_ = nullptr;

} // namespace naive

void demo_naive_singleton() {
    std::cout << "========================================\n";
    std::cout << "  Naive Singleton(有問題的版本)\n";
    std::cout << "========================================\n\n";

    auto* s1 = naive::Singleton::get_instance();
    auto* s2 = naive::Singleton::get_instance();

    std::cout << "  s1 == s2: " << std::boolalpha << (s1 == s2) << "\n";
    std::cout << "  s1 address: " << s1 << "\n";
    std::cout << "  s2 address: " << s2 << "\n";

    s1->set_data("修改的資料");
    std::cout << "  s2->get_data(): " << s2->get_data() << " (同一個物件)\n";

    std::cout << "\n  問題:\n";
    std::cout << "  1. 多執行緒不安全\n";
    std::cout << "  2. 使用 new 但沒有 delete(記憶體洩漏)\n";
    std::cout << "  3. 可以被複製\n";
    std::cout << "\n";
}

// ============================================================
// 第二部分:Meyers' Singleton(推薦做法)
// ============================================================

class MeyersSingleton {
public:
    static MeyersSingleton& instance() {
        // C++11 保證 static 區域變數的初始化是執行緒安全的
        static MeyersSingleton inst;
        return inst;
    }

    // 刪除複製與移動操作
    MeyersSingleton(const MeyersSingleton&) = delete;
    MeyersSingleton& operator=(const MeyersSingleton&) = delete;
    MeyersSingleton(MeyersSingleton&&) = delete;
    MeyersSingleton& operator=(MeyersSingleton&&) = delete;

    void set_value(int v) { value_ = v; }
    int get_value() const { return value_; }

private:
    MeyersSingleton() : value_(0) {
        std::cout << "  [Meyers] Singleton 已建構\n";
    }
    ~MeyersSingleton() {
        std::cout << "  [Meyers] Singleton 已解構\n";
    }

    int value_;
};

void demo_meyers_singleton() {
    std::cout << "========================================\n";
    std::cout << "  Meyers' Singleton(推薦做法)\n";
    std::cout << "========================================\n\n";

    auto& s1 = MeyersSingleton::instance();
    auto& s2 = MeyersSingleton::instance();

    std::cout << "  &s1 == &s2: " << std::boolalpha << (&s1 == &s2) << "\n";
    std::cout << "  s1 address: " << &s1 << "\n";

    s1.set_value(42);
    std::cout << "  s1 設定值 42, s2 讀取: " << s2.get_value() << "\n";

    // 以下會編譯錯誤(已刪除)
    // MeyersSingleton copy = s1;                    // 錯誤:deleted copy
    // MeyersSingleton moved = std::move(s1);        // 錯誤:deleted move

    std::cout << "\n  優點:\n";
    std::cout << "  1. C++11 保證執行緒安全初始化\n";
    std::cout << "  2. 自動在程式結束時解構(無記憶體洩漏)\n";
    std::cout << "  3. 刪除了複製/移動操作\n";
    std::cout << "  4. 實作簡潔\n";
    std::cout << "\n";
}

// ============================================================
// 第三部分:實用範例 — Logger
// ============================================================

class Logger {
public:
    enum class Level { DEBUG, INFO, WARNING, ERROR };

    static Logger& instance() {
        static Logger logger;
        return logger;
    }

    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

    void set_level(Level level) {
        std::lock_guard<std::mutex> lock(mutex_);
        min_level_ = level;
    }

    void debug(const std::string& msg)   { log(Level::DEBUG, msg); }
    void info(const std::string& msg)    { log(Level::INFO, msg); }
    void warning(const std::string& msg) { log(Level::WARNING, msg); }
    void error(const std::string& msg)   { log(Level::ERROR, msg); }

    const std::vector<std::string>& get_logs() const { return logs_; }

private:
    Logger() : min_level_(Level::DEBUG) {
        std::cout << "  [Logger] 初始化完成\n";
    }

    void log(Level level, const std::string& msg) {
        if (level < min_level_) return;

        std::lock_guard<std::mutex> lock(mutex_);

        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);

        std::ostringstream oss;
        oss << std::put_time(std::localtime(&time), "%H:%M:%S")
            << " [" << level_to_string(level) << "] " << msg;

        std::string formatted = oss.str();
        logs_.push_back(formatted);
        std::cout << "  " << formatted << "\n";
    }

    static std::string level_to_string(Level level) {
        switch (level) {
            case Level::DEBUG:   return "DEBUG  ";
            case Level::INFO:    return "INFO   ";
            case Level::WARNING: return "WARNING";
            case Level::ERROR:   return "ERROR  ";
        }
        return "UNKNOWN";
    }

    std::mutex mutex_;
    Level min_level_;
    std::vector<std::string> logs_;
};

void demo_logger() {
    std::cout << "========================================\n";
    std::cout << "  實用範例:Logger Singleton\n";
    std::cout << "========================================\n\n";

    auto& logger = Logger::instance();

    logger.debug("應用程式啟動");
    logger.info("載入設定檔");
    logger.warning("設定檔中有過時的選項");
    logger.info("連線到資料庫");
    logger.error("無法連線到快取伺服器");

    logger.set_level(Logger::Level::WARNING);
    std::cout << "\n  設定最低等級為 WARNING 後:\n";
    logger.debug("這條不會顯示");
    logger.info("這條也不會顯示");
    logger.warning("這條會顯示");
    logger.error("這條也會顯示");

    std::cout << "\n  總共記錄 " << logger.get_logs().size() << " 條日誌\n\n";
}

// ============================================================
// 第四部分:實用範例 — Configuration Manager
// ============================================================

class ConfigManager {
public:
    static ConfigManager& instance() {
        static ConfigManager config;
        return config;
    }

    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;

    void set(const std::string& key, const std::string& value) {
        std::lock_guard<std::mutex> lock(mutex_);
        config_[key] = value;
    }

    std::string get(const std::string& key, const std::string& default_value = "") const {
        std::lock_guard<std::mutex> lock(mutex_);
        auto it = config_.find(key);
        return (it != config_.end()) ? it->second : default_value;
    }

    bool has(const std::string& key) const {
        std::lock_guard<std::mutex> lock(mutex_);
        return config_.find(key) != config_.end();
    }

    int get_int(const std::string& key, int default_value = 0) const {
        std::string val = get(key);
        if (val.empty()) return default_value;
        try { return std::stoi(val); }
        catch (...) { return default_value; }
    }

    bool get_bool(const std::string& key, bool default_value = false) const {
        std::string val = get(key);
        if (val == "true" || val == "1" || val == "yes") return true;
        if (val == "false" || val == "0" || val == "no") return false;
        return default_value;
    }

    void print_all() const {
        std::lock_guard<std::mutex> lock(mutex_);
        std::cout << "  設定項目 (" << config_.size() << " 項):\n";
        for (const auto& [key, value] : config_) {
            std::cout << "    " << key << " = " << value << "\n";
        }
    }

private:
    ConfigManager() {
        // 載入預設設定
        config_["app.name"] = "MyApp";
        config_["app.version"] = "1.0.0";
        config_["server.port"] = "8080";
        config_["server.host"] = "localhost";
        config_["debug.enabled"] = "false";
        config_["log.level"] = "INFO";
    }

    mutable std::mutex mutex_;
    std::map<std::string, std::string> config_;
};

void demo_config_manager() {
    std::cout << "========================================\n";
    std::cout << "  實用範例:ConfigManager Singleton\n";
    std::cout << "========================================\n\n";

    auto& config = ConfigManager::instance();

    // 讀取預設設定
    config.print_all();

    // 修改設定
    std::cout << "\n  修改設定:\n";
    config.set("debug.enabled", "true");
    config.set("server.port", "3000");
    config.set("database.url", "postgresql://localhost:5432/mydb");

    // 讀取不同型別
    std::cout << "  app.name      = " << config.get("app.name") << "\n";
    std::cout << "  server.port   = " << config.get_int("server.port") << "\n";
    std::cout << "  debug.enabled = " << std::boolalpha
              << config.get_bool("debug.enabled") << "\n";
    std::cout << "  missing.key   = \"" << config.get("missing.key", "預設值") << "\"\n";

    // 確認是同一個實例
    auto& config2 = ConfigManager::instance();
    std::cout << "\n  config == config2: " << (&config == &config2) << "\n";
    std::cout << "  config2.app.name = " << config2.get("app.name") << "\n";

    std::cout << "\n  更新後的設定:\n";
    config.print_all();
    std::cout << "\n";
}

// ============================================================
// 第五部分:Singleton 的替代方案討論
// ============================================================

struct Monostate {
    int get_value() const { return value_; }
    void set_value(int v) { value_ = v; }
private:
    static inline int value_ = 0;
};

void demo_alternatives() {
    std::cout << "========================================\n";
    std::cout << "  Singleton 的替代方案\n";
    std::cout << "========================================\n\n";

    std::cout << "  Singleton 雖然方便,但也有缺點:\n";
    std::cout << "  1. 本質上是全域狀態,增加耦合\n";
    std::cout << "  2. 難以進行單元測試(無法替換為 mock)\n";
    std::cout << "  3. 隱藏了依賴關係\n\n";

    std::cout << "  替代方案:\n";
    std::cout << "  1. 依賴注入(Dependency Injection):\n";
    std::cout << "     將物件作為參數傳入,而非在內部取得\n\n";

    std::cout << "  2. 服務定位器(Service Locator):\n";
    std::cout << "     集中管理服務,但仍可替換實作\n\n";

    std::cout << "  3. Monostate 模式:\n";
    std::cout << "     所有實例共享 static 成員,但可以自由建構\n\n";

    // Monostate 範例(見下方全域定義的 Monostate 類別)
    Monostate m1, m2;
    m1.set_value(42);
    std::cout << "  Monostate 範例:\n";
    std::cout << "    m1.set_value(42)\n";
    std::cout << "    m2.get_value() = " << m2.get_value()
              << " (所有實例共享狀態)\n\n";
}

// ============================================================
// 主程式
// ============================================================

int main() {
    std::cout << "╔══════════════════════════════════════╗\n";
    std::cout << "║  設計模式:Singleton 模式              ║\n";
    std::cout << "╚══════════════════════════════════════╝\n\n";

    demo_naive_singleton();
    demo_meyers_singleton();
    demo_logger();
    demo_config_manager();
    demo_alternatives();

    std::cout << "=== 程式結束 ===\n";
    return 0;
}

関連記事