单例模式_

单例模式

单例模式是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

基本概念

核心思想

  • 一个类只能有一个实例
  • 该实例必须由自身创建
  • 必须向整个系统提供这个实例

实现方式

1. 懒汉式(线程不安全)

cpp 复制代码
class Singleton {
private:
    static Singleton* instance;
    
    // 私有构造函数,防止外部实例化
    Singleton() {}
    
    // 防止拷贝和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
    
    void someBusinessLogic() {
        // 业务逻辑
    }
};

// 初始化静态成员
Singleton* Singleton::instance = nullptr;

问题:多线程环境下可能创建多个实例。

2. 懒汉式(线程安全 - 锁)

cpp 复制代码
#include <mutex>

class ThreadSafeSingleton {
private:
    static ThreadSafeSingleton* instance;
    static std::mutex mtx;
    
    ThreadSafeSingleton() {}
    ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
    ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;

public:
    static ThreadSafeSingleton* getInstance() {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        if (instance == nullptr) {
            instance = new ThreadSafeSingleton();
        }
        return instance;
    }
};

ThreadSafeSingleton* ThreadSafeSingleton::instance = nullptr;
std::mutex ThreadSafeSingleton::mtx;

缺点:每次获取实例都要加锁,性能较差。

3. 双重检查锁定(DCLP)

cpp 复制代码
#include <atomic>
#include <mutex>

class DoubleCheckedSingleton {
private:
    static std::atomic<DoubleCheckedSingleton*> instance;
    static std::mutex mtx;
    
    DoubleCheckedSingleton() {}
    DoubleCheckedSingleton(const DoubleCheckedSingleton&) = delete;
    DoubleCheckedSingleton& operator=(const DoubleCheckedSingleton&) = delete;

public:
    static DoubleCheckedSingleton* getInstance() {
        DoubleCheckedSingleton* tmp = instance.load(std::memory_order_acquire);
        if (tmp == nullptr) {  // 第一次检查
            std::lock_guard<std::mutex> lock(mtx);
            tmp = instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {  // 第二次检查
                tmp = new DoubleCheckedSingleton();
                instance.store(tmp, std::memory_order_release);
            }
        }
        return tmp;
    }
};

std::atomic<DoubleCheckedSingleton*> DoubleCheckedSingleton::instance{nullptr};
std::mutex DoubleCheckedSingleton::mtx;

4. Meyers' Singleton(推荐)

cpp 复制代码
class MeyersSingleton {
private:
    MeyersSingleton() {}
    MeyersSingleton(const MeyersSingleton&) = delete;
    MeyersSingleton& operator=(const MeyersSingleton&) = delete;

public:
    static MeyersSingleton& getInstance() {
        static MeyersSingleton instance;  // C++11保证线程安全
        return instance;
    }
    
    void businessMethod() {
        // 业务方法
    }
};

优点

  • 线程安全(C++11标准保证)
  • 延迟初始化
  • 自动销毁
  • 代码简洁

现代 C++ 单例模式实现

1. 模板化单例

cpp 复制代码
#include <memory>
#include <mutex>

template<typename T>
class Singleton {
protected:
    Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static T& getInstance() {
        static T instance;
        return instance;
    }
};

// 使用方式
class ConfigManager : public Singleton<ConfigManager> {
    friend class Singleton<ConfigManager>;
    
private:
    ConfigManager() = default;
    
public:
    void loadConfig(const std::string& path) {
        // 加载配置
    }
    
    std::string getValue(const std::string& key) {
        return ""; // 返回配置值
    }
};

2. 带参数的单例

cpp 复制代码
template<typename T>
class ParametrizedSingleton {
protected:
    ParametrizedSingleton() = default;
    ParametrizedSingleton(const ParametrizedSingleton&) = delete;
    ParametrizedSingleton& operator=(const ParametrizedSingleton&) = delete;

public:
    template<typename... Args>
    static T& getInstance(Args&&... args) {
        static T instance(std::forward<Args>(args)...);
        return instance;
    }
};

class DatabaseConnection : public ParametrizedSingleton<DatabaseConnection> {
    friend class ParametrizedSingleton<DatabaseConnection>;
    
private:
    std::string connectionString_;
    
    // 私有构造函数,接受参数
    DatabaseConnection(const std::string& connStr) : connectionString_(connStr) {}
    
public:
    void connect() {
        // 连接数据库
    }
};

// 使用
auto& db = DatabaseConnection::getInstance("server=localhost;database=test");

实际应用示例

1. 日志系统单例

cpp 复制代码
#include <fstream>
#include <mutex>
#include <string>
#include <chrono>
#include <iomanip>

class Logger {
private:
    std::ofstream logFile;
    std::mutex logMutex;
    
    Logger() {
        logFile.open("application.log", std::ios::app);
        if (!logFile.is_open()) {
            throw std::runtime_error("无法打开日志文件");
        }
    }
    
    ~Logger() {
        if (logFile.is_open()) {
            logFile.close();
        }
    }
    
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    static Logger& getInstance() {
        static Logger instance;
        return instance;
    }
    
    void log(const std::string& message, const std::string& level = "INFO") {
        std::lock_guard<std::mutex> lock(logMutex);
        
        auto now = std::chrono::system_clock::now();
        auto time = std::chrono::system_clock::to_time_t(now);
        
        logFile << "[" << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S") << "] "
                << "[" << level << "] " << message << std::endl;
    }
    
    void info(const std::string& message) {
        log(message, "INFO");
    }
    
    void error(const std::string& message) {
        log(message, "ERROR");
    }
    
    void warn(const std::string& message) {
        log(message, "WARN");
    }
};

// 使用示例
void someFunction() {
    Logger::getInstance().info("函数开始执行");
    // ... 业务逻辑
    Logger::getInstance().info("函数执行完成");
}

2. 配置管理器单例

cpp 复制代码
#include <unordered_map>
#include <string>
#include <fstream>
#include <sstream>

class ConfigManager {
private:
    std::unordered_map<std::string, std::string> config_;
    bool loaded_ = false;
    
    ConfigManager() = default;
    ConfigManager(const ConfigManager&) = delete;
    ConfigManager& operator=(const ConfigManager&) = delete;

public:
    static ConfigManager& getInstance() {
        static ConfigManager instance;
        return instance;
    }
    
    bool loadFromFile(const std::string& filename) {
        std::ifstream file(filename);
        if (!file.is_open()) {
            return false;
        }
        
        std::string line;
        while (std::getline(file, line)) {
            // 跳过空行和注释
            if (line.empty() || line[0] == '#') continue;
            
            size_t pos = line.find('=');
            if (pos != std::string::npos) {
                std::string key = line.substr(0, pos);
                std::string value = line.substr(pos + 1);
                
                // 去除前后空白
                key.erase(0, key.find_first_not_of(" \t"));
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t") + 1);
                
                config_[key] = value;
            }
        }
        
        loaded_ = true;
        return true;
    }
    
    std::string getString(const std::string& key, const std::string& defaultValue = "") const {
        auto it = config_.find(key);
        return it != config_.end() ? it->second : defaultValue;
    }
    
    int getInt(const std::string& key, int defaultValue = 0) const {
        auto it = config_.find(key);
        if (it != config_.end()) {
            try {
                return std::stoi(it->second);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    double getDouble(const std::string& key, double defaultValue = 0.0) const {
        auto it = config_.find(key);
        if (it != config_.end()) {
            try {
                return std::stod(it->second);
            } catch (...) {
                return defaultValue;
            }
        }
        return defaultValue;
    }
    
    bool getBool(const std::string& key, bool defaultValue = false) const {
        auto it = config_.find(key);
        if (it != config_.end()) {
            std::string value = it->second;
            std::transform(value.begin(), value.end(), value.begin(), ::tolower);
            return value == "true" || value == "1" || value == "yes";
        }
        return defaultValue;
    }
    
    void set(const std::string& key, const std::string& value) {
        config_[key] = value;
    }
    
    bool isLoaded() const {
        return loaded_;
    }
};

// 使用示例
void initializeApplication() {
    auto& config = ConfigManager::getInstance();
    if (!config.loadFromFile("config.ini")) {
        throw std::runtime_error("无法加载配置文件");
    }
    
    std::string host = config.getString("database.host", "localhost");
    int port = config.getInt("database.port", 3306);
    bool debug = config.getBool("app.debug", false);
}

3. 线程池单例

cpp 复制代码
#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>

class ThreadPool {
private:
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    
    std::mutex queueMutex;
    std::condition_variable condition;
    bool stop = false;
    
    ThreadPool(size_t threads = std::thread::hardware_concurrency()) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                for (;;) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(this->queueMutex);
                        this->condition.wait(lock, [this] {
                            return this->stop || !this->tasks.empty();
                        });
                        if (this->stop && this->tasks.empty()) {
                            return;
                        }
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    task();
                }
            });
        }
    }
    
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            stop = true;
        }
        condition.notify_all();
        for (std::thread& worker : workers) {
            worker.join();
        }
    }
    
    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete;

public:
    static ThreadPool& getInstance() {
        static ThreadPool instance;
        return instance;
    }
    
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) 
        -> std::future<typename std::invoke_result<F, Args...>::type> {
        
        using return_type = typename std::invoke_result<F, Args...>::type;
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queueMutex);
            if (stop) {
                throw std::runtime_error("线程池已停止");
            }
            tasks.emplace([task](){ (*task)(); });
        }
        condition.notify_one();
        return res;
    }
};

// 使用示例
void parallelComputation() {
    auto& pool = ThreadPool::getInstance();
    
    std::vector<std::future<int>> results;
    for (int i = 0; i < 10; ++i) {
        results.emplace_back(
            pool.enqueue([i] {
                std::this_thread::sleep_for(std::chrono::milliseconds(100));
                return i * i;
            })
        );
    }
    
    for (auto&& result : results) {
        std::cout << result.get() << std::endl;
    }
}

单例模式的变体

1. 多例模式(Multiton)

cpp 复制代码
#include <map>
#include <memory>
#include <mutex>

template<typename Key, typename T>
class Multiton {
private:
    static std::map<Key, std::shared_ptr<T>> instances;
    static std::mutex mutex;
    
    Multiton() = default;
    Multiton(const Multiton&) = delete;
    Multiton& operator=(const Multiton&) = delete;

public:
    template<typename... Args>
    static std::shared_ptr<T> getInstance(const Key& key, Args&&... args) {
        std::lock_guard<std::mutex> lock(mutex);
        
        auto it = instances.find(key);
        if (it == instances.end()) {
            auto instance = std::make_shared<T>(std::forward<Args>(args)...);
            instances[key] = instance;
            return instance;
        }
        return it->second;
    }
};

template<typename Key, typename T>
std::map<Key, std::shared_ptr<T>> Multiton<Key, T>::instances;

template<typename Key, typename T>
std::mutex Multiton<Key, T>::mutex;

// 使用示例
class DatabaseConnection {
private:
    std::string connectionString_;
    
public:
    DatabaseConnection(const std::string& connStr) : connectionString_(connStr) {}
    
    void connect() {
        // 连接逻辑
    }
};

void multitonExample() {
    auto db1 = Multiton<std::string, DatabaseConnection>::getInstance(
        "primary", "server=primary;database=main");
    auto db2 = Multiton<std::string, DatabaseConnection>::getInstance(
        "secondary", "server=secondary;database=backup");
    
    db1->connect();
    db2->connect();
}

单例模式的优缺点

优点

  1. 严格控制实例数量:确保只有一个实例存在
  2. 全局访问点:方便在程序的任何地方访问
  3. 延迟初始化:只有在第一次使用时才创建实例
  4. 减少资源消耗:避免重复创建相似对象

缺点

  1. 违反单一职责原则:同时负责创建实例和业务逻辑
  2. 隐藏依赖关系:使类之间的依赖关系不明显
  3. 难以测试:全局状态使得单元测试困难
  4. 线程安全问题:需要额外处理多线程环境
  5. 可能产生隐藏耦合:代码过度依赖单例

最佳实践

1. 使用场景

  • 需要严格控制资源使用的场景(如配置管理、日志系统)
  • 频繁使用的重量级对象(如数据库连接池)
  • 需要全局访问点的工具类

2. 避免使用场景

  • 可以用参数传递的对象
  • 需要测试的代码
  • 可能需要在未来扩展为多实例的类

3. 现代 C++ 建议

cpp 复制代码
// 推荐使用 Meyers' Singleton
class ModernSingleton {
private:
    ModernSingleton() = default;
    
public:
    static ModernSingleton& getInstance() {
        static ModernSingleton instance;
        return instance;
    }
    
    // 明确删除拷贝操作
    ModernSingleton(const ModernSingleton&) = delete;
    ModernSingleton& operator=(const ModernSingleton&) = delete;
    
    // 可以添加移动语义(虽然单例通常不需要移动)
    ModernSingleton(ModernSingleton&&) = delete;
    ModernSingleton& operator=(ModernSingleton&&) = delete;
};

总结

单例模式在特定场景下非常有用,但应该谨慎使用。在现代 C++ 中,推荐使用 Meyers' Singleton 实现,因为它简洁、线程安全且自动管理生命周期。

关键要点

  • 优先考虑依赖注入而不是单例
  • 如果需要单例,使用静态局部变量实现
  • 明确删除拷贝和赋值操作
  • 考虑线程安全性
  • 避免在单例中管理复杂的资源生命周期

单例模式是一个强大的工具,但像所有全局状态一样,应该在有充分理由时才使用。

相关推荐
wheeldown16 小时前
【Linux】多线程核心速记:线程池 + 单例模式 + 线程安全 + 死锁 + 智能指针
linux·运维·服务器·安全·单例模式
Malone-AI3 天前
设计模式之单例模式
单例模式·设计模式
Irene19913 天前
JavaScript 模块 单例模式 和 副作用 详解
单例模式·副作用
那我掉的头发算什么3 天前
【javaEE】多线程 -- 超级详细的核心组件精讲(单例模式 / 阻塞队列 / 线程池 / 定时器)原理与实现
java·单例模式·java-ee
TechMasterPlus3 天前
java:单例模式
java·开发语言·单例模式
IT永勇6 天前
C++设计模式-单例
c++·单例模式·设计模式
爱学的小码6 天前
JavaEE初阶——多线程3(案例)
java·开发语言·单例模式·java-ee
@老蝴7 天前
Java EE - 多线程下单例模式的设计
单例模式·java-ee·intellij-idea
乂爻yiyao8 天前
设计模式思想——从单例模式说起
java·单例模式·设计模式