跳转到内容

C++ 单例模式实现

来自代码酷


单例模式(Singleton Pattern)是设计模式中最简单的创建型模式之一,用于确保一个类仅有一个实例,并提供一个全局访问点。该模式在需要控制资源(如数据库连接、线程池、配置管理)或确保全局一致性时非常有用。

基本概念[编辑 | 编辑源代码]

单例模式的核心思想是通过以下机制实现:

  • 私有构造函数:防止外部直接实例化
  • 静态私有成员变量:保存唯一实例
  • 静态公有方法:提供全局访问接口

UML类图[编辑 | 编辑源代码]

classDiagram class Singleton { -static instance: Singleton* -Singleton() +static getInstance() Singleton* }

基础实现[编辑 | 编辑源代码]

以下是C++中最基础的线程不安全实现:

class Singleton {
private:
    static Singleton* instance;
    
    // 私有构造函数防止外部实例化
    Singleton() {}
    
public:
    // 删除拷贝构造函数和赋值运算符
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
    
    void someMethod() {
        // 示例方法
    }
};

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

线程安全实现[编辑 | 编辑源代码]

在多线程环境中,基础实现可能导致多个实例被创建。以下是改进方案:

双重检查锁定[编辑 | 编辑源代码]

#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx;
    
    Singleton() {}
    
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    static Singleton* getInstance() {
        if (instance == nullptr) {  // 第一次检查
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {  // 第二次检查
                instance = new Singleton();
            }
        }
        return instance;
    }
};

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

Meyers单例(最优实现)[编辑 | 编辑源代码]

利用局部静态变量的线程安全特性(C++11起保证):

class Singleton {
private:
    Singleton() {}
    
public:
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }
};

实际应用案例[编辑 | 编辑源代码]

配置管理器[编辑 | 编辑源代码]

游戏开发中常用单例管理全局配置:

class ConfigManager {
private:
    std::unordered_map<std::string, std::string> settings;
    static ConfigManager* instance;
    
    ConfigManager() {
        // 加载配置文件
        settings["resolution"] = "1920x1080";
        settings["volume"] = "80";
    }
    
public:
    static ConfigManager& getInstance() {
        static ConfigManager instance;
        return instance;
    }
    
    std::string getSetting(const std::string& key) {
        return settings[key];
    }
    
    void setSetting(const std::string& key, const std::string& value) {
        settings[key] = value;
    }
};

日志系统[编辑 | 编辑源代码]

class Logger {
private:
    std::ofstream logFile;
    static Logger* instance;
    
    Logger() {
        logFile.open("app.log", std::ios::app);
    }
    
public:
    static Logger& getInstance() {
        static Logger instance;
        return instance;
    }
    
    void log(const std::string& message) {
        logFile << "[" << getCurrentTime() << "] " << message << std::endl;
    }
    
    ~Logger() {
        logFile.close();
    }
};

数学分析[编辑 | 编辑源代码]

单例模式的时间复杂度为O(1),空间复杂度同样为O(1),因为无论调用多少次getInstance(),都只存在一个实例。

优缺点[编辑 | 编辑源代码]

优点:

  • 严格控制实例数量
  • 全局访问点方便管理
  • 延迟初始化节省资源

缺点:

  • 违反单一职责原则(管理自身生命周期)
  • 单元测试困难(全局状态)
  • 多线程环境下需要特殊处理

最佳实践建议[编辑 | 编辑源代码]

1. 优先使用Meyers单例(简单且线程安全) 2. 考虑是否需要单例(可能依赖注入更合适) 3. 明确生命周期管理(特别是需要清理资源时) 4. 避免在单例中保存过多状态

扩展阅读[编辑 | 编辑源代码]

  • 单例模式与依赖注入的比较
  • 单例在分布式系统中的挑战
  • 单例模式的反模式讨论