单例模式-创建型

一、单例模式

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:***没有完美的设计模式,只有更适合该场景的模式;在使用单例模式,需权衡利弊
相关推荐
在路上看风景1 天前
19. 成员初始化列表和初始化对象
c++
zmzb01031 天前
C++课后习题训练记录Day98
开发语言·c++
念风零壹1 天前
C++ 内存避坑指南:如何用移动语义和智能指针解决“深拷贝”与“内存泄漏”
c++
孞㐑¥1 天前
算法——BFS
开发语言·c++·经验分享·笔记·算法
MZ_ZXD0011 天前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
A星空1231 天前
一、Linux嵌入式的I2C驱动开发
linux·c++·驱动开发·i2c
凡人叶枫1 天前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
会叫的恐龙1 天前
C++ 核心知识点汇总(第六日)(字符串)
c++·算法·字符串
小糯米6011 天前
C++顺序表和vector
开发语言·c++·算法
独望漫天星辰1 天前
C++ 多态深度解析:从语法规则到底层实现(附实战验证代码)
开发语言·c++