Serie: C++
cpp
375 righe
· Aggiornato 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;
}
Articoli correlati
C++
c
Aggiornato 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/deviceAlpha.h).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
finalproject.c
finalproject.c — c source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.c).
Leggi l'articolo →
C++
cpp
Aggiornato 2026-07-21
finalproject.cpp
finalproject.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/FinalProject/finalproject.cpp).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
deviceAlpha.h
deviceAlpha.h — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/deviceAlpha.h).
Leggi l'articolo →
C++
c
Aggiornato 2026-07-21
lab8.c
lab8.c — c source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.c).
Leggi l'articolo →
C++
cpp
Aggiornato 2026-07-21
lab8.cpp
lab8.cpp — cpp source code from the C++ learning materials (C++/Mavis_Homework/Lab8/lab8.cpp).
Leggi l'articolo →