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();
相关推荐
呱呱巨基2 分钟前
Docker 基础概念学习
linux·c++·学习·docker
breeze jiang1 小时前
JavaScript 单例模式:用静态属性保证 Popup 只创建一次
开发语言·javascript·单例模式
郝学胜_神的一滴1 小时前
C++20 高级编程 001:从极简HelloWorld到现代类型体系全梳理
c++·后端
Lazionr2 小时前
list容器详解——双向链表的封装与使用
数据结构·c++·链表·list
Nil2082 小时前
C++ emplace_back 与 push_back 详解
开发语言·c++
漂流瓶jz2 小时前
UVA-12174 Shuffle的播放记录 题解答案代码 算法竞赛入门经典第二版
数据结构·c++·算法·图论·滑动窗口·算法竞赛入门经典·uva
Darkwanderor2 小时前
线程概念和Linux线程
linux·c语言·c++
lingran__2 小时前
C++ 高阶数据结构:红黑树万字详解|完整原理推导 + 插入实现 + 完整性校验【STL 底层】
数据结构·c++·面试·红黑树·二叉搜索树·平衡二叉树·stl底层
东东最爱敲键盘2 小时前
day9.C++多态之虚函数
开发语言·c++
meilindehuzi_a2 小时前
WebGPU DeepSeek项目实战(二):用单例模式加载Tokenizer并转发下载进度
单例模式·typescript·react