【C++几种单例模式解读及实现方式】

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

单例模式几乎是每种语言都不可少的一种设计模式,今天就针对C++语言来解读下集中单例模式,并给出代码说明。


一、单例是什么?

单例就是单实例,简而言之内存中只存在这一个实例,在很多场景下很有用,比如线程池、内存池等等。

二、解读

按照创建时机可以分为饿汉式和懒汉式。所谓饿汉式就是程序启动的时候立即创建,饿汉式就是需要的时候创建。

1.懒汉式

懒汉式顾名思义,不会主动创建,直到主动获取才创建。懒汉式又主要分为两种,线程安全和线程不安全。接下来分别介绍这几种和提供源代码示例。

简单模式:

cpp 复制代码
class Singleton {
        static Singleton *getInstance() {
            if (instance == nullptr) {
                instance = new Singleton();
            }
            return instance;
        }

    private:
        static Singleton *instance;
    };

    Singleton *Singleton::instance = nullptr;

优点:性能和饿汉式差不多,没有锁,性能不会损失
缺点:线程不安全
总结:需要自己确保线程安全,比如启动线程前就要创建好,避免竞争条件产生

线程安全:

cpp 复制代码
#include <mutex>
class Singleton {
    public:
        static Singleton *getInstance() {
            std::lock_guard<std::mutex> lock(mtx);
            if (instance == nullptr) {
                instance = new Singleton();
            }
            return instance;
        }

    private:
        static Singleton *instance;
        static std::mutex mtx;
    };

    Singleton *Singleton::instance = nullptr;
    std::mutex Singleton::mtx;

优点:线程安全,随时随地都是安全的
缺点:性能差,每次获取实例都要上锁
总结:不建议使用

双重检查锁:

cpp 复制代码
#include <mutex>
class Singleton {
    public:
        static Singleton *getInstance() {
            if (instance == nullptr) {
                std::lock_guard<std::mutex> lock(mtx);
                if (instance == nullptr) {
                    instance = new Singleton();
                }
            }
            return instance;
        }

    private:
        static Singleton *instance;
        static std::mutex mtx;
    };

    Singleton *Singleton::instance = nullptr;
    std::mutex Singleton::mtx;

优点:线程安全,性能比上面的强
缺点:性能有轻微损耗,只有第一次创建才需要上锁,后面访问不需要上锁
总结:强烈推荐,既解决了线程安全问题,又解决了性能问题

2.饿汉式

不会产生数据竞争,程序启动时就创建和,在竞争条件出现之前创建完成,所以先天是线程安全的。

cpp 复制代码
class Singleton {
    public:
        static Singleton &getInstance() {
            return instance;
        }

    private:
        static Singleton instance;
    };

    Singleton Singleton::instance;

优点:线程安全,性能最好
缺点:无论需不需要都会创建,会浪费资源
总结:可用但不太推荐

3.static变量特性

C++11之后static修饰的局部变量有一个很重要的特性:初始化的时候会自动加锁,这个特性是编译器负责维护的。所以可以借用这个特性开发一个单例模式,一定是线程安全的,而且提供不俗的性能。

注:这个单例又叫米尔斯单例(Meyers' Singleton)

cpp 复制代码
class Singleton {
    public:
        static Singleton &getInstance() {
            static Singleton instance;
            return instance;
        }

    private:
        Singleton() {}

    public:
        Singleton(const Singleton &) = delete;

        Singleton &operator=(const Singleton &) = delete;
    };

优点:线程安全,性能不俗
缺点:初始化上锁略微拖慢性能,后面正常获取,C++11以后版本才行
总结:极其推荐

4.call_once特性

除了米尔斯单例,还有一个C++11的新特性可以设计单例,这个特性我们平常容易忽视,那就是call_once。它也是一种线程同步机制,C++保证call_once保护的方法只被调用一次。

cpp 复制代码
#include <atomic>
class Singleton {
    public:
        static Singleton& getInstance(){
            std::call_once(flag,[]{
                instance = new Singleton();
            });
            return *instance;
        }
    private:
        static Singleton* instance;
        static std::once_flag flag;
    };
    Singleton* Singleton::instance = nullptr;
    std::once_flag Singleton::flag;

优点:线程安全,性能不俗,针对方法可以同时初始化多个资源
缺点:第一次调用拖慢性能,后面就不会了,C++11以后版本才行
总结:和米尔斯很像,但是实现比米尔斯复杂,推荐指数放在米尔斯单例之后


总结

1、C++11以上版本优先使用米尔斯单例,比双重检查锁性能强

2、其他场景建议双重检查锁,call_once酌情使用

相关推荐
黑妹天下第一乖3 分钟前
第 05 讲:阿加犀 AidCV 图像处理加速与 OpenCV 一致开发实战
开发语言·图像处理·人工智能·嵌入式硬件·数码相机·opencv·计算机视觉
言乐63 分钟前
Python基于关键词分拣快递(适用于电商与货运代理等)
开发语言·python·django·virtualenv·pygame
一条破秋裤9 分钟前
01_字符设备基本概念_从分类到LED控制链路
开发语言·php
朝朝辞暮i16 分钟前
C++ 第 40 章:ROS2 Publisher + Timer + Subscriber + Callback + Executor 完整闭环
开发语言·c++·算法·ros2
专注仿真27 分钟前
决策树的算法
开发语言·高性能算法
Evand J28 分钟前
【电机滤波例程2】负载转矩增广扩展卡尔曼滤波(EKF)原理与MATLAB例程:五维PMSM状态与负载阶跃估计。订阅专栏后可查看完整代码
开发语言·matlab·电机·ekf·卡尔曼滤波
清水白石00836 分钟前
Python 对象模型深度解析:从“一切皆对象”到 id、type、isinstance 底层机制与小整数缓存原理
开发语言·python·缓存
燕落南枝41 分钟前
c语言 洛谷P1765手机题解
c语言·开发语言
Rosanci42 分钟前
Codex 下载与本地部署实战:从安装到运行全流程指南
开发语言·前端·算法·chatgpt·codex
zhangzeyuaaa1 小时前
深入 Ruby:Block、Proc、Lambda 核心区别与最佳实践
开发语言·前端·ruby