单例模式-创建型

一、单例模式

1.1、核心思想

保证一个类只有一个实例,并提供一个全局访问点

1.2、为什么要用单例模式?

问题场景:

假设有一个配置管理器,如果程序中到处都能随意创建它的实例:

cpp 复制代码
// 在A文件中
ConfigManager* config1 = new ConfigManager();
config1->setValue("theme", "dark");

// 在B文件中  
ConfigManager* config2 = new ConfigManager();
string theme = config2->getValue("theme"); // 可能得到错误的值!

配置信息不一致,内存浪费。

解决方案:单例模式

cpp 复制代码
// 在A文件中
ConfigManager* config1 = ConfigManager::getInstance();
config1->setValue("theme", "dark");

// 在B文件中
ConfigManager* config2 = ConfigManager::getInstance(); 
string theme = config2->getValue("theme"); // 保证是 "dark"
// config1 和 config2 实际上是同一个实例!

1.3、单例模式实现

  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 doSomething() {
        cout << "单例对象在工作..." << endl;
    }
};

// 静态成员初始化
Singleton* Singleton::instance = nullptr;
  1. 懒汉式(线程安全,但不高效)
cpp 复制代码
#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mtx;  // 互斥锁
    
    Singleton() { 
        cout << "Singleton 实例被创建!" << endl; 
    }
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {  // 第一次检查,避免不必要的锁
            std::lock_guard<std::mutex> lock(mtx);  // 加锁
            if (instance == nullptr) {  // 第二次检查,确保线程安全
                instance = new Singleton();
            }
        }
        return instance;
    }
    
    void doSomething() {
        cout << "单例对象在工作..." << endl;
    }
};

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

利用互斥锁进行线程安全控制,但每次调用getInstance()时都需要加锁解锁,效率较低。

  1. C++11 静态局部变量(线程安全,高效)
cpp 复制代码
class Singleton {
private:
    Singleton() = default;
    ~Singleton() = default;
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

public:
    static Singleton& getInstance() {
        static Singleton instance;  // C++11保证这是线程安全的
        return instance;
    }
    
    void doSomething() {
        cout << "单例对象在工作..." << endl;
    }
};

线程安全:C++11标准保证

二、实战案例

获取系统时间:

很明确这是全局且唯一的,那么可以使用单例模式。

cpp 复制代码
class SystemTime
{
    public:
        static SystemTime* getInstance()
        {
            static SystemTime instance;
            return &instance;
        }
        ~ SystemTime(){}
        DateTime getCurrentTime()const{ return DateTime::now(); }
    private:
        SystemTime(){}
        SystemTime(const SystemTime&)=delete;
        SystemTime& operator=(const SystemTime&)=delete;
};

// 使用
auto currentTime = SystemTime::getInstance()->getCurrentTime();

不过实际应用中,时间一般都包含在某个组件中,比如日志,游戏存档等,所以一般不会单独用一个单例类来管理时间。

三、总结

3.1、单例模式优缺点

优点 缺点
严格控制实例数量 全局状态,可能被任意修改
全局访问点,方便使用 不利于单元测试
节约系统资源 违反单一职责原则(既要管理业务,也要管理生命周期)
懒加载,提高性能 隐藏了依赖关系

3.2、适用场景

  1. 日志系统:整个程序共享一个日志实例
  2. 数据库连接池:只需要一个连接池管理器
  3. 线程池
  4. 游戏存档系统
    ***Tips:***没有完美的设计模式,只有更适合该场景的模式;在使用单例模式,需权衡利弊
相关推荐
云泽80837 分钟前
C++11 核心特性全解:列表初始化、右值引用与移动语义实战
开发语言·c++
AI进化营-智能译站1 小时前
ROS2 C++开发系列12-用多态与虚函数构建可扩展的ROS2机器人行为模块
开发语言·c++·ai·机器人
Morwit2 小时前
QML组件之间的通信方案(暴露子组件)
c++·qt·职场和发展
qeen872 小时前
【数据结构】建堆的时间复杂度讨论与TOP-K问题
c语言·数据结构·c++·学习·
图码2 小时前
如何用多种方法判断字符串是否为回文?
开发语言·数据结构·c++·算法·阿里云·线性回归·数字雕刻
handler012 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
zhouwy1132 小时前
Linux进程与线程编程详解
linux·c++
A7bert7773 小时前
【YOLOv8pose部署至RDK X5】模型训练→转换bin→Sunrise 5部署
c++·python·深度学习·yolo·目标检测
li1670902704 小时前
第二十七章:智能指针
c语言·数据结构·c++·visual studio
王老师青少年编程4 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii