Qt:懒汉单例(附带单例使用和内存管理)

前言

本文主要写懒汉单例以及单例的释放,网上很多教程只有单例的创建,但是并没有告诉我们单例的内存管理,这就很头疼。

正文

以下是两种懒汉单例的写法

1. 懒汉式单例(多线程不安全,但是在单线程里面是安全的)

创建
// Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Singleton; }
QT_END_NAMESPACEclass Singleton : public QWidget
{Q_OBJECTpublic:static Singleton* getInstance();
private:// 私有化构造函数,防止外部创建实例Singleton(QWidget *parent = nullptr);// 禁止拷贝构造和赋值操作Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;~Singleton();private:Ui::Singleton *ui;// 创建静态指针变量static Singleton* instance;
};
#endif // SINGLETON_H// Singleton.cpp
#include "singleton.h"
#include "ui_singleton.h"
#include "qdebug.h"
// 静态变量需要在类外进行初始化
Singleton* Singleton::instance = nullptr;Singleton *Singleton::getInstance()
{if (instance == nullptr) {// 使用构造函数instance = new Singleton();}return instance;
}Singleton::Singleton(QWidget *parent): QWidget(parent), ui(new Ui::Singleton)
{ui->setupUi(this);}Singleton::~Singleton()
{qDebug()<<"单例安全销毁";delete ui;
}

解释:

  • 懒汉式在第一次调用时创建实例,延迟初始化。但未加锁,在多线程环境下不安全。

使用

//UseSingleton.h
#ifndef USESINGLETON_H
#define USESINGLETON_H#include <QWidget>namespace Ui {
class UseSingleton;
}class UseSingleton : public QWidget
{Q_OBJECTpublic:explicit UseSingleton(QWidget *parent = nullptr);~UseSingleton();private slots:// 这里我在UseSingleton.ui中添加了一个按钮,用于创建单例void on_pushButton_clicked();private:Ui::UseSingleton *ui;
};#endif // USESINGLETON_H//UseSingleton.cpp
#include "usesingleton.h"
#include "ui_usesingleton.h"
#include "singleton.h"
UseSingleton::UseSingleton(QWidget *parent) :QWidget(parent),ui(new Ui::UseSingleton)
{ui->setupUi(this);
}UseSingleton::~UseSingleton()
{delete ui;
}void UseSingleton::on_pushButton_clicked()
{// 创建单例,但是这里是局部变量,只能在这里使用,也可以将创建一个单例类的成员对象Singleton* instance = Singleton::getInstance();instance->show();
}
内存管理

此处的单例类是作为局部变量来创建的,在更安全的懒汉中我将单例类作为成员变量来创建来展示内存管理。

  • 1.当单例是一个窗口类时,我们可以重写closeEvent来管理内存,即使得窗口关闭时,销毁单例,代码如下
// 在Singleton类中添加如下代码
void Singleton::closeEvent(QCloseEvent *)
{// 销毁对象instance->deleteLater();// 指针置空非常重要instance = nullptr;
}
程序运行结果

当我通过按钮重复创建对象后,并且关闭单例窗口类时,单例能安全销毁。
在这里插入图片描述

  • 1.1当单例是窗口类时,我们也可以通过,设置Qt::WA_DeleteOnClose属性来管理内存,代码如下:
// 在Singleton构造函数中添加
Singleton::Singleton(QWidget *parent): QWidget(parent), ui(new Ui::Singleton)
{ui->setupUi(this);// 添加this->setAttribute(Qt::WA_DeleteOnClose,true);}
// 在析构函数中添加
Singleton::~Singleton()
{qDebug()<<"单例安全销毁";// 添加置空,置空非常重要instance = nullptr;delete ui;
}
程序运行结果

当我通过按钮重复创建对象后,并且关闭单例窗口类时,单例能安全销毁。
在这里插入图片描述

注意:当你按照我以上的方法管理内存时,你就不要更改我的单例,不要在栈上创建单例,否则delete栈上的空间程序直接崩溃不要来找我。

  • 1.2自己管理内存。
    这个你参考下面单例是非窗口类中的自己管理内存吧,都一样.
    这里说明下,为什么每次销毁完对象要指针置空,因为我们存储对象的指针是静态的,所以初始化的时候只会初始化一次,要是你的单例是主程序还好,像上面我的类中单例类并不是主程序,使用单例类的类才是主程序,所以当我将单例对象销毁后(此时主程序并没有结束),再次创建单例对象的时候,程序就会崩溃,因为我的指针并不是空的,它就不会执行new那一部分,而是直接返回一个空的内容,所以程序会崩溃。感兴趣的朋友可以自己尝试下,或者我们私下交流下。
  • 2 当单例类不是窗口类的时候,我们可以自己管理内存,具体实现是自己写一个销毁单例的函数,如下
// 新建一个没有窗口的类
// SingletonNoUi.h
#ifndef SINGLETONNOUI_H
#define SINGLETONNOUI_H#include <QObject>class SingleTonNoUi : public QObject
{Q_OBJECT
public:static SingleTonNoUi* getInstance();// 销毁单例static void destoryInstance();
private:explicit SingleTonNoUi(QObject *parent = nullptr);// 禁止拷贝构造和赋值操作SingleTonNoUi(const SingleTonNoUi&) = delete;SingleTonNoUi& operator=(const SingleTonNoUi&) = delete;~SingleTonNoUi();
signals:private:// 创建静态指针变量static SingletonNoUi* instance;
};#endif // SINGLETONNOUI_H// SingletonNoUi.cpp
#include "singletonnoui.h"
#include "qdebug.h"
// 初始化静态变量
SingleTonNoUi*SingleTonNoUi::instance = nullptr;SingleTonNoUi *SingleTonNoUi::getInstance()
{if (instance == nullptr) {instance = new SingleTonNoUi();}return instance;
}
void SingleTonNoUi::destoryInstance()
{if (instance) {instance->deleteLater();// 指针置空非常重要instance = nullptr;}
}
SingleTonNoUi::~SingleTonNoUi()
{qDebug()<<"非窗口单例类安全销毁";
}SingleTonNoUi::SingleTonNoUi(QObject *parent) : QObject(parent)
{qDebug()<<"非窗口单例创建成功";
}//在UseSingleton中再添加一个按钮,转到槽;在槽函数中添加
void UseSingleton::on_pushButton_2_clicked()
{SingleTonNoUi* instance = SingleTonNoUi::getInstance();/*其它处理逻辑*/instance->destoryInstance();
}
程序运行结果

刚创建会被直接销毁
在这里插入图片描述

  • 2.1使用智能指针来管理内存,但是这种方法需要对原先的单例做出一些改变,代码如下
// SingletonNoUi.h
#ifndef SINGLETONNOUI_H
#define SINGLETONNOUI_H#include <QObject>
#include <QScopedPointer>class SingleTonNoUi : public QObject
{Q_OBJECT
public:static SingleTonNoUi* getInstance();// 需要将析构函数声明为public,要不然智能指针管理不了~SingleTonNoUi();
private:explicit SingleTonNoUi(QObject *parent = nullptr);// 禁止拷贝构造和赋值操作SingleTonNoUi(const SingleTonNoUi&) = delete;SingleTonNoUi& operator=(const SingleTonNoUi&) = delete;signals:private:// 创建静态指针变量static QScopedPointer<SingleTonNoUi> instance;
};#endif // SINGLETONNOUI_H// SingletonNoUi.cpp
#include "singletonnoui.h"
#include "qdebug.h"
// 初始化静态成员变量,此处不能赋予nullptr
QScopedPointer<SingleTonNoUi> SingleTonNoUi::instance;SingleTonNoUi *SingleTonNoUi::getInstance()
{if(instance.isNull()) {instance.reset(new SingleTonNoUi());}return instance.data();
}SingleTonNoUi::~SingleTonNoUi()
{qDebug()<<"非窗口单例类安全销毁";
}SingleTonNoUi::SingleTonNoUi(QObject *parent) : QObject(parent)
{qDebug()<<"非窗口单例创建成功";
}
//注意去掉UseSingleton类的槽函数的destoryInstace,即
void UseSingleton::on_pushButton_2_clicked()
{SingleTonNoUi* instance = SingleTonNoUi::getInstance();/*其它处理逻辑*/
}
程序运行结果

点击创建按钮后,输出框显示非窗口单例创建成功;当我再次点击创建按钮时,没有任何变化(只要想想就会理解,因为此时我的单例类又没有被销毁,单例只能存在一个,第二个单例自然就不可能创建了),关闭主窗口,被正常销毁。
在这里插入图片描述

2. 懒汉式单例(线程安全)

// 还是和上面一样的类,只更改getInstance中的内容就行了
// Singleton.cpp中
#include <QMutex>static Singleton* getInstance() {// 添加锁机制确保线程安全static QMutex mutex;if (instance == nullptr) {// 加锁,确保多线程环境下的安全性,使用locker()不用显示的解锁QMutexLocker locker(&mutex);// 双重检查,防止多次创建if (instance == nullptr) {    instance = new Singleton();}}return instance;
}

解释:

  • 线程安全的懒汉式单例通过 QMutex 加锁,确保在多线程环境中实例只被创建一次。

使用

这里是单例类作为成员变量时的内存管理,所以要在UseSingleton中添加SingletonSingletonNoUi这两个类的成员变量,如下

#ifndef USESINGLETON_H
#define USESINGLETON_H#include <QWidget>
#include "singleton.h"
#include "singletonnoui.h"
namespace Ui {
class UseSingleton;
}class UseSingleton : public QWidget
{Q_OBJECTpublic:explicit UseSingleton(QWidget *parent = nullptr);~UseSingleton();private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::UseSingleton *ui;// 添加两个类的成员变量Singleton* instance;SingleTonNoUi* instanceNoUi;
};#endif // USESINGLETON_H

内存管理

当单例类是窗口类时:
其实和上面的单例对象作为局部变量一样。

当单例类是非窗口类时:

其实和上面的单例对象作为局部变量一样。

为什么不适用Qt中的父子机制来管理单例内存?

在Qt中,单例模式一般不使用父子机制来管理内存。因为单例模式的设计目的是保证在整个程序运行期间,某个类只有一个实例,并且它的生命周期通常贯穿整个应用程序。而Qt的父子(如QObject的父子关系)主要用于管理对象的生命周期,当父对象被销毁时,子对象也被自动销毁。单例对象的生命周期通常不与父对象绑定,所以父子机制不太适合管理单例的生命周期

小结

如有错误请指正。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

protobuf中c、c++、python使用

文章目录 protobuf实例&#xff1a;例题1&#xff1a;[CISCN 2023 初赛]StrangeTalkBot分析&#xff1a;思路&#xff1a;利用&#xff1a; 例题2&#xff1a;[CISCN 2024]protoverflow分析&#xff1a; protobuf Protocol Buffers&#xff0c;是Google公司开发的一种数据描述语…

二十三种设计模式之原型模式

一.什么是原型模式 ‌‌原型模式是一种创建型对象设计模式&#xff0c;它通过复制一个已经创建的实例&#xff08;即原型对象&#xff09;来创建一个和原型对象相同的新对象。‌ 这种模式在面向对象软件设计中非常有用&#xff0c;因为它允许通过复制现有对象来快速生成多个相似…

新160个crackme - 057-bbbs-crackme04

运行分析 因软件版本老旧&#xff0c;需使用windows XP虚拟机运行有个SystemID&#xff0c;值为12345678需破解User ID和Password PE分析 yC壳&#xff0c;32位 OD手动脱壳 使用windows XP虚拟机&#xff0c;将程序拖入OD按一下F8&#xff0c;ESP变红&#xff0c;根据ESP定律设…

子比主题美化 - 可移动悬浮窗 弹窗功能代码教程

移动页面演示效果 这个功能完全适配子比主题使用&#xff0c;代码开源&#xff0c;可以做其它功能弹窗或者菜单栏等等&#xff0c;后期有时间在做成桌面页面也可以鼠标移动&#xff0c;点击参考&#xff1a;移动悬浮窗详细代码教程

黑马十天精通MySQL知识点

一. MySQL概述 安装使用 MySQL安装完成之后&#xff0c;在系统启动时&#xff0c;会自动启动MySQL服务&#xff0c;无需手动启动。 也可以手动的通过指令启动停止&#xff0c;以管理员身份运行cmd&#xff0c;进入命令行执行如下指令&#xff1a; 1 、 net start mysql80…

SpringBoot父子工程搭建

SpringBoot父子工程搭建 1、父工程 1.1、创建父工程 1.2、移除无用文件 1.3、修改pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XML…

秋韵虫趣.

文章目录 虫鸣概览虫坛文化蟀种纷呈中华蟋蟀宁阳蟋蟀刻点铁蟋长颚斗蟋 油葫芦棺头蟋中华灶蟋小素蟋树皮蟋蟀 花生大蟋斑腿针蟋其他鸣虫树蟋&#xff0c;又名竹蛉、邯郸梨片蟋&#xff0c;又名金钟、天蛉、绿蛣蛉、银琵琶凯纳奥蟋&#xff0c;又名石蛉&#xff0c;鳞蟋黄蛉蟋&am…

NarratoAI利用AI大模型,一键解说并剪辑视频

测试视频: 字幕/配乐后期添加的,视频由NarratoAI自动生成的 雪迷宫-NarratoAI利用AI大模型剪辑解说视频测试 WIN整合包 下载链接&#xff1a;https://pan.quark.cn/s/8f54ef99e3fb 使用前先更新&#xff0c;运行update.bat Gemini API Key 访问 https://aistudio.google.c…

quartz 搭配SQL Server时出现deadlock的解决方案

背景&#xff1a; 最近在折腾换OA系统&#xff0c;遇到了一个很诡异的事情。在测试阶段&#xff0c;OA系统经常莫名地宕机&#xff0c;停止响应。查下来&#xff0c;发现是数据库出现大量死锁&#xff0c;耗尽了连接池。出现问题的语句是一样的&#xff0c;问题锁定在QRTZ_TRI…

C++ 面试必备知识大全:从基础到高级特性全面解析

创作不易&#xff0c;您的打赏、关注、点赞、收藏和转发是我坚持下去的动力&#xff01; C 面试中常见的问题涵盖了语言基础、面向对象编程、内存管理、STL&#xff08;标准模板库&#xff09;、并发编程、设计模式等。以下是一些常见的 C 面试问题及其详细答案总结&#xff1…

第311题| 超好用!二重积分求旋转体体积公式|武忠祥老师每日一题

第一步&#xff1a; &#xff08;1&#xff09;找渐近线&#xff0c;先看水平渐近线&#xff0c;看x趋于无穷时&#xff0c;y有没有趋于一个有限的值。 , 得出水平渐近线y1。因为左右两边都是水平渐近线&#xff0c;所以没有斜渐近线。 第二步&#xff1a; 画出图像&#…

e选择排序---复杂度O(X^2)

排序原理: 1.每一次遍历的过程中&#xff0c;都假定第一个索引处的元素是最小值,和其他索引处的值依次进行比较,如果当前索引处的值大于其他某个素引处的值&#xff0c;则假定其他某个索引出的值为最小值&#xff0c;最后可以找到最小值所在的索引 2.交换第一个索引处和最小值所…

LocalMamba: Visual State Space Model with Windowed Selective Scan 论文总结

题目&#xff1a;LocalMamba: Visual State Space Model&#xff08;视觉状态空间模型&#xff09; with Windowed Selective Scan&#xff08;窗口化的选择扫描&#xff09; 论文&#xff1a;[2403.09338] LocalMamba: Visual State Space Model with Windowed Selective Scan…

STM32F407单片机开发入门(二)STM32F407VET6单片机详解

文章目录 一.概要二.单片机型号命名规则三.STM32F407系统架构四.STM32F40VET6单片机启动流程五.STM32F40VET6单片机主要外设资源六.开发过程中查看芯片数据手册的必要性1.单片机外设资源情况2.STM32F407单片机内部框图3.STM32F407单片机管脚图4.STM32F407单片机每个管脚功能5.单…

艾里斑(Airy Disk)与瑞利判据(Rayleigh criterion)

艾里斑的定义与形成 艾里斑&#xff08;Airy Disk&#xff09;&#xff0c;又称艾里图样&#xff08;Airy Pattern&#xff09;&#xff0c;是光通过圆形孔径发生衍射时&#xff0c;在焦点处形成的光斑。这一现象由英国数学家和天文学家George Biddell Airy在19世纪首次详细描…

Linux服务器中在指定python环境中新建notebook

在指定环境下建立新的IPython内核pip install ipykernel生成ipykernel的配置文件&#xff1a;python -m ipykernel install --user --name netCLR 其中--user是指在用户级别生成&#xff0c;如果没有--user就会在系统中安装&#xff0c;如果不是管理员的话就会没有权限生成&…

Acrobat XI 安装教程

软件介绍 Adobe Acrobat 是由Adobe公司开发的一款PDF&#xff08;Portable Document Format&#xff0c;便携式文档格式&#xff09;编辑软件。借助它&#xff0c;可以以PDF格式制作和保存文档&#xff0c;以便于浏览和打印&#xff0c;同时还可以使用一些高级工具来创建、编辑…

MySQL练手题--公司和部门平均工资比较(困难)

一、准备工作 Create table If Not Exists Salary (id int, employee_id int, amount int, pay_date date); Create table If Not Exists Employee (employee_id int, department_id int); Truncate table Salary; insert into Salary (id, employee_id, amount, pay_date) va…

FreeRTOS—任务通知

一&#xff0c;概念介绍 队列、信号量、事件组等IPC技术都需要创建一个中间对象进程之间通过这些中间对象进行通讯或同步。创建对象就需要分配内存&#xff0c;占用一定内存。 二&#xff0c;任务通知的特点&#xff1a; 一个任务或ISR向另外一个指定的任务发送通知&#xff0c…

『功能项目』切换职业技能面板【49】

我们打开上一篇48切换职业面板的项目&#xff0c; 本章要做的事情是制作第二职业法师技能面板、第三职业面板并且完成切换 双击打开Canvas进入预制体空间 复制三个技能栏面板 重命名 设置第一技能栏 设置第二职业技能栏 设置第三职业技能栏 修改脚本&#xff1a;ChangeProfess…