0

0

什么时候应该使用C++单例模式 线程安全实现与适用场景分析

P粉602998670

P粉602998670

发布时间:2025-07-18 09:07:01

|

547人浏览过

|

来源于php中文网

原创

单例模式用于确保一个类在整个程序运行期间只有一个实例。它适用于需要全局唯一资源管理、配置中心或控制硬件设备的场景,但应避免过度使用以防止代码耦合和测试困难。实现方式包括:1. 懒汉式(线程安全,延迟初始化,需加锁);2. 饿汉式(程序启动时创建,无需加锁);3. meyers' singleton(利用c++++11静态局部变量线程安全,简洁推荐)。此外,需防范反射、序列化及多线程破坏单例,并注意在需要多实例、依赖注入或测试困难时避免使用该模式。游戏引擎中常用于资源、输入和日志管理器。

什么时候应该使用C++单例模式 线程安全实现与适用场景分析

单例模式,简单来说,就是确保一个类在整个程序运行期间只有一个实例。什么时候用?嗯,当我们需要一个全局唯一的资源管理器、配置中心,或者需要控制某些硬件设备时,单例模式就派上用场了。但要注意,过度使用单例可能会导致代码耦合度过高,测试困难,所以要谨慎。

什么时候应该使用C++单例模式 线程安全实现与适用场景分析

解决方案:

什么时候应该使用C++单例模式 线程安全实现与适用场景分析

单例模式的核心在于控制实例的创建,并提供一个全局访问点。在 C++ 中,实现单例模式有几种常见方法,但线程安全是必须要考虑的。

立即学习C++免费学习笔记(深入)”;

1. 懒汉式(Lazy Initialization) - 线程安全

什么时候应该使用C++单例模式 线程安全实现与适用场景分析

这是最常见的单例模式实现方式。延迟初始化,只有在第一次使用时才创建实例。为了保证线程安全,需要使用互斥锁。

#include 
#include 

class Singleton {
private:
    Singleton() {
        std::cout << "Singleton created!" << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton destroyed!" << std::endl;
    }
    static Singleton* instance;
    static std::mutex mutex;

public:
    static Singleton* getInstance() {
        std::lock_guard lock(mutex); // RAII 风格的锁
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }

    void doSomething() {
        std::cout << "Singleton is doing something..." << std::endl;
    }

    // 禁止拷贝构造和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

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

int main() {
    Singleton* s1 = Singleton::getInstance();
    s1->doSomething();
    Singleton* s2 = Singleton::getInstance(); // 返回同一个实例
    return 0;
}

关键点:

  • instance 是静态成员变量,用于保存唯一的实例。
  • mutex 是互斥锁,用于保证线程安全。
  • getInstance() 是静态方法,用于获取单例实例。使用了 std::lock_guard,确保在离开作用域时自动释放锁,防止死锁。
  • 拷贝构造函数和赋值运算符被删除,防止通过拷贝创建新的实例。

2. 饿汉式(Eager Initialization)

在程序启动时就创建实例,简单粗暴,但线程安全。

#include 

class Singleton {
private:
    Singleton() {
        std::cout << "Singleton created!" << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton destroyed!" << std::endl;
    }
    static Singleton instance; // 直接初始化

public:
    static Singleton* getInstance() {
        return &instance;
    }

    void doSomething() {
        std::cout << "Singleton is doing something..." << std::endl;
    }

    // 禁止拷贝构造和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

Singleton Singleton::instance; // 静态成员变量定义

int main() {
    Singleton* s1 = Singleton::getInstance();
    s1->doSomething();
    Singleton* s2 = Singleton::getInstance(); // 返回同一个实例
    return 0;
}

关键点:

  • instance 在类定义时直接初始化。
  • 不需要互斥锁,天然线程安全。

3. Meyers' Singleton

唱鸭
唱鸭

音乐创作全流程的AI自动作曲工具,集 AI 辅助作词、AI 自动作曲、编曲、混音于一体

下载

利用 C++11 的静态局部变量线程安全特性。这是我个人比较推荐的方式,代码简洁且线程安全。

#include 

class Singleton {
private:
    Singleton() {
        std::cout << "Singleton created!" << std::endl;
    }
    ~Singleton() {
        std::cout << "Singleton destroyed!" << std::endl;
    }

public:
    static Singleton& getInstance() {
        static Singleton instance; // 静态局部变量,C++11 保证线程安全
        return instance;
    }

    void doSomething() {
        std::cout << "Singleton is doing something..." << std::endl;
    }

    // 禁止拷贝构造和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

int main() {
    Singleton& s1 = Singleton::getInstance();
    s1.doSomething();
    Singleton& s2 = Singleton::getInstance(); // 返回同一个实例
    return 0;
}

关键点:

  • instance 是静态局部变量,只在第一次调用 getInstance() 时初始化。
  • C++11 标准保证静态局部变量的初始化是线程安全的,无需手动加锁。

单例模式破坏与应对策略

单例模式虽然简单,但也容易被破坏,例如通过反射(虽然 C++ 没有直接的反射机制,但可以通过其他方式模拟)、序列化/反序列化、多线程并发创建等。

  • 防止反射/模拟反射: 严格控制类的访问权限,将构造函数设为私有,并删除拷贝构造函数和赋值运算符。
  • 防止序列化/反序列化: 实现 Serializable 接口时要特别小心,可以重写 readResolve 方法来返回单例实例。
  • 防止多线程并发创建: 使用线程安全的单例模式实现方式,如懒汉式加锁或 Meyers' Singleton。

单例模式与其他设计模式的比较

单例模式经常与工厂模式、建造者模式等一起使用。

  • 单例模式 vs. 工厂模式: 单例模式保证只有一个实例,而工厂模式用于创建对象,可以创建多个实例。工厂模式可以使用单例来管理工厂实例本身。
  • 单例模式 vs. 建造者模式: 单例模式用于控制实例数量,建造者模式用于创建复杂对象。

何时避免使用单例模式

单例模式并非银弹,过度使用会导致代码耦合度过高,测试困难。以下情况应避免使用单例模式:

  • 需要多个实例: 如果需要多个实例,显然不能使用单例模式。
  • 依赖注入更合适: 如果可以使用依赖注入来管理对象,则优先考虑依赖注入,而不是单例模式。
  • 测试困难: 单例模式会增加测试的难度,因为它创建全局状态,难以模拟和隔离。

单例模式在游戏引擎中的应用

在游戏引擎中,单例模式常用于管理全局资源,例如:

  • 资源管理器: 负责加载和管理游戏资源,如纹理、模型、音频等。
  • 输入管理器: 负责处理用户输入,如键盘、鼠标、触摸屏等。
  • 日志管理器: 负责记录游戏运行时的日志信息。

这些管理器通常只需要一个实例,因此使用单例模式非常合适。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1435

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

223

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

84

2025.10.17

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

989

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

50

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2025.12.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

471

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

107

2025.12.24

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 5.7万人学习

C 教程
C 教程

共75课时 | 3.8万人学习

C++教程
C++教程

共115课时 | 10.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号