C++之单例模式

C++之单例模式

  • 前言
  • 介绍
    • 1、单例模式是什么?
      • 1.1 实现单例模式的三个要点
      • 1.2 单例模式分类
    • 2. 懒汉式
      • 2.1 懒汉实现:基础方法
      • 2.2 懒汉实现:基于单锁
      • 2.3 懒汉实现:基于双重检测锁
      • 2.4 懒汉实现:基于双重检测锁和资源管理
        • 2.4.1 智能指针方式
        • 2.4.2 静态嵌套类方式
      • 2.5 懒汉实现:基于局部静态对象
    • 3. 饿汉式
      • 3.1 饿汉实现:基础方法
    • 4. 总结

前言

单例模式(Singleton Pattern)是 面向对象中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

优点:通过单例模式的设计,使得创建的类在当前进程中只有一个实例,并提供一个全局性的访问点,这样可以规避因频繁创建对象而导致的 内存飙升 情况。

介绍

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决:一个全局使用的类频繁地创建与销毁。

何时使用:当您想控制实例数目,节省系统资源的时候。

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

优点:

1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

1、单例模式是什么?

在面向对象编程中,有时候我们希望一个类只有一个实例化的对象,比如线程池,缓存等。这些类有且只有一个唯一的实例,这种设计模式被称为单例模式。

1.1 实现单例模式的三个要点

1)私有化构造函数:这样外界就无法自由地创建类对象,进而阻止了多个实例的产生。
2)类定义中含有该类的唯一静态私有对象:静态变量存放在全局存储区,且是唯一的,供所有对象使用。
3)用公有的静态函数来获取该实例:提供了访问接口。

1.2 单例模式分类

单例模式有两种主要实现方法:懒汉模式和饿汉模式。

  1. 懒汉模式特点是当外界调用时才进行实例化;
  2. 饿汉模式特点是一开始就对实例进行初始化,调用时直接返回这个构建好的实例。

2. 懒汉式

懒汉模式特点是当外界调用时才进行实例化。

2.1 懒汉实现:基础方法

是否多线程安全:否
实现难度:易
描述:这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
缺点:一个是线程安全,另一个是内存泄漏。
线程安全是因为在多线程场景下,有可能出现多个线程同时进行new操作的情况,没通过加锁来限制。
内存泄漏是因为使用了new在堆上分配了资源,那么在程序结束时,也应该进行delete,确保堆中数据释放。

public class Singleton {  // 静态私有对象private static Singleton instance;  // 私有构造函数private Singleton (){}  // 公有接口获取唯一实例public static Singleton getInstance() {  if (instance == null) {  instance = new Singleton();  }  return instance;  }  
}

这种方式在单线程下没有问题,但是如果多线程模式下,当唯一实例还没有创建,两个线程同时调用getinstance就可能同时创建对象,导致错误。

2.2 懒汉实现:基于单锁

是否多线程安全:是
实现难度:较易
描述:这种方式采用单锁机制,有可能造成阻塞。

Singleton*Singleton::getInstance(){m.lock();if (_instance == nullptr)_instance = new Singleton;m.unlock();return _instance;
}

加锁又会带来另外的性能问题,如果每个线程每次获取实例都加锁,有可能造成阻塞的发生。实际上,上锁的目的是为了防止有多个线程在实例未被初始化的情况下,同时对他进行初始化,如果实例已经被创建了,就不需要考虑这个问题了,所以就可以采用二次加锁的方法来提高程序的性能。

2.3 懒汉实现:基于双重检测锁

是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,可以确保线程安全,且在多线程情况下能保持高性能。
getInstance() 的性能对应用程序很关键。

Singleton*Singleton::getInstance(){if (_instance == nullptr){m.lock();if (_instance == nullptr){_instance = new Singleton;}m.unlock();}return _instance;
}

接下来,我们再解决内存泄漏(资源释放)问题,对懒汉式实现进行进一步的改进。

2.4 懒汉实现:基于双重检测锁和资源管理

是否多线程安全:是
实现难度:较复杂
描述:这种方式采用双锁机制,可以确保线程安全,且在多线程情况下能保持高性能。并且加入资源管理机制,以达到对资源的释放的目的。
我们加入资源管理机制,以达到对资源的释放的目的,解决方法有两个:智能指针&静态嵌套类。

2.4.1 智能指针方式

将实例指针更换为智能指针,另外智能指针在初始化时,还需要人为添加公有的毁灭函数,因为析构函数私有化了。

#include <iostream>
#include <mutex>
using namespace std;// 单例模式演示类
class Singleton
{
public:
// 公有接口获取唯一实例static shared_ptr<Singleton> getInstance() {
// 若为空则创建if (instance == nullptr) {
// 加锁保证线程安全
// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞
// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance.reset(new Singleton(), destoryInstance);cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;
}
// 毁灭实例
static void destoryInstance(Singleton* x) {cout << "自定义释放实例" << endl;delete x;
}private:
// 私有构造函数
Singleton() {cout << "构造函数启动。" << endl;
};// 私有析构函数
~Singleton() {cout << "析构函数启动。" << endl;
};private:
// 静态私有对象
static shared_ptr<Singleton> instance;
// 锁
static mutex m_mutex;
};// 初始化
shared_ptr<Singleton> Singleton::instance;
mutex Singleton::m_mutex;

应用智能指针后,在程序结束时,它自动进行资源的释放,解决了内存泄漏的问题。

2.4.2 静态嵌套类方式

类中定义一个嵌套类,初始化该类的静态对象,当程序结束时,该对象进行析构的同时,将单例实例也删除了。

#include <iostream>
#include <mutex>
using namespace std;// 单例模式演示类
class Singleton
{
public:
// 公有接口获取唯一实例
static Singleton* getInstance() {
// 若为空则创建if (instance == nullptr) {
// 加锁保证线程安全
// 如果两个线程同时进行到这一步,一个线程继续向下执行时,另一个线程被堵塞
// 等锁解除后,被堵塞的线程就会跳过下面的if了,因为此时实例已经构建完毕lock_guard<mutex> l(m_mutex);if (instance == nullptr) {cout << "实例为空,开始创建。" << endl;instance = new Singleton();cout << "地址为:" << instance << endl;cout << "创建结束。" << endl;}}else {cout << "已有实例,返回。" << endl;}return instance;
}private:
// 私有构造函数
Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数
~Singleton() {
cout << "析构函数启动。" << endl;
};// 定义一个删除器
class Deleter {
public:
Deleter() {};
~Deleter() {
if (instance != nullptr) {cout << "删除器启动。" << endl;delete instance;instance = nullptr;}
}
};// 删除器是嵌套类,当该静态对象销毁的时候,也会将单例实例销毁
static Deleter m_deleter;
private:
// 静态私有对象
static Singleton* instance;
// 锁
static mutex m_mutex;
};// 初始化
Singleton* Singleton::instance = nullptr;
mutex Singleton::m_mutex;
Singleton::Deleter Singleton::m_deleter;

2.5 懒汉实现:基于局部静态对象

是否多线程安全:是
实现难度:一般
描述:C++11后,规定了局部静态对象在多线程场景下的初始化行为,只有在首次访问时才会创建实例,后续不再创建而是获取。若未创建成功,其他的线程在进行到这步时会自动等待。注意C++11前的版本不是这样的。

因为有上述的改动,所以出现了一种更简洁方便优雅的实现方法,基于局部静态对象实现。

#include <iostream>
#include <mutex>
using namespace std;// 单例模式演示类
class Singleton
{
public:
// 公有接口获取唯一实例
static Singleton& getInstance() {cout << "获取实例" << endl;static Singleton instance;cout << "地址为:" << &instance << endl;return instance;}
private:
// 私有构造函数
Singleton() {cout << "构造函数启动。" << endl;};// 私有析构函数
~Singleton() {cout << "析构函数启动。" << endl;};
};

3. 饿汉式

饿汉模式特点是一开始就对实例进行初始化,调用时直接返回这个构建好的实例。

3.1 饿汉实现:基础方法

是否多线程安全:是
实现难度:易
描述:这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。第一次调用才初始化,避免内存浪费。
缺点:类加载时就初始化,浪费内存。必须加锁 synchronized 才能保证单例,但加锁会影响效率。
它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

#include <iostream>
#include <mutex>
using namespace std;// 单例模式演示类
class Singleton
{
public:
// 公有接口获取唯一实例
static Singleton* getInstance() {cout << "获取实例" << endl;cout << "地址为:" << instance << endl;return instance;}
private:
// 私有构造函数
Singleton() {cout << "构造函数启动。" << endl;
};// 私有析构函数
~Singleton() {cout << "析构函数启动。" << endl;
};private:
// 静态私有对象
static Singleton* instance;
};// 初始化
Singleton* Singleton::instance = new Singleton();

main还没开始,实例就已经构建完毕,获取实例的函数也不需要进行判空操作,因此也就不用双重检测锁来保证线程安全了,它本身已经是线程安全状态了。
但是内存泄漏的问题还是要解决的,这点同懒汉是一样的。可以通过智能指针和静态嵌套实现。

4. 总结

一般情况下,建议使用基于双重检测锁和资源管理搭配智能指针的懒汉方式。

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

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

相关文章

Javascript——vue下载blob文档流

<el-table-column label"操作" fixed"right" width"150" showOverflowTooltip><template slot-scope"scope"><el-button type"text" v-has"stbsd-gjcx-down" class"edit-button" click&…

Mac 远程到 windows/Linux 开发体验为什么这么差?

随着科技的发展&#xff0c;远程开发已经成为一种常见的开发方式&#xff0c;其中Mac远程到windows/Linux开发也得到了广泛的应用。然而&#xff0c;许多用户反映Mac远程到windows/Linux开发体验并不理想&#xff0c;存在诸多问题。本文将深入探讨这些问题&#xff0c;并分析其…

MySQL 从零开始:02 MySQL 安装

文章目录 1、下载 MySQL 安装程序2、安装 MySQL 要操作 MySQL &#xff0c;首先要安装 MySQL &#xff0c;本文将一步步展示如何安装 MySQL&#xff0c;简直详细到令人发指。 环境&#xff1a; 操作系统&#xff1a;Windows10 64位MySQL版本&#xff1a;社区版 8.0.11.0 1、下…

探索AI技术的奥秘:揭秘人工智能的核心原理

人工智能&#xff08;AI&#xff09;已经成为当今科技领域最引人注目的话题之一。然而&#xff0c;对于许多人来说&#xff0c;AI仍然是一个神秘的领域&#xff0c;不清楚其核心原理。本文将探索AI技术的奥秘&#xff0c;为读者揭开人工智能的核心原理。 随着技术的飞速发展&am…

Asynchronous FIFO and synchronous FIFO-翻译自外网

Synchronous FIFO 先进先出 (FIFO) 是一种非常流行且有用的设计块&#xff0c;用于模块之间的同步和握手机制。 FIFO 的深度&#xff1a; FIFO 中的槽数或行数称为 FIFO 的深度。 FIFO 的宽度&#xff1a;每个槽或行中可以存储的位数称为 FIFO 的宽度。 在同步 FIFO 中&…

6.2 声音编辑工具GoldWave5简介(1)

不管你的MP3歌曲的简单剪接或者音频格式的转换,还是更加高级的后期加工 GoldWave5都可以令你轻松胜,甚至你自己录一首卡拉OK,也可以经过GoldWave5的修饰成为像歌星一样水晶般的动人声音!除了附有许多的效果处理功能外&#xff0c;它还能将编辑好的文件存成WAV、AU、SND、RAW、A…

vue 公众号开发,调用jssdk封装

vue 公众号开发&#xff0c;经常会使用到 转发朋友&#xff0c;朋友圈&#xff0c;调用扫一扫等功能&#xff0c;这时就要使用微信的 jssdk 微信jssdk传送门 1. 安装jssdk 插件 (jweixin-module) npm install jweixin-module --save 2. 封装方法 utils/jwx.js let jweixin…

sqlilabs第四十九五十关

Less-49(GET - Error based - String Bind - ORDER BY CLAUSE) 手工注入 无回显(还是单引号闭合)&#xff0c;只能使用延时注入 自动脚本 和上一关一样 Less-50(GET - Error based - ORDER BY CLAUSE -numeric- Stacked injection) 手工注入 这里需要使用堆叠注入的思路 自…

Qt优秀开源项目之二十:RedPanda-CPP(小熊猫C++)

小熊猫C是跨平台、轻量易用的开源C/C集成开发环境。 官网&#xff1a;http://royqh.net/redpandacpp github&#xff1a;https://github.com/royqh1979/RedPanda-CPP 小熊猫C&#xff08;原名小熊猫Dev-C 7)是基于Qt开发的Dev-C替代版本。和经典的Dev-C 5.11、新的Embarcadero …

TDengine 签约积成电子

随着电力系统的复杂性和数据量不断增加&#xff0c;电力负荷、电压、频率等庞大的时序数据需要更高效的存储和处理能力&#xff0c;才能确保数据的可靠性和实时性。此外&#xff0c;电力系统还需要对实时数据进行快速分析和决策&#xff0c;以确保电网的稳定运行。然而&#xf…

openssl3.2 - 官方dmeo学习 - sconnect.c

文章目录 openssl3.2 - 官方dmeo学习 - sconnect.c概述笔记END openssl3.2 - 官方dmeo学习 - sconnect.c 概述 TLS客户端 - 使用根证书, 非阻塞, 向服务器要东西. 笔记 开始一个新demo学习时, 要从头配置包含路径, 麻烦. 直接拷贝上一个实现工程, 换掉实现.c方便一些. 换的…

(超详细)4-YOLOV5改进-添加ShuffleAttention注意力机制

1、在yolov5/models下面新建一个SE.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import numpy as np import torch from torch import nn from torch.nn import init from torch.nn.parameter import Parameterclass ShuffleAttention(nn.Module):def __…

C //练习 4-12 运用printd函数的设计思想编写一个递归版本的itoa函数,即通过递归调用把整数转换为字符串。

C程序设计语言 &#xff08;第二版&#xff09; 练习 4-12 练习 4-12 运用printd函数的设计思想编写一个递归版本的itoa函数&#xff0c;即通过递归调用把整数转换为字符串。 注意&#xff1a;代码在win32控制台运行&#xff0c;在不同的IDE环境下&#xff0c;有部分可能需要…

【IOS】惯性导航详解(包含角度、加速度、修正方式的api分析)

参考文献 iPhone的惯性导航&#xff0c;基于步态。https://www.docin.com/p-811792664.html Inertial Odometry on Handheld Smartphones: https://arxiv.org/pdf/1703.00154.pdf 惯性导航项目相关代码&#xff1a;https://github.com/topics/inertial-navigation-systems use…

openssl3.2 - 官方dmeo学习 - 索引贴

文章目录 openssl3.2 - 官方dmeo学习 - 索引贴概述笔记工程的搭建和调试环境BIOBIO - client-arg.cBIO - client-conf.cBIO - saccept.cBIO - sconnect.cBIO - server-arg.cBIO - server-cmod.cBIO - server-conf.cBIO - 总结certsEND openssl3.2 - 官方dmeo学习 - 索引贴 概述…

长尾分布定义,举个物种长尾分布和词频长尾分布的例子。

问题描述&#xff1a;长尾分布定义&#xff0c;举个物种长尾分布和词频长尾分布的例子。 问题解答&#xff1a; 长尾分布是一种概率分布的类型&#xff0c;它描述的是一种极端事件或者稀有事件的发生概率。具体来说&#xff0c;长尾分布描述的是少量的类别占据了大部分的样本…

SpringIOC之support模块GenericXmlApplicationContext

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

STM32(HAL库) CubeMX+Keil5 建立工程

STM32&#xff08;HAL库&#xff09; CubeMXKeil5 建立工程 目标选择 菜单栏 File 新建工程打开工程退出软件 Window 输出窗口的开启软件字体设置 Help 软件帮助文档检查软件更新管理MCU 已存在工程&#xff08;Existing Projects&#xff09; 最近打开过的工程(Recent Open…

2024年值得关注的10种自动化测试趋势

超级自动化测试这是利用人工智能&#xff08;AI&#xff09;和机器学习&#xff08;ML&#xff09;来自动化测试任务。超级自动化测试可以帮助减少手动测试的需求&#xff0c;提高测试的效率&#xff0c;并在开发生命周期的早期发现缺陷。 TestOps集成TestOps是将DevOps实践扩…

PyPDF2 3.0.0更新,一些函数被弃用,需要重新写

1.PdfFileWriter is deprecated and was removed in PyPDF2 3.0.0. Use PdfWriter instead. 这错误表明你正在使用的 PyPDF2 版本中已经移除了 PdfFileWriter&#xff0c;并在版本 3.0.0 中被替代为 PdfWriter。这是因为在 PyPDF2 的更新中&#xff0c;一些 API 被重新组织和更…