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

embedded/2024/9/22 23:02:47/

前言

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

正文

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

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.ppmy.cn/embedded/112844.html

相关文章

Linux 监控I/O的状态

在进行 I/O 密集型操作时&#xff0c;可以通过多种工具来查看当前的 I/O 状态&#xff0c;获取与磁盘或文件系统相关的活动信息。这些工具可以帮助你监控 I/O 操作的性能&#xff0c;发现潜在的瓶颈或问题。 1. 使用 iostat 查看 I/O 负载 iostat 是一个常用的工具&#xff0…

关于RabbitMQ消息丢失的解决方案

RabbitMQ如何保证消息的可靠性传输 一、消息丢失的原因 1. 生产者端 网络问题&#xff1a; 原因&#xff1a;生产者与RabbitMQ服务器之间的网络连接不稳定或中断&#xff0c;导致消息在传输过程中丢失。解决方案&#xff1a;确保网络连接稳定&#xff0c;监控网络状态&#x…

[网络]https的概念及加密过程

文章目录 一. HTTPS二. https加密过程 一. HTTPS https本质上就是http的基础上增加了一个加密层, 抛开加密之后, 剩下的就是个http是一样的 s > SSL HTTPS HTTP SSL 这个过程, 涉及到密码学的几个核心概念 明文 要传输的真正意思是啥 2)密文 加密之后得到的数据 这个密文…

C语言编译原理

目录 一、C语言的编译过程 二、预处理 三、编译阶段 3.1 词法分析&#xff08;Lexical Analysis&#xff09; 3.2 语法分析&#xff08;Syntax Analysis&#xff09; 语法分析的主要步骤&#xff1a; 语法分析的关键技术&#xff1a; 构建AST&#xff1a; 符号表的维护…

【学习笔记】SSL密码套件之哈希

本篇将介绍TLS/SSL密码套件中常用的哈希算法&#xff0c;包括Poly1305、SHA384、SHA256、SHA、MD5 以上的哈希算法将作为 MAC 使用 MAC - Message Authentication Code 为批量数据提供了完整性&#xff08;Integrity&#xff09;以及真实性&#xff08;Authentication&#xf…

Unity3D 小案例 像素贪吃蛇 02 蛇的觅食

Unity3D 小案例 像素贪吃蛇 第二期 蛇的觅食 像素贪吃蛇 食物生成 在场景中创建一个 2D 正方形&#xff0c;调整颜色&#xff0c;添加 Tag 并修改为 Food。 然后拖拽到 Assets 文件夹中变成预制体。 创建食物管理器 FoodManager.cs&#xff0c;添加单例&#xff0c;可以设置…

LineageOS源码下载和编译(Xiaomi Mi 6X,wayne)

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ 源码下载 LineageOS官网&#xff1a;https://lineageos.org/ LineageOS源码 github 地址&#xff1a;https://github.com/LineageOS/android LineageOS源码…

opencv anaconda yolov5安装流程

目录 1. opencv-4.72. anaconda的配置2.1 配置环境变量2.2 配置anaconda源2.3 常用的命令 3. Yolov5环境的安装 1. opencv-4.7 下载opencv-4.7的安装包 https://opencv.org/releases/ 安装最新的cmake sudo apt-get update sudo apt-get upgrade sudo apt install cmake安装o…