单例模式-创建型

一、单例模式

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:***没有完美的设计模式,只有更适合该场景的模式;在使用单例模式,需权衡利弊
相关推荐
linweidong2 小时前
C++ 模块化编程(Modules)在大规模系统中的实践难点?
linux·前端·c++
半桔7 小时前
【IO多路转接】高并发服务器实战:Reactor 框架与 Epoll 机制的封装与设计逻辑
linux·运维·服务器·c++·io
HABuo8 小时前
【linux文件系统】磁盘结构&文件系统详谈
linux·运维·服务器·c语言·c++·ubuntu·centos
我在人间贩卖青春8 小时前
C++之多重继承
c++·多重继承
m0_736919109 小时前
C++代码风格检查工具
开发语言·c++·算法
2301_7634724610 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
阿猿收手吧!10 小时前
【C++】std::promise原理与实战解析
c++
m0_7066532311 小时前
分布式系统安全通信
开发语言·c++·算法
Zach_yuan11 小时前
深入浅出 JSONCpp
linux·服务器·网络·c++
寻寻觅觅☆12 小时前
东华OJ-基础题-104-A == B ?(C++)
开发语言·c++