C++:设计模式-单例模式

单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并且提供全局访问点。实现单例模式的关键是防止类被多次实例化,且能够保证实例的唯一性。常见的实现手法包括懒汉式饿汉式线程安全的懒汉式等。

1. 饿汉式(Eager Initialization)

饿汉式单例在程序启动时就创建实例,并且保证只有一个实例。适用于单例实例比较简单、没有资源消耗问题的情况。

class Singleton {
public:// 提供静态的访问方式static Singleton& getInstance() {return instance;  // 直接返回静态实例}private:Singleton() {}  // 构造函数私有化,防止外部创建实例~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton instance;  // 静态实例,程序启动时创建
};// 静态实例必须定义在类外
Singleton Singleton::instance;

注意
Singleton Singleton::instance;应在类的实现文件(.cpp 文件)中定义。如果静态成员变量的定义(如 Singleton Singleton::instance;)也放在头文件中,由于头文件可能被多个源文件包含,就会导致多重定义错误。

优点

  • 简单直接,保证了类的实例唯一。
  • 实例在程序启动时就被创建,不会受到多线程的影响。

缺点

  • 无法延迟实例化。即使单例没有被使用,实例也会在程序启动时创建,可能会浪费资源。

在饿汉式单例中,实例在程序启动时就被创建,因此你无需显式地调用 getInstance() 来创建对象。它是静态的,并且一开始就存在。你只能通过 getInstance() 方法来访问该实例。

以下是如何在代码中调用饿汉式单例的示例:

完整代码示例
#include <iostream>
using namespace std;class Singleton {
public:// 提供静态的访问方式static Singleton& getInstance() {return instance;  // 直接返回静态实例}void showMessage() {cout << "Singleton instance is working!" << endl;}private:Singleton() { cout << "Singleton Constructor Called!" << endl; }  // 构造函数私有化~Singleton() { cout << "Singleton Destructor Called!" << endl; }Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton instance;  // 静态实例,程序启动时创建
};// 静态实例必须定义在类外
Singleton Singleton::instance;int main() {// 通过调用 getInstance 来获取单例实例Singleton& s1 = Singleton::getInstance();s1.showMessage();// 由于我们不能复制实例,以下代码会编译错误:// Singleton s2 = Singleton::getInstance();  // 编译错误,不能复制单例// 单例是全局唯一的,只能通过 getInstance() 获取return 0;
}
代码解析:
  1. 静态实例定义

    • static Singleton instance; 在类内部声明了一个静态的实例,该实例在程序启动时被创建。
    • 静态变量 instance 的生命周期从程序开始直到程序结束,所以它是全局唯一的。
  2. getInstance() 方法

    • static Singleton& getInstance() 提供了一个全局的访问点,用来获取唯一的实例。这个方法返回一个对静态成员 instance 的引用。
  3. main() 中的使用

    • main() 函数中,我们通过 Singleton::getInstance() 获取了单例实例,并调用了实例的方法 showMessage()
    • 单例实例的构造函数在第一次调用 getInstance() 时自动执行,但我们并没有显式地创建 Singleton 对象。
输出:
Singleton Constructor Called!
Singleton instance is working!
注意事项:
  • 全局唯一Singleton::getInstance() 返回的是同一个对象,所以每次调用 getInstance() 都会得到相同的实例。
  • 构造函数:构造函数只在第一次调用 getInstance() 时执行一次,因此 Singleton Constructor Called! 只会打印一次。
  • 禁止复制:拷贝构造函数和赋值运算符被删除,避免了实例的复制。编译时如果尝试复制单例实例(如 Singleton s2 = Singleton::getInstance();),会导致编译错误。
总结:

通过 Singleton::getInstance() 方法获取单例实例,这是调用饿汉式单例的标准方式。由于饿汉式实例在程序启动时就被创建,所以你不需要显式地进行实例化操作。


2. 懒汉式(Lazy Initialization)

懒汉式是在需要实例时才创建实例,这样可以延迟实例化,提高资源的使用效率。基本实现没有线程安全,多个线程同时访问时可能会出现问题。

class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {instance = new Singleton();}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;

优点

  • 实例化延迟,只有在第一次使用时才创建实例,避免了不必要的资源消耗。

缺点

  • 不是线程安全的。在多线程环境下,可能会创建多个实例。

3. 线程安全的懒汉式

为了确保线程安全,可以使用互斥锁(mutex)来同步访问,确保只有一个线程能够创建实例。

#include <mutex>class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mutex);  // 加锁确保线程安全if (instance == nullptr) {instance = new Singleton();}}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;static std::mutex mutex;  // 互斥锁
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

优点

  • 线程安全,保证在多线程环境下只有一个实例被创建。

缺点

  • 使用互斥锁增加了性能开销,尤其是频繁访问 getInstance() 时。

4. 双重检查锁(Double-Checked Locking)

双重检查锁定是懒汉式的优化版本,它减少了锁的使用频率。在第一次检查时不加锁,只有当实例为 nullptr 时才加锁,这样可以避免每次调用 getInstance() 时都进行加锁操作。

#include <mutex>class Singleton {
public:static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mutex);  // 加锁if (instance == nullptr) {  // 双重检查instance = new Singleton();}}return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值static Singleton* instance;static std::mutex mutex;  // 互斥锁
};// 静态实例初始化为空指针
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mutex;

优点

  • 提供了线程安全的懒汉式实现,且避免了每次都加锁的性能开销。

缺点

  • 代码相对复杂,需要小心实现。对 instance 的访问需要特别注意线程间的同步。

5. 静态局部变量(最推荐的实现方式)

使用静态局部变量来实现单例模式,这种方式是线程安全的,并且实现简单。C++11标准及以上可以确保静态局部变量的初始化是线程安全的。

class Singleton {
public:static Singleton& getInstance() {static Singleton instance;  // 静态局部变量return instance;}private:Singleton() {}  // 构造函数私有化~Singleton() {}Singleton(const Singleton&) = delete;  // 禁止拷贝构造函数Singleton& operator=(const Singleton&) = delete;  // 禁止赋值
};

优点

  • 简洁、线程安全。
  • 在程序启动时不会立即创建实例,而是等到第一次使用时创建。
  • 不需要显式加锁。

缺点

  • 只适用于需要在第一次使用时实例化的情况。

总结

  • 饿汉式:简单,适合不需要延迟初始化的场景,程序启动时就创建实例。
  • 懒汉式:适合需要延迟初始化的场景,但需要考虑线程安全。
  • 线程安全懒汉式:通过加锁保证线程安全,但可能带来性能开销。
  • 双重检查锁:线程安全,减少了加锁的频率,但实现复杂。
  • 静态局部变量:线程安全,简洁,现代 C++ 中推荐的单例实现方式。

在现代 C++ 中,静态局部变量是实现单例模式的首选方法,既保证了线程安全,又没有性能开销,是最优选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887046.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据库入门】关系型数据库入门及SQL语句的编写

1.数据库的类型&#xff1a; 数据库分为网状数据库&#xff0c;层次数据库&#xff0c;关系型数据库和非关系型数据库四种。 目前市场上比较主流的是&#xff1a;关系型数据库和非关系型数据库。 关系型数据库使用结构化查询语句&#xff08;SQL&#xff09;对关系型数据库进行…

【通俗理解】ELBO(证据下界)——机器学习中的“情感纽带”

【通俗理解】ELBO&#xff08;证据下界&#xff09;——机器学习中的“情感纽带” 关键词提炼 #ELBO #证据下界 #变分推断 #机器学习 #潜变量模型 #KL散度 #期望 #对数似然 第一节&#xff1a;ELBO的类比与核心概念【尽可能通俗】 ELBO&#xff0c;即证据下界&#xff0c;在…

react后台管理系统(二)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;React篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来React篇专栏内容:react后台管理系统(二) 前言 本文档旨在详细说明如何在一个基于React的应用程序中实现左侧菜单…

【学习笔记】AD智能PDF导出(装配文件)

2.下一步“NEXT” 3.选择文件名称&#xff0c;下一步“NEXT” 4.可选导出原材料的BOM表 右键选择装配图“Create Assembly Drawings” 5.可以双击下图方框&#xff0c;或者右键需要编辑的标题&#xff0c;选择“Properties”&#xff0c;勾选如下图 6.装配文件&#xff0c;添加…

在win10环境部署opengauss数据库(包含各种可能遇到的问题解决)

适用于windows环境下通过docker desktop实现opengauss部署&#xff0c;请审题。 文章目录 前言一、部署适合deskdocker的环境二、安装opengauss数据库1.配置docker镜像源2.拉取镜像源 总结 前言 注意事项&#xff1a;后面docker拉取镜像源最好电脑有科学上网工具如果没有科学上…

如何构建高效的接口自动化测试框架?

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在选择接口测试自动化框架时&#xff0c;需要根据团队的技术栈和项目需求来综合考虑。对于测试团队来说&#xff0c;使用Python相关的测试框架更为便捷。无论选…

AI Prompt Engineering

AI Prompt Engineering 简介 Prompt Engineering, 提示工程&#xff0c;是人工智能领域的一项技术&#xff0c;它旨在通过设计高效的提示词&#xff08;prompts&#xff09;来优化生成式 AI&#xff08;如 GPT、DALLE 等&#xff09;的输出。提示词是用户与生成式 AI 交互的核…

【bug】使用transformers训练二分类任务时,训练损失异常大

使用transformers训练二分类任务时&#xff0c;训练损失异常大 问题分析 问题 training_loss异常大&#xff0c;在二分类损失中&#xff0c;收敛在1~2附近&#xff0c;而eval_loss却正常&#xff08;小于0.5&#xff09; 分析 参考&#xff1a; Bug in gradient accumulation…

基于SpringBoot实现的城镇保障性住房管理系统(代码+论文)

&#x1f389;博主介绍&#xff1a;Java领域优质创作者&#xff0c;阿里云博客专家&#xff0c;计算机毕设实战导师。专注Java项目实战、毕设定制/协助 &#x1f4e2;主要服务内容&#xff1a;选题定题、开题报告、任务书、程序开发、项目定制、论文辅导 &#x1f496;精彩专栏…

springboot基于SpringBoot的社区居民诊疗健康管理系统

摘 要 社区居民诊疗健康管理系统的建设强化了社区医疗服务与居民之间的联系&#xff0c;优化了健康服务供给&#xff0c;提高了医疗资源的利用效率。它不仅有助于提升居民的健康素养和自我管理能力&#xff0c;也是推动实现全民健康信息化、构建以人为本的健康服务体系的重要步…

VSCode 间距太小

setting->font family 使用&#xff1a;Consolas, Courier New, monospace 字体

IntelliJ+SpringBoot项目实战(九)--整合Thymyleaf模版引擎

一、Thymeleaf 基本介绍 Thymeleaf是一款模板引擎产品&#xff0c;是一款优秀的面向JAVA的XML/XHTML/HTML5页面模板&#xff0c;具有丰富的标签语言和函数。因此&#xff0c;在使用SpringBoot开发前端网页&#xff0c;经常选择Thymeleaf。 在前后端分离框架流行的今天&a…

Python中常用的函数介绍

Python中常用的几种函数 1、input函数 input()函数&#xff1a;主要作用是让用户输入某个内容并接收它。 #输入你的年龄 >>> age input("my age is :") my age is :20 执行代码后输入年龄&#xff0c;年龄被存放到age变量中&#xff0c;执行print后终端会…

【动手学深度学习Pytorch】1. 线性回归代码

零实现 导入所需要的包&#xff1a; # %matplotlib inline import random import torch from d2l import torch as d2l import matplotlib.pyplot as plt import matplotlib import os构造人造数据集&#xff1a;假设w[2, -3.4]&#xff0c;b4.2&#xff0c;存在随机噪音&…

亿咖通科技应邀出席微软汽车行业智享会,分享ECARX AutoGPT全新实践

11月14日&#xff0c;全球出行科技企业亿咖通科技&#xff08;纳斯达克股票代码&#xff1a;ECX&#xff09;应邀于广州参加由微软举行的汽车行业智享会&#xff0c;揭晓了亿咖通科技对“AI定义汽车”时代的洞察与技术布局&#xff0c;分享了亿咖通科技汽车垂直领域大模型ECARX…

鸿蒙开发学习|Promise的介绍与在鸿蒙中的使用

Promise的介绍与在鸿蒙中的使用 异步编程 学习Promise的开始,我们要先了解异步编程 一般代码的执行是单线程的机制,就是按次序执行,执行完一个任务后,再执行下一个,如果我们在页面加载的同时时候执行一个请求,拿到数据后映射到界面上,这时我们就需要异步操作来执行这个请求 异…

第二课 Model模型资源导入设置检查与优化

上期我们学习了最简单的audio音效的优化&#xff0c;接下来我们继续model模型资源的优化&#xff0c;我将汇总各路大神关于模型优化的思路和方法供你和我学习。 首先我们还是要把我们优化的目标重申一遍&#xff1a; 优化的目标 1.文件体积尽可能小 2.内存占用尽可能小 3.…

小米路由器用外网域名访问管理界面

本文在Redmi AX3000 (RA81)设置&#xff0c;其他型号路由器的管理界面端口可能各不相同。 开始之前需要保证路由器SSH功能正常&#xff0c;如果没有SSH可以参考这里。 1. 给WAN口开放80端口 可以通过下载mixbox的firewall插件或者其他防火墙插件开放端口。 2. 把域名解析到路…

一次需升级系统的wxpython安装(macOS M1)

WARNING: The scripts libdoc, rebot and robot are installed in /Users/用户名/Library/Python/3.8/bin which is not on PATH. 背景&#xff1a;想在macos安装Robot Framework &#xff0c;显示pip3不是最新&#xff0c;更新pip3后显示不在PATH上 参看博主文章末尾 MAC系统…

Leetcode 求根节点到叶节点数字之和

使用深度优先搜索 DFS 来做 我提供的代码使用的是 深度优先搜索&#xff08;DFS&#xff0c;Depth-First Search&#xff09; 算法。以下是具体的算法思想和实现步骤的解释&#xff1a; 算法思想 树的路径代表数字&#xff1a; 树中每条从根节点到叶子节点的路径可以看作一个整…