C++奇迹之旅:C++的单例模式

embedded/2025/3/3 21:49:26/

请添加图片描述

文章目录

      • 📝 一、单例模式的核心原则
      • 二、基础实现(懒汉式,线程不安全)
        • 问题:
      • 三、线程安全的懒汉式(双重检查锁定)
        • 特点:
      • 四、饿汉式(线程安全)
        • 特点:
      • 五、C++11 后的现代实现(Meyers' Singleton)
        • 优势:
      • 六、单例模式的销毁问题
        • 注意:
      • 七、单例模式的优缺点
      • 八、适用场景
      • 九、完整示例(现代实现)
        • 输出:
      • 完整代码实现
      • 输出结果
      • 关键点说明
      • 改进建议
  • 🚩总结



在 C++ 中,单例模式(Singleton Pattern) 是一种创建型设计模式,其核心目标是确保一个类只有一个实例,并提供一个全局访问点。它常用于管理全局唯一的资源(如配置、日志系统、线程池等)。以下是单例模式的详细实现和分析:


📝 一、单例模式的核心原则

  1. 私有化构造函数:禁止外部通过 new 创建实例。
  2. 禁用拷贝和赋值:防止通过拷贝或赋值生成新对象。
  3. 静态方法获取实例:通过类内部的静态方法控制实例的创建和访问。
  4. 全局唯一性:无论从何处访问,始终返回同一个实例。

二、基础实现(懒汉式,线程不安全)

最简单的单例实现,但存在多线程安全问题:

class Singleton {
private:static Singleton* instance;  // 静态成员保存唯一实例Singleton() {}               // 私有构造函数Singleton(const Singleton&) = delete;            // 禁用拷贝构造Singleton& operator=(const Singleton&) = delete; // 禁用赋值操作符public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}
};// 初始化静态成员
Singleton* Singleton::instance = nullptr;
问题:
  • 线程不安全:若多个线程同时调用 getInstance(),可能创建多个实例。

三、线程安全的懒汉式(双重检查锁定)

通过双重检查锁定(Double-Checked Locking) 优化性能和线程安全:

#include <mutex>class Singleton {
private:static Singleton* instance;static std::mutex mtx;  // 互斥锁Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static Singleton* getInstance() {if (instance == nullptr) {              // 第一次检查(避免每次加锁)std::lock_guard<std::mutex> lock(mtx);if (instance == nullptr) {          // 第二次检查(确保线程安全)instance = new Singleton();}}return instance;}
};// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
特点:
  • 双重检查:减少锁的使用频率,提升性能。
  • 互斥锁:确保多线程环境下只创建一个实例。

四、饿汉式(线程安全)

在程序启动时(静态初始化阶段)直接创建实例,天然线程安全:

class Singleton {
private:static Singleton* instance;Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static Singleton* getInstance() {return instance;}
};// 在类外部直接初始化实例
Singleton* Singleton::instance = new Singleton();
特点:
  • 提前初始化:实例在程序启动时创建,可能增加启动时间。
  • 线程安全:静态初始化在 C++11 后是线程安全的。

五、C++11 后的现代实现(Meyers’ Singleton)

利用局部静态变量的特性,C++11 保证其初始化是线程安全的:

class Singleton {
private:Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static Singleton& getInstance() {static Singleton instance;  // C++11 保证线程安全的局部静态初始化return instance;}
};
优势:
  • 简洁性:无需手动管理锁或指针。
  • 自动析构:实例在程序结束时自动销毁,避免内存泄漏。
  • 返回引用:避免外部误操作指针(如 delete)。

六、单例模式的销毁问题

若单例实例需要显式释放资源,可以添加销毁方法:

class Singleton {private:Singleton() {}Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:static void destroyInstance() {if (instance != nullptr) {delete instance;instance = nullptr;}}
};
注意:
  • 谨慎使用:单例的生命周期通常与程序一致,手动销毁可能引入风险。

七、单例模式的优缺点

优点缺点
全局唯一访问点,方便资源管理隐藏依赖关系,增加代码耦合性
节省内存和资源难以扩展(如需要多个实例时需重构)
延迟初始化(懒汉式)多线程环境下需处理同步问题
单元测试困难(全局状态难以隔离或模拟)

八、适用场景

  1. 配置管理:全局唯一的配置对象。
  2. 日志系统:统一管理日志写入。
  3. 数据库连接池:共享数据库连接资源。
  4. 硬件访问:如打印机、GPU 设备等独占资源。

九、完整示例(现代实现)

#include <iostream>class Logger {
private:Logger() {}  // 私有构造函数Logger(const Logger&) = delete;Logger& operator=(const Logger&) = delete;public:static Logger& getInstance() {static Logger instance;return instance;}void log(const std::string& message) {std::cout << "Log: " << message << std::endl;}
};int main() {Logger::getInstance().log("Application started");Logger::getInstance().log("Processing data...");return 0;
}
输出:
Log: Application started
Log: Processing data...

以下是一个完整的单例模式实现,包含实例的显式销毁方法,并通过双重检查锁定确保线程安全:


完整代码实现

#include <iostream>
#include <mutex>class Singleton {
private:static Singleton* instance;  // 静态单例实例指针static std::mutex mtx;       // 互斥锁(用于线程安全)// 私有化构造函数和析构函数Singleton() {std::cout << "Singleton instance created." << std::endl;}~Singleton() {std::cout << "Singleton instance destroyed." << std::endl;}// 禁用拷贝和赋值Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;public:// 获取单例实例(线程安全)static Singleton* getInstance() {if (instance == nullptr) {              // 第一次检查(避免每次加锁)std::lock_guard<std::mutex> lock(mtx);if (instance == nullptr) {          // 第二次检查(确保线程安全)instance = new Singleton();}}return instance;}// 显式销毁单例实例(线程安全)static void destroyInstance() {std::lock_guard<std::mutex> lock(mtx);  // 加锁保护if (instance != nullptr) {delete instance;instance = nullptr;  // 重置指针,避免悬空引用}}// 示例方法void doSomething() {std::cout << "Doing something..." << std::endl;}
};// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;// 使用示例
int main() {// 获取单例实例并操作Singleton* s1 = Singleton::getInstance();s1->doSomething();// 显式销毁实例Singleton::destroyInstance();// 再次获取实例(会创建新实例)Singleton* s2 = Singleton::getInstance();s2->doSomething();return 0;
}

输出结果

Singleton instance created.
Doing something...
Singleton instance destroyed.
Singleton instance created.
Doing something...

关键点说明

  1. 线程安全设计

    • 使用 std::mutexstd::lock_guard 确保 getInstance()destroyInstance() 的线程安全。
    • 双重检查锁定(Double-Checked Locking)减少锁的竞争,提升性能。
  2. 显式销毁逻辑

    • destroyInstance() 方法通过 delete 释放实例内存,并将 instance 指针置为 nullptr
    • 销毁后再次调用 getInstance() 会创建新实例(根据需求可修改为禁止重新创建)。
  3. 资源管理

    • 析构函数 ~Singleton() 可以释放单例持有的资源(如文件句柄、网络连接)。
    • 若不显式调用 destroyInstance(),单例实例将在程序结束时由操作系统回收(可能不符合预期)。

改进建议

若需禁止销毁后重新创建实例,可增加一个标志位:

class Singleton {
private:static bool destroyed;  // 新增销毁标志public:static Singleton* getInstance() {if (destroyed) {throw std::runtime_error("Singleton instance has been destroyed.");}// ...(原有逻辑)}static void destroyInstance() {// ...(原有逻辑)destroyed = true;  // 标记已销毁}
};// 初始化静态成员
bool Singleton::destroyed = false;


🚩总结

单例模式在 C++ 中通过控制实例化过程确保全局唯一性。现代 C++ 推荐使用 局部静态变量 实现(Meyers’ Singleton),既简洁又线程安全。需根据实际需求权衡懒汉式与饿汉式,并注意避免滥用单例导致代码耦合性增加。
请添加图片描述


http://www.ppmy.cn/embedded/169720.html

相关文章

Elasticsearch:使用阿里云 AI 服务进行嵌入和重新排名

作者&#xff1a;来自 Elastic Toms Mura 将阿里云 AI 服务功能与 Elastic 结合使用。 更多阅读&#xff0c;请参阅 “Elasticsearch&#xff1a;使用阿里 infererence API 及 semantic text 进行向量搜索”。 在本文中&#xff0c;我们将介绍如何将阿里云 AI 功能与 Elastics…

江协科技/江科大-51单片机入门教程——P[1-3] 单片机及开发板介绍

前言&#xff1a;本节主要的任务是了解一下 51 单片机和所用的普中51开发板。 目录 一、单片机介绍 二、单片机的应用领域 三、STC89C52单片机 四、命名规则 五、单片机内部拆解 六、单片机内部结构图 七、单片机管脚图 八、单片机最小系统 九、开发板介绍 十、开发…

DeepSeek 助力 Vue3 开发:打造丝滑的悬浮按钮(Floating Action Button)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

51单片机制作彩屏触摸小电子琴STC32G12K128+RA6809+彩屏1024x600

分享一个案例&#xff0c;用51单片机制作彩屏触摸小电子琴&#xff0c;很好玩的一个实验项目&#xff0c;适合广大爱好者探究&#xff01; 硬件需求&#xff1a; 1.STC32G12K128 单片机–我们已制作开发板 2.RA6809/RA8889 液晶控制芯片–我们已制作RA6809开发板 3.彩屏&…

10. 作者去换监控源了,不知道什么原因,zabbix自定义监控无法获取

作者去换监控源了&#xff0c;不知道什么原因&#xff0c;zabbix自定义监控无法获取 通过网络抓包&#xff0c;抓出了两个空值&#xff0c;也没有必要非杠&#xff0c;我先去研究普罗米修了&#xff0c;研究明白了在继续更新&#xff0c;或者又大神知道原因的话也可以给我留言…

未来该如何选择编程语言?

随着技术的飞速发展&#xff0c;编程语言的选择变得越来越重要。无论是初学者还是资深开发者&#xff0c;选择一门适合未来发展的编程语言都至关重要。以下是一些关键因素和建议&#xff0c;帮助您做出明智的选择。 --- #### 1. **明确目标和需求** - **职业方向**&#x…

DeepSeek效应初现:Grok-3补刀ChatGPT,OpenAI已在ICU?

嘿&#xff0c;技术小伙伴们&#xff01;今天咱们聊聊最近在AI界引发轰动的新闻——DeepSeek和xAI相继用R1和Grok-3证明了预训练Scaling Law并非OpenAI的护城河。这意味着什么呢&#xff1f;让我们一探究竟&#xff01; 开场白 首先&#xff0c;让我们看看最新的“全能冠军”…

torch.einsum 的 10 个常见用法详解以及多头注意力实现

torch.einsum 是 PyTorch 提供的一个高效的张量运算函数&#xff0c;能够用紧凑的 Einstein Summation 约定&#xff08;Einstein Summation Convention, Einsum&#xff09;描述复杂的张量操作&#xff0c;例如矩阵乘法、转置、内积、外积、批量矩阵乘法等。 1. 基本语法 tor…