C++单例模式

局部静态变量方式

cpp 复制代码
//通过静态成员变量实现单例
//懒汉式
class Single2
{
private:
    Single2()
    {
    }
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;
public:
    static Single2 &GetInst()
    {
        static Single2 single;
        return single;
    }
};

一个经典的懒汉式单例模式实现,通过静态成员变量来确保 Single2 类只有一个实例。这个实现是正确的,但有几点需要注意:

  1. 线程安全性 :在C++11及更高版本中,局部静态变量(如 GetInst 方法中的 static Single2 single)的初始化是线程安全的。因此,这个实现是线程安全的,不会在多线程环境中引发问题。

  2. 构造函数和赋值运算符的删除 :你通过 delete 删除了拷贝构造函数和赋值运算符,这可以防止在外部创建类的拷贝或通过赋值创建多个实例,这是单例模式的正确做法。

  3. 懒汉式单例的使用 :懒汉式单例在首次调用 GetInst 时才会创建实例,这样可以避免程序启动时不必要的内存消耗。

在C++11以下的版本中,使用你提供的懒汉式单例实现可能会遇到线程安全问题。具体来说,问题出现在静态局部变量 single 的初始化上。

线程安全问题

在C++11之前,局部静态变量的初始化不是线程安全的。如果多个线程同时首次调用 GetInst 方法,有可能会发生竞态条件(race condition),导致创建出多个 Single2 实例,破坏了单例模式的初衷。

解决方法

如果需要在C++11以下的版本中确保线程安全,可以考虑以下几种方法:

  1. 双重检查锁定(Double-Checked Locking)
cpp 复制代码
class Single2
{
private:
    static Single2* instance;
    static std::mutex mtx;
    Single2() {}
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;

public:
    static Single2* GetInst()
    {
        if (instance == nullptr) // 第一次检查
        {
            std::lock_guard<std::mutex> lock(mtx); // 加锁
            if (instance == nullptr) // 第二次检查
            {
                instance = new Single2();
            }
        }
        return instance;
    }
};

Single2* Single2::instance = nullptr;
std::mutex Single2::mtx;
  1. 静态成员变量初始化(线程不安全版本)

如果你确定单例只会在单线程环境下使用,可以使用以下更简单的方式:

cpp 复制代码
class Single2
{
private:
    static Single2* instance;
    Single2() {}
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;

public:
    static Single2* GetInst()
    {
        if (instance == nullptr)
        {
            instance = new Single2();
        }
        return instance;
    }
};

Single2* Single2::instance = nullptr;

这种实现不适用于多线程环境。

C++11 引入了一些新的特性和改进,其中之一就是保证了局部静态变量的线程安全性。这意味着在 C++11 及更高版本中,局部静态变量的初始化是由编译器自动处理的,并且保证了这种初始化过程在多线程环境下是安全的。

C++11 之前的问题

在 C++11 之前,当多个线程同时首次访问一个局部静态变量时,可能会出现竞态条件(Race Condition),导致多个线程同时进入变量的初始化过程。由于这个初始化过程没有得到线程的同步保护,可能会导致初始化多次,进而破坏单例模式的唯一性要求。

C++11 的改进

C++11 标准引入了以下保证:

  • 局部静态变量的初始化是线程安全的:C++11 规定,如果多个线程试图同时初始化同一个局部静态变量,只有一个线程会成功初始化它,而其他线程会被阻塞,直到初始化完成。这就确保了静态局部变量在多线程环境下也只会被初始化一次。

  • 懒汉式单例的线程安全性:由于局部静态变量的初始化已经是线程安全的,在 C++11 及更高版本中,可以安全地使用局部静态变量来实现懒汉式单例模式,无需额外的锁机制来防止竞态条件。

C++11 线程安全的实现机制

在底层,C++11 通过引入新的机制,确保了局部静态变量初始化的线程安全。这些机制包括:

  1. 线程同步机制:C++11 在初始化局部静态变量时,使用了编译器提供的同步机制(如锁、屏障等)来确保在多线程环境下,只有一个线程可以执行初始化代码。

  2. 按需初始化:C++11 确保局部静态变量在首次使用时才会被初始化,这种方式不仅节省资源,还保证了多线程环境中的唯一性。

相关推荐
Scott9999HH5 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔5 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社6 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海6 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖7 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai10 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说11 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json