C++中如何实现单例模式?

ops/2024/12/20 8:21:14/

单例模式(Singleton Pattern)是一种设计模式,旨在确保一个类只有一个实例,并提供全局访问点。在 C++ 中实现单例模式有多种方式,下面介绍几种常见的实现方法。

1. 饿汉式(线程安全)

饿汉式是指在程序启动时就创建单例对象。这种方式实现简单,但不适用于创建过程较为复杂的对象。

示例:饿汉式单例模式
#include <iostream>class Singleton {
private:static Singleton instance;  // 声明静态实例// 构造函数和拷贝构造函数私有,防止外部创建对象Singleton() {
        std::cout << "Singleton instance created!" << std::endl;}Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;public:// 提供公共的静态方法来访问单例static Singleton& getInstance() {return instance;}void showMessage() {
        std::cout << "Hello, Singleton!" << std::endl;}
};// 在类外定义静态实例
Singleton Singleton::instance;int main() {
    Singleton& singleton1 = Singleton::getInstance();
    singleton1.showMessage();    Singleton& singleton2 = Singleton::getInstance();
    singleton2.showMessage();return 0;
}
说明:
  • Singleton 类有一个静态成员 instance,在程序加载时就会创建它。
  • 构造函数、拷贝构造函数和赋值操作符是私有的,因此外部无法直接创建或拷贝 Singleton 对象。
  • getInstance() 提供了唯一的访问点来获取单例对象。

2. 懒汉式(线程不安全)

懒汉式是指在首次使用时才创建单例对象。这个方法的缺点是没有考虑线程安全性,可能在多线程环境下出现问题。

示例:懒汉式单例模式(线程不安全)
#include <iostream>class Singleton {
private:static Singleton* instance;// 构造函数和拷贝构造函数私有,防止外部创建对象Singleton() {
        std::cout << "Singleton instance created!" << std::endl;}Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;public:// 提供公共的静态方法来访问单例static Singleton* getInstance() {if (instance == nullptr) {
            instance = new Singleton();}return instance;}void showMessage() {
        std::cout << "Hello, Singleton!" << std::endl;}
};// 静态成员初始化为空指针
Singleton* Singleton::instance = nullptr;int main() {
    Singleton* singleton1 = Singleton::getInstance();
    singleton1->showMessage();    Singleton* singleton2 = Singleton::getInstance();
    singleton2->showMessage();return 0;
}
说明:
  • instance 是一个静态指针,首次调用 getInstance() 时创建单例对象。
  • 对于多线程程序,这种方式存在风险,因为多个线程可能同时通过 getInstance() 创建多个实例。

3. 线程安全的懒汉式(加锁)

为了确保在多线程环境下,懒汉式单例模式是线程安全的,可以使用互斥锁(mutex)来保证在多线程环境下的安全性。

示例:线程安全的懒汉式单例模式(加锁)
#include <iostream>
#include <mutex>class Singleton {
private:static Singleton* instance;static std::mutex mtx;// 构造函数和拷贝构造函数私有,防止外部创建对象Singleton() {
        std::cout << "Singleton instance created!" << 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;}void showMessage() {
        std::cout << "Hello, Singleton!" << std::endl;}
};// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;int main() {
    Singleton* singleton1 = Singleton::getInstance();
    singleton1->showMessage();    Singleton* singleton2 = Singleton::getInstance();
    singleton2->showMessage();return 0;
}
说明:
  • 通过在 getInstance() 方法中使用 std::mutex 进行加锁,确保在多线程环境下只有一个线程能创建单例对象。
  • 使用双重检查锁定模式:第一次检查 instance == nullptr 是为了避免不必要的加锁,第二次检查是在加锁后进行,确保只会有一个线程创建对象。

4. 使用 std::call_once 实现线程安全的单例模式

C++11 提供了 std::call_oncestd::once_flag,可以在多线程环境下以更加高效的方式实现线程安全的单例模式

示例:使用 std::call_once 实现线程安全的懒汉式单例
#include <iostream>
#include <mutex>class Singleton {
private:static Singleton* instance;static std::once_flag flag;// 构造函数和拷贝构造函数私有,防止外部创建对象Singleton() {
        std::cout << "Singleton instance created!" << std::endl;}Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;public:// 提供公共的静态方法来访问单例static Singleton* getInstance() {
        std::call_once(flag, []() {
            instance = new Singleton();});return instance;}void showMessage() {
        std::cout << "Hello, Singleton!" << std::endl;}
};// 静态成员初始化
Singleton* Singleton::instance = nullptr;
std::once_flag Singleton::flag;int main() {
    Singleton* singleton1 = Singleton::getInstance();
    singleton1->showMessage();    Singleton* singleton2 = Singleton::getInstance();
    singleton2->showMessage();return 0;
}
说明:
  • std::call_once 保证了某段代码(这里是创建单例对象)在多线程环境下只会执行一次,避免了线程安全的问题。

总结

  • 饿汉式:类加载时即创建单例对象,线程安全,适用于简单对象。
  • 懒汉式(线程不安全):只有在首次使用时才创建对象,不适用于多线程环境。
  • 懒汉式(线程安全):通过互斥锁保证线程安全,但性能较差,适用于多线程环境。
  • std::call_once:通过 std::call_once 实现线程安全且性能较优的懒汉式单例模式

http://www.ppmy.cn/ops/143427.html

相关文章

WPF ControlTemplate 控件模板

区别于 DataTemplate 数据模板&#xff0c;ControlTemplate 是控件模板&#xff0c;是为自定义控件的 Template 属性服务的&#xff0c;Template 属性类型就是 ControlTemplate。 演示&#xff0c; 自定义一个控件 MyControl&#xff0c;包含一个字符串类型的依赖属性。 pub…

BOE(京东方)“向新2025”年终媒体智享会首站落地上海 六大维度创新开启产业发展新篇章

12月17日,BOE(京东方)以“向新2025”为主题的年终媒体智享会在上海启动。正值BOE(京东方)新三十年的开局之年,活动全面回顾了2024年BOE(京东方)在各领域所取得的领先成果,深度解读了六大维度的“向新”发展格局,同时详细剖析了BOE(京东方)在智能制造领域的领先实践。BOE(京东方…

复习打卡大数据篇——Hadoop HDFS 01

目录 1. HDFS简介 2. HDFS基本操作 3. HDFS原理 1. HDFS简介 HDFS概念&#xff1a; HDFS是一个分布式的文件系统。分布式意味着多台机器存储&#xff0c;文件系统&#xff0c;就是用来存储文件、存储数据。是大数据最底层一个服务。 HDFS设计目标&#xff1a; 故障的检测…

ZZNUOJ 1601:字母序号(C/C++/Java)

题目描述 我们把字母 A-Z 分别编号为 1-26, 现在给你一个大写字母, 输出这个大写字母的序号。 输入 输入一个大写字母 输出 输出这个大写字母的序号 样例输入 C 样例输出 3 常见的ASCII值 ASCII表中可以记下部分特殊的值(十进制)(字母从A到Z,从a到z,ASCII值依次递增)

linux 下nmcli命令使用方法

1、nmcli 是 NetworkManager Command Line Interface 的缩写。 详细解释 NetworkManager: 是 Linux 上常用的网络管理工具&#xff0c;负责管理有线、无线、VPN 等网络连接。 Command Line Interface (CLI): 意味着 nmcli 是 NetworkManager 的命令行界面工具&#xff0c;提…

[Linux] 进程信号概念 | 信号产生

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 为什么我的课设这么难…

dbcat mysql 慢日志监控利器

dbcat mysql 慢日志监控利器 pt-query-digest 的问题DBCAT 特性核心特性1.集中监控&#xff0c;一目了然2.安装便捷&#xff0c;无复杂依赖3.无 Agent 代理&#xff0c;轻量级监控4 MySQL 慢日志远程实时监控5.数据可视化&#xff0c;一目了然6.安全性保障&#xff0c; 安装教程…

Flutter-底部分享弹窗(showModalBottomSheet)

showModalBottomSheet 构造函数的样式 Future<T?> showModalBottomSheet<T>({required BuildContext context, // 上下文对象&#xff0c;通常是当前页面的上下文bool isScrollControlled false, // 控制底部弹窗的大小&#xff0c;如果为…