Qt下实现支持多线程的单例模式

2023-11-01

1. 代码介绍

实现单例模式的代码很多。
本文的单例模式实现代码是本人一直在工程项目中使用的,现拿出和大家交流分享。
本文实现的单例模式,支持多线程,采用双重校验检索的方式,集成析构类,杜绝内存泄漏,稳定性好。 使用C++/Qt的朋友们可以了解一下。
不再废话,直接上代码。

2. 代码之路

头文件makelog.h

#include <QMutex>
#include <QObject>
class Makelog: public QObject
{
  	Q_OBJECT
  public:
  	static Makelog* getInstance()
  	{
  		if (m_pInstance == NULL)
  		{
  			QMutexLocker mlocker(&m_Mutex);  //双检索,支持多线程
  			if (m_pInstance == NULL)
  			{
  				m_pInstance = new Makelog();
  			}
  		}
  		return m_pInstance;
  	}
  private:
  	Makelog(){}
  	Makelog(const Makelog&){}
  	Makelog& operator ==(const Makelog&){}

  	static Makelog* m_pInstance;   //类的指针
  	static QMutex m_Mutex;
  public:
  	class CGarbo  //专用来析构m_pInstance指针的类
  	{
  		public:
  		~CGarbo()
  		{
  			if (m_pInstance != NULL)
  			{
  				delete m_pInstance;
  				m_pInstance = NULL;
  			}
  		}
  	};
  	static CGarbo m_Garbo; 					
};

makelog.cpp文件

Makelog* Makelog::m_pInstance = NULL;
Makelog::CGarbo m_Garbo;
QMutex Makelog::m_Mutex;

支持多线程,无内存泄漏的单例模式就实现了。
下面举例说明具体的使用:
在头文件中加入一个public函数、一个public变量。

public:
void readFile();
QString m_config;

在源文件中加入函数具体实现,使得readFile()或m_config有实际的意义。
那么,在一个工程内的其他类中,只需做两个步骤,就可以使用这个readFile()函数和m_config变量了。
步骤1:包含头文件

#include “makefile.h”

步骤2:通过单例类入口调用函数或变量

Makelog::getInstance()->readFile(); //使用函数
Makelog::getInstance()->m_config; //使用变量

3. 详细分析

3.1 什么是单例

单例是一种软件设计模式,采用单例模式书写的类可以确保在一个工程中只有一个对象实例。再通俗点,就是一个类写好了之后,就不需要也无法再把这个类实例化了,因为写这个类的时候已经确保了有且仅有一个已经实例化的对象。
这样不是很蠢么?花了这么多功夫写了一个类,你告诉我这个类没法用来new出对象了?那我怎么使用这个类?我写个配合静态变量的静态函数,使用起来不是更方便?
当然不蠢,非但不蠢,而且单例模式是所有设计模式中使用最为频繁的一个设计模式。没法new出对象,因为单例模式已经帮你new了一个对象,而且让你的工程中只有这个对象了;使用这个对象只需要包含头文件,然后调用接口指针函数就可以了;静态的全局函数或变量代码实现起来方便,但是不具有类的封装性和灵活性。

3.2 如何让类无法实例化

首先要清楚类实例化无非就是三种方式:
1)采用构造函数实例化;
2)用拷贝函数实现实例化;
3)赋值操作实现实例化。
所以,只需要把这个类的构造函数、拷贝函数、赋值操作写成私有的,就无法调用这些函数,自然就无法实例化了。
正如上文所示的几个函数:

private:
Makelog(){} //构造函数
Makelog(const Makelog&){} //拷贝函数
Makelog& operator ==(const Makelog&){} //赋值操作符重写

当然,如果有一些初始化的操作,也可以写在私有构造函数的双括号内。

3.3 如何调用这个唯一实例

广泛采用的做法就是在写一个public的函数作为接口,这个函数返回单例类唯一实例的指针。
最简单的写法如下:

Makelog* getInstance()
{
if (m_pInstance == NULL)
{
m_pInstance = new Makelog(); //调用private构造函数,把唯一实例的指针实例化
}
return m_pInstance;
}
Makelog* m_pInstance; //唯一实例的指针

这个写法看着真不错,可是这么写遇到了一个小小的悖论。
“我如何去调用执行这个getInstance()函数啊,对,我需要一个实例化对象才能去执行!那我去new个对象,等等,唯一的实例化对象是通过这个函数才能找到的啊!”
有方法解决么,当然,类的静态方法不需要实例化对象,用

类名::方法()

的形式就可以调用执行了,所以把getInstance()函数前加一个static就好了。
但是静态方法只能使用静态成员变量啊,那就把唯一实例的指针m_pInstance也变成static的吧。
Ok,这样有没有隐患啊?隐患?static类型的instance存在静态存储区,每次调用时,都指向的同一个对象。非但没有隐患,简直堪称完美!
现在上面的代码就变成这样:

public:
static Makelog* getInstance()
{
if (m_pInstance == NULL )
{
m_pInstance = new Makelog(); //用私有构造函数new出了这个类的唯一对象
}
return m_pInstance;
}
private:
static Makelog* m_pInstance; //唯一实例指针

这样写完全没有问题,但是不支持多线程的调用。因为new Makelog()需要时间,所以当两个线程同时判断m_pInstance ==NULL,同时执行了m_pInstance = new Makelog()这句代码,问题就大了。

3.4 如何支持多线程

为了解决3.3节产生的bug,广泛采用的方式是双重校验检索的方法。
就是利用互斥锁(用来保证锁内代码最多只有一个线程在同时执行)的方式,确保不会出现两个线程同时new出这个单例类的唯一实例的情况发生。
具体代码如下:

static Makelog* getInstance()
{
if (m_pInstance == NULL)
{
QMutexLocker mlocker(&m_Mutex); //加锁,锁内代码只有一个线程执行
if (m_pInstance == NULL) //先执行的线程会进入内部new对象,后一个线程判断m_pInstance就不是NULL了
{
m_pInstance = new Makelog();
}
}
return m_pInstance;
}

至此双重校验检索解决多线程问题的单例问题就解决了。当然还可以用原子锁的方法来解决,但是灵活性不强(也可能是我太外行,灵活不起来—。—),这里就不介绍了。

3.5 如何解决内存泄漏

解决单例类的内存析构主要就是解决static Makelog* m_pInstance这个指针的析构问题(毕竟其他的可以不用指针的嘛)。我总结觉得写一个专门用来析构的类是最方便有效和无脑的方法了,推荐给大家。
具体就是在单例类中写一个类:

public:
class CGarbo //专用来析构m_pInstance指针的类
{
public:
~CGarbo() //这个类只有析构函数
{
if (m_pInstance != NULL)
{
delete m_pInstance;
m_pInstance = NULL;
}
}
};
static CGarbo m_Garbo; //声明一个静态的对象

然后在cpp文件中声明一下 Makelog::CGarbo m_Garbo就可以了。
这个类只有析构函数,析构函数的作用就是delete单例唯一对象的指针。
析构类声明一个static对象,因为静态对象系统会在关闭程序时自动析构,就可以执行到析构函数内部的代码了。

4. 结束语

单例模式是非常常用而基础的一个设计模式,本文作者第一次写博客,有不详或错误之处还请大家指正。对于还在使用C++/Qt的初学者,请不要因为害怕而不去深究和掌握单例模式这个好用实用的工具。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Qt下实现支持多线程的单例模式 的相关文章

  • 通过CMake实现Qt项目

    我正在尝试通过 Cmake 构建并运行非常简单且基本的 Qt 示例 删除 pro 文件 以下是Qt项目的代码 自动生成的Qt项目的目录结构为 Cmake my project name headers mainwindow h source
  • QGraphicsScene没有删除QWidget的功能

    QGraphicsScene 有一个addWidget QWidget 有函数 但是没有对应的removeWidget QWidget 它只有removeItem QGraphicsItem 如何删除 QWidget 这是一个基本示例 看看
  • 加载 QPixmap 数据的更好方法

    更好的方法来做到这一点 没有QImage QImage image width height QImage Format RGB888 memcpy image bits m frameRGB gt data 0 height width
  • 获取 QPushButton 在 2D 数组 QPushButton 上的索引

    我有一个二维数组QPushButton 当用户单击按钮时如何获取按钮的索引 例如当用户单击按钮时a 2 3 它会显示 2 3 该示例如下所示 Qt 4 5 使用对象名称 您可以为按钮指定唯一的对象名称 理想情况下 名称应该是有效的 C 标识
  • 如何在 QT 中绘制点?

    我正在用 QT 用 C 编写一个应用程序 其中有 n 个点并计算它的凸包 然而 一旦计算出来 我不知道如何绘制点并绘制船体的边界 制作菜单按钮等很简单 但我不确定我是否知道执行此操作的工具 你怎么做到这一点 图形视图 addEllipse
  • 仅在内部/外部抚摸路径?

    Given a QPainterPath http qt project org doc qt 4 8 qpainterpath html如何仅在路径的内侧或外侧边缘 或非闭合路径的左侧或右侧 描边路径 QPainter strokePat
  • 用 C++/Qt 编写的程序中的 RTF / doc / docx 文本提取

    我正在写一些程序Qt https en wikipedia org wiki Qt 28software 29 C 我需要从中读取文本微软Word https en wikipedia org wiki Microsoft Word RTF
  • QMutex 是否需要是静态的,以便此类实例的其他线程调用知道暂停其操作?

    从多个线程调用以下附加函数 我不希望数据重写附加 因为计数器尚未增加 除了当前使用 Append 的线程之外 这是否会挂起所有进入的线程 或者其他线程会继续运行而不追加数据吗 互斥锁是否需要是 静态 的 或者每个实例都知道要暂停操作吗 如果
  • QStyledItemDelegate 绘制自定义小部件失败

    在我的一个项目中 我使用的是QTableWidget为了显示一些复杂的计算结果 为了提高表格的可读性 我需要在单个表格单元格内显示两个对齐的值 后来我想通过使用颜色或箭头等来进一步自定义小部件 为此我源自QStyledItemDelegat
  • 第一次信号发射后自动断开

    我正在从文件加载网页 然后替换其中的一些 html self template web page QtWebKit QWebPage self template web page mainFrame load QtCore QUrl tem
  • 将 jstring 转换为 QString

    我正在调用一个返回字符串的 Java 函数 QAndroidJniObject obj QAndroidJniObject callStaticObjectMethod
  • 在 Qt 中使用多个不同的流读取同一文件

    使用 Qt 是否可以使用多个流读取文件以同时访问其中的不同数据部分 请注意 Qt 中的流 QTextStream QDataStream 不处理底层设备中的位置 流类只是一个包装器 用于更轻松地解析设备 QFile 实例 内的二进制数据 因
  • Qt:不完整类型和前向声明的使用无效

    我有一些误解 A h ifndef A H define A H include B h class A public B Q OBJECT public A endif A cpp include A h A A B ui gt blan
  • PyQt4 QPalette 不工作

    btn QtGui QPushButton Button self palettes btn palette palettes setColor btn backgroundRole QtCore Qt green btn setPalet
  • 是否有 Qt 小部件可以浏览应用程序中小部件的层次结构(类似于 Spy++)?

    我们有一个具有复杂的小部件层次结构的应用程序 我希望能够以与 Spy 类似的方式浏览此层次结构 查看和编辑属性 例如大小 如果有一个小部件可以显示此信息 则它不需要在外部应用程序中运行 那么问题来了 这样的神兽存在吗 您可以使用Gammar
  • 了解 Qt3D 创建的网格

    我创建了一个 Qt3D 网格 如下所示 Qt3DCore QEntity newEntity new Qt3DCore QEntity Qt3DExtras QConeMesh mesh new Qt3DExtras QConeMesh m
  • PyQt4 信号和槽

    我正在使用 PyQt4 编写我的第一个 Python 应用程序 我有一个 MainWindow 和一个 Dialog 类 它是 MainWindow 类的一部分 self loginDialog LoginDialog 我使用插槽和信号 这
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • QFileSystemModel setRootPath

    我正在尝试创建一个 Qt 应用程序来显示文件夹 Mac OS 中的 Users 文件夹 的内容 这是代码 QFileSystemModel dirModel new QFileSystemModel dirModel gt setRootP
  • QTabWidget 选项卡在垂直方向,但文本在水平方向

    我正在尝试用 C Qt 制作一个带有这样的侧边栏的应用程序 但是当将 QTabWidget 方向设置为西时 它会使文本垂直 如何让文本位于左侧 但水平对齐 PS 我不需要图标 提前致谢 您可以使用QListWidget http doc q

随机推荐

  • 4. Hadoop伪分布式运行模式

    文章目录 Hadoop伪分布式运行模式 1 启动HDFS并运行MapReduce程序 1 1 配置集群 配置hadoop env sh 配置core site xml 配置hdfs site xml 1 2 启动集群 格式化NameNode
  • window.open()的所有参数列表(转)

    前言 经常上网的朋友可能会到过这样一些网站 一进入首页立刻会弹出一个窗口 或者按一个连接或按钮弹出 通常在这个窗口里会显示一些注意事项 版权信息 警告 欢迎光顾之类的话或者作者想要特别提示的信息 其实制作这样的页面效果非常的容易 只要往该页
  • Python-玩转数据-Scrapy框架介绍及安装

    一 Scrapy框架说明 1 Scrapy介绍 Scrapy框架官方网址 http doc scrapy org en latest Scrapy是用纯Python实现一个为了爬取网站数据 提取结构性数据而编写的应用框架 用途非常广泛 用户
  • 如果访问云服务器上的文件,如果访问云服务器上的文件

    如果访问云服务器上的文件 内容精选 换一换 WinSCP工具可以实现在本地与远程计算机之间安全地复制文件 与使用FTP上传代码相比 通过 WinSCP 可以直接使用服务器账户密码访问服务器 无需在服务器端做任何配置 通常本地Windows计
  • [Qt]不带标题栏(FramelessWindowHint)的窗体移动及调整大小

    Qt窗体若设置了 setWindowFlags Qt FramelessWindowHint 运行后该窗体是无法进行移动和调整大小的 那要如何才能让它和普通窗体一样进行移动和调整其大小的呢 方案如下 void QFramelessWindo
  • 【八组输入输出你都了解多少】

    目录 前言 一 总览 一 只能从标准流 std 中输入和输出数据 二 可以从所以流中读取和写入数据 三 只能从字符串中读取和写入数据 二 详细描述 一 scanf fscanf sscanf 1 scanf 2 fscanf 3 sscan
  • 震惊,docker操作竟如此简单

    Docker概述 1 什么是Docker Docker 是一个开源的应用容器引擎 让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中 然后发布到任何流行的Linux和Windows操作系统的机器上 也可以实现虚拟化 容器是完全使用沙箱
  • c#—OpenFileDialog(打开文件对话框)

    OpenFileDialog是什么 OpenFileDialog是一个类 实例化此类可以设置弹出一个文件对话框 比如 我们发邮件时需要上传附件的时候 就会弹出一个让我们选择文件的对话框 我们可以根据自己的需求 自行设置一些对话框的属性 那么
  • ASP.NET WebAPI 连接数据库

    ASP NET Web API 是一种框架 用于轻松构建可以访问多种客户端 包括浏览器和移动设备 的 HTTP 服务 ASP NET Web API 是一种用于在 NET Framework 上构建 RESTful 应用程序的理想平台 本文
  • window11中Jdk1.8下载,安装和环境配置(超详细)

    一 下载安装包 这里为了方便大家 提供百度网盘下载 链接 https pan baidu com s 1 Qz7pO226To7yy6ytdPR Q https pan baidu com s 1 Qz7pO226To7yy6ytdPR Q
  • Windows——进程间通信

    进程间通信 进程间通信的概念 Mailslots 关于Mailslots 命名规则 使用 创建 Mailslot 写入 Mailslot 读取Mailslot 管道 关于管道 匿名管道 匿名管道创建 命名管道 命名规则 访问模式 相关操作
  • Spring Boot 多数据源及事务解决方案

    一个主库和N个应用库的数据源 并且会同时操作主库和应用库的数据 需要解决以下两个问题 如何动态管理多个数据源以及切换 如何保证多数据源场景下的数据一致性 事务 本文主要探讨这两个问题的解决方案 希望能对读者有一定的启发 1 数据源切换原理
  • 关于Springboot 无法捕获异常(@Transactional注解导致)

    在工作中发现了一个非常奇怪的事情 就是我突然间捕获不了异常 异常捕获 关于为什么会出现这样的问题呢 经过研究发现 原来是在这个类上 被加上了事务的注解 Transactional 这个事务的注解 就把我的异常给处理掉了 所以在这个被事务注解
  • 使用IntelliJ IDEA创建基于Gradle的kotlin项目

    一 首先打开Idea 选择Create New Project 二 选择Gradle项目并勾选 三 填写项目信息 四 然后一路默认下去就可以了 五 最终创建好的项目如下 六 写个HelloWorld
  • STM32F103ZET6【标准库函数开发】------按键扫描和外部中断的优先级对比

    1 打开正点原子的库函数源码可以看到关于按键的程序有两个 一个是按键输入实验 一个是外部中断实验 从最后体现的效果来看 这两个似乎是一样的 那么如果按键输入和外部中断冲突了 那么哪个优先级比较高呢 今天就来试试 2 首先还是简单介绍 硬件用
  • 什么是数据流图 Data Flow Diagram (DFD)

    什么是数据流图 DFD 如何绘制DFD 一张图片胜过千言万语 数据流图 DFD 是系统内信息流的传统视觉表示 一个整齐而清晰的DFD可以用图形描绘出大量的系统需求 它可以是手动的 自动的或两者的组合 它显示了信息如何进入和离开系统 什么改变
  • Windows Server --- RDP远程桌面服务器激活和RD授权

    RDP远程桌面服务器激活和RD授权 一 激活服务器 二 设置RD授权 系统 Window server 2008 R2 服务 远程桌面服务 注 该方法适合该远程桌面服务器没网络状态下 离线 激活服务器 一 激活服务器 1 打开远程桌面授权管
  • 链表的运用:多项式加法

    通过链表来实现两个多项式的加法 1 创建节点类型 用链表储存多项式则链表的一个节点就代表多项式的某一项 所以一个节点应该包含多项式的系数 多项式的指数以及指向下个节点的指针 2 打印多项式 传入一个指向多项式链表的指针 遍历该链表 依次打印
  • 线性代数的本质——几何角度理解

    B站网课来自 3Blue1Brown的翻译版 看完醍醐灌顶 强烈推荐 线性代数的本质 本课程从几何的角度翻译了线代中各种核心的概念及性质 对做题和练习效果有实质性的提高 下面博主来总结一下自己的理解 1 向量的本质 在物理中的理解是一个有起
  • Qt下实现支持多线程的单例模式

    1 代码介绍 实现单例模式的代码很多 本文的单例模式实现代码是本人一直在工程项目中使用的 现拿出和大家交流分享 本文实现的单例模式 支持多线程 采用双重校验检索的方式 集成析构类 杜绝内存泄漏 稳定性好 使用C Qt的朋友们可以了解一下 不