单例模式-创建型

一、单例模式

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:***没有完美的设计模式,只有更适合该场景的模式;在使用单例模式,需权衡利弊
相关推荐
明洞日记2 小时前
【VTK手册024】高效等值面提取:vtkFlyingEdges3D 详解与实战
c++·图像处理·vtk·图形渲染
遥望九龙湖2 小时前
3.析构函数
开发语言·c++
qq_479875432 小时前
systemd-resolved.service实验实战3
linux·服务器·c++
闻缺陷则喜何志丹3 小时前
【图论 组合数学】P10912 [蓝桥杯 2024 国 B] 数星星|普及+
c++·数学·蓝桥杯·图论
Jeff-Nolan3 小时前
C++运算符重载
java·开发语言·c++
YouEmbedded3 小时前
解码智能指针
开发语言·c++·unique_ptr·shared_ptr·auto_ptr·weak_ptr
神仙别闹3 小时前
基于QT(C++)实现(图形界面)连连看
java·c++·qt
NZT-483 小时前
C++基础笔记(三)链表list
c++·笔记·链表
_Voosk3 小时前
C指针存储字符串为何不能修改内容
c语言·开发语言·汇编·c++·蓝桥杯·操作系统