C++:单例模式

1.什么是单例

单,就是单个的意思,例,就是实例化。所谓单例模式,指的就是程序运行期间,在全局范围内,有且只有一个类的实例化存在。它其实是一种设计思想,和语言没有关系。只要按照这种设计思想,哪种语言都可以实现自己的单例模式。

单例模式用的最多的场景就是工具类,节省资源,全局调用。

而为了保证只有一个实例存在,在设计单例时候,就要将类的构造函数私有化,防止外部创建。同时将类的拷贝构造函数和赋值操作符删除,防止复制和赋值。

单例模式的实现方式有三种。C++11之前,主要通过饿汉式或懒汉式实现,C++11之后,通过静态局部变量实现。静态局部变量这种方式,既简洁又高效。

2.饿汉式

饿汉式是通过类的静态成员变量实现的。因为静态成员变量的初始化,是在main函数加载前完成的,所以它天然是线程安全的。

代码举例如下:

头文件:EagerSingleton.h

cpp 复制代码
//EagerSingleton.h

#pragma once

#include <iostream>
using namespace std;

class EagerSingleton
{
public:
    static EagerSingleton& getInstance() {
        return instance;
    }

    void doSomething() {
        cout << "EagerSingleton 做事..." << endl;
        
    }

private:
    // 私有构造函数
    EagerSingleton() { cout << "EagerSingleton 构造函数" << endl; }

    // 拷贝构造和赋值操作符删除,防止复制
    EagerSingleton(const EagerSingleton&) = delete;
    EagerSingleton& operator=(const EagerSingleton&) = delete;

    // 静态实例在类外定义,程序启动时即初始化
    static EagerSingleton instance;

};

源文件:EagerSingleton.cpp

cpp 复制代码
#include "EagerSingleton.h"


EagerSingleton EagerSingleton::instance;

调用方式:

cpp 复制代码
EagerSingleton& s1 = EagerSingleton::getInstance()

3.懒汉式

懒汉式又可分为两种,一种是简单版本,一种是双重检测+锁版本。他俩唯一区别就是第一个不是线程安全,第二个是线程安全的。

简单版本代码举例

头文件:LazySingleton.h

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;

class LazySingleton
{
public:
    static LazySingleton* getInstance() {
        if (instance == nullptr) {   // 多线程下可能同时进入
            instance = new LazySingleton();
        }
        return instance;
    }

    void doSomething() {
        cout << "LazySingleton 做事..." << endl;

    }

private:
    // 私有构造函数
    LazySingleton() { cout << "LazySingleton 构造函数" << endl; }

    // 拷贝构造和赋值操作符删除,防止复制
    LazySingleton(const LazySingleton&) = delete;
    LazySingleton& operator=(const LazySingleton&) = delete;

    // 静态实例在类外定义,程序启动时即初始化
    static LazySingleton* instance;
};

源文件:LazySingleton.cpp

cpp 复制代码
#include "LazySingleton.h"

LazySingleton* LazySingleton::instance = nullptr;

双重检测+锁版本代码举例

头文件:LazySingletonDCL.h

cpp 复制代码
#pragma once

#include <iostream>
#include <mutex>
using namespace std;

class LazySingletonDCL
{
public:
    static LazySingletonDCL* getInstance() {
        // 第一层判断:避免每次加锁损耗性能
        if (instance == nullptr) {
            lock_guard<mutex> lock(mtx);
            // 第二层判断:防止多线程等待锁后重复创建
            if (instance == nullptr) {
                instance = new LazySingletonDCL();
            }
        }
        return instance;
    }

    void doSomething() {
        cout << "LazySingletonDCL 做事..." << endl;

    }

private:
    // 私有构造函数
    LazySingletonDCL() { cout << "LazySingletonDCL 构造函数" << endl; }

    // 拷贝构造和赋值操作符删除,防止复制
    LazySingletonDCL(const LazySingletonDCL&) = delete;
    LazySingletonDCL& operator=(const LazySingletonDCL&) = delete;

    // 静态实例在类外定义,程序启动时即初始化
    static LazySingletonDCL* instance;
    static mutex mtx;
};

源文件:LazySingletonDCL.cpp

cpp 复制代码
#include "LazySingletonDCL.h"

LazySingletonDCL* LazySingletonDCL::instance = nullptr;
mutex LazySingletonDCL::mtx;

调用方式

cpp 复制代码
LazySingleton* s1 = LazySingleton::getInstance();

4.静态局部变量方式

这种方式是C++11后才能使用,是目前单例的最优解。

代码举例如下:

头文件:SingletonMeyer.h

cpp 复制代码
#pragma once

#include <iostream>
using namespace std;

class SingletonMeyer
{
public:
    static SingletonMeyer& getInstance() {
        // 第一次调用才创建,线程安全
        static SingletonMeyer instance;
        return instance;
    }

    void doSomething() {
        cout << "SingletonMeyer 做事..." << endl;

    }

private:
    // 私有构造函数
    SingletonMeyer() { cout << "SingletonMeyer 构造函数" << endl; }

    // 拷贝构造和赋值操作符删除,防止复制
    SingletonMeyer(const SingletonMeyer&) = delete;
    SingletonMeyer& operator=(const SingletonMeyer&) = delete;


};

源文件:SingletonMeyer.cpp

cpp 复制代码
#include "SingletonMeyer.h"

//相关函数也可以在头文件中声明,在这里实现。

调用方式

cpp 复制代码
SingletonMeyer& ins = SingletonMeyer::getInstance();
ins.doSomething();
相关推荐
同勉共进10 小时前
记一例 vibe coding + gcc bug 导致的线程池死锁问题
c++·线程池·gcc·死锁·vibe coding
我叫洋洋10 小时前
C ++ [ hello world ]
c语言·c++·算法
王维同学14 小时前
[自学][Windows C++]RunOnceEx 注册表分组键的安全遍历
c++·windows·安全·开源
Scott9999HH15 小时前
告别流量波动玄学!从法拉第电磁定律到底层 C/C++ 流量累积算法,深度解密工业级电磁流量计选型与开发
c语言·c++·算法
脱胎换骨-军哥15 小时前
C++/Rust无缝互操作:混合系统新常态
开发语言·c++·rust
旖-旎17 小时前
LeetCode 279:完全平方数(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
胖大和尚18 小时前
C++ 多线程编程的实现方式
c++·thread
在水一缸19 小时前
深入浅出 Catch2:现代 C++ 测试框架的优雅实践
开发语言·c++·单元测试·log4j·测试框架·catch2
2401_8414956419 小时前
【数据结构】B*树
数据结构·c++·b树·算法·删除·插入·三分分裂