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

news2024/11/13 11:38:25

前言

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

正文

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

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

创建
// Singleton.h
#ifndef SINGLETON_H
#define SINGLETON_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Singleton; }
QT_END_NAMESPACE

class Singleton : public QWidget
{
    Q_OBJECT

public:
    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_OBJECT

public:
    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_OBJECT

public:
    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.coloradmin.cn/o/2147747.html

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

相关文章

【828华为云征文|华为云Flexus X实例部署指南:轻松搭建可道云KODBOX项目】

文章目录 华为云 Flexus X 实例&#xff1a;引领高效云服务的新时代部署【可道云KODBOX】项目准备工作具体操作指南服务器环境确认宝塔软件商店操作域名解析可道云KODBOX登录页效果验证 总结 华为云 Flexus X 实例&#xff1a;引领高效云服务的新时代 在云计算领域&#xff0c…

【UE5】使用2DFlipbook图作为体积纹理,实现实时绘制体积纹理【第一篇】

这是一篇对“Creating a Volumetric Ray Marcher-Shader Bits”的学习心得 文章时间很早&#xff0c;因此这里针对UE5对原文做出兼容性修正&#xff08;为避免累赘不做出注明。链接如上&#xff0c;有需要自行学习&#xff09; 以及最后对Custom做可能的蓝图移植&#xff0c;做…

虚拟机与物理机的文件共享

之前往虚拟机里传文件都是直接拖拽或者借助工具上传&#xff0c;都不太方便&#xff0c;倘若物理机的文件直接能在虚拟机里读取使用&#xff0c;那多好啊~ 1 虚拟机设置 注意文件夹名称不要中文/空格 2 验证Kali下分享文件夹功能是否启用 vmware-hgfsclient 3 创建挂载目录…

【踩坑】utools黑框和chrome白屏

记录一下bug&#xff0c;后面找到解决方案再同步 刚开机会黑框&#xff0c;但是输入wx能正常打开&#xff0c;功能一切正常 过一段时间会恢复正常 chrome会白屏 过一段时间恢复正常&#xff0c;大概是utools恢复正常时间的三倍 猜测是前两天关机时提示xx应用阻止关机&#…

旷视轻量化网络shufflenet算法解读

目录 预备知识 1. 回顾MobileNet V1的核心思想---深度可分离卷积 2.ShuffleNet主要有两个创新点 2.1 分组卷积与11分组卷积 2.2 channel Shuffle&#xff08;通道重排&#xff09; 2.3 通道重排过程 3. ShuffleNet网络结构 3.1 ShuffleNet unit 3.2 不同分组数的Shu…

Mamba YOLO World

论文地址&#xff1a;https://arxiv.org/pdf/2409.08513v1 代码地址&#xff1a; GitHub - Xuan-World/Mamba-YOLO-World: Mamba-YOLO-World: Marrying YOLO-World with Mamba for Open-Vocabulary Detection 开集检测&#xff08;OVD&#xff09;旨在检测预定义类别之外的物体…

上海做网站公司发展分析报告

上海网站公司发展分析报告 一、行业概况 上海作为中国的经济、金融和科技中心&#xff0c;其网站开发行业在近年来迅速发展。根据统计数据&#xff0c;当地的网站公司数量逐年增加&#xff0c;涵盖了企业网站、电子商务平台、移动应用开发等多个领域。随着互联网技术不断演进&…

维钧团队与广东能源集团携手共创未来

2024 年 9 月 19日&#xff0c;能源与投资领域传出一则令人振奋的消息——多元化投资团队维钧团队与广东能源集团正式达成合作&#xff0c;如同璀璨星辰交汇&#xff0c;开启了双方共同发展的壮丽新篇章。 维钧团队&#xff0c;以其敏锐专业的投资眼光、灵活多元的投资策略和卓…

大牛直播SDK核心音视频模块探究

技术背景 视沃科技旗下”大牛直播SDK”&#xff0c;始于2015年&#xff0c;致力于传统行业极致体验的音视频直播技术解决方案&#xff0c;产品涵盖跨平台的实时RTMP推流、RTMP/RTSP直播播放(支持RTSP|RTMP H.265&#xff0c;Enhanced RTMP H.265)、GB28181设备接入、推送端播放…

redis为什么不使用一致性hash

Redis节点间通信时&#xff0c;心跳包会携带节点的所有槽信息&#xff0c;它能以幂等方式来更新配置。如果采用 16384 个插槽&#xff0c;占空间 2KB (16384/8);如果采用 65536 个插槽&#xff0c;占空间 8KB (65536/8)。 今天我们聊个知识点为什么Redis使用哈希槽而不是一致性…

Llama 3.1 Omni:颠覆性的文本与语音双输出模型

你可能听说过不少关于语言模型的进展,但如果告诉你,有一种模型不仅能生成文本,还能同时生成语音,你会不会觉得特别酷?今天咱们就来聊聊一个相当前沿的项目——Llama 3.1 Omni模型。这个模型打破了传统的文字生成边界,直接让文本和语音同时输出,实现了真正的"多模态…

【mac】MacOS无法打开XXX文件,因为无法验证开发者的问题解决

博主介绍&#xff1a; 大家好&#xff0c;我是想成为Super的Yuperman&#xff0c;互联网宇宙厂经验&#xff0c;17年医疗健康行业的码拉松奔跑者&#xff0c;曾担任技术专家、架构师、研发总监负责和主导多个应用架构。 技术范围&#xff1a; 目前专注java体系&#xff0c;以及…

OpenCv(一)

计算机视觉和机器视觉的区别 计算机视觉&#xff08;Computer Vision&#xff09;和机器视觉&#xff08;Machine Vision&#xff09;是两个密切相关但又有区别的领域。两者在应用、技术和目标上都有所不同。 **计算机视觉&#xff1a;**主要是研究如何使计算机能够理解和处理…

单细胞代谢组学数据分析利器---SCMeTA

今天继续进行新的分享&#xff0c;为了支持单细胞代谢研究的严谨性和可重复性 &#xff0c;今天分享基于时间序列的单细胞代谢数据分析处理工作流程&#xff0c;名为 SCMeTA。它保留了可扩展的接口和插件系统&#xff0c;以适应来自各种仪器的数据。对从 QE-Orbitrap MS 获取的…

2024最新的软件测试面试八股文(答案+文档)

一、软件测试基础面试题 1、阐述软件生命周期都有哪些阶段? 常见的软件生命周期模型有哪些? 软件生命周期是指一个计算机软件从功能确定设计&#xff0c;到开发成功投入使用&#xff0c;并在使用中不断地修改、增补和完善&#xff0c;直到停止该软件的使用的全过程(从酝酿到…

黑马头条APP手工测试项目

1.app有关概念 APP测试范围&#xff1a; 业务功能测试 专项测试&#xff1a;兼容性测试 、安装/卸载/升级测试、交叉事件测试 、push消息推送测试、性能测试、其他测试&#xff08;用户体验、权限/边界、权限&#xff09; 功能测试测试对象&#xff1a; 功能点&#xff08;单…

Linux系统通过libgpiod读写GPIO

本文介绍Linux系统通过libgpiod读写GPIO。 从Linux 4.8后&#xff0c;官方不再推荐使用sysfs操作GPIO&#xff0c;目前&#xff0c;libgpiod是操作GPIO的首选方法。本文以Raspberry Pi 4开发板为例简要介绍通过libgpiod读写GPIO。 1.libgpiod简介 libgpiod是用于Linux环境下…

虚拟机:3、(待更)WSL2安装Ubuntu系统+实现GPU直通

WSL2实现linux子系统GPU直通 安装WSL2和Ubuntu 见https://blog.csdn.net/bule_shake/article/details/135992375 问题&#xff1a;wsl --update进度卡住 如果命令wsl --update进度一直为0&#xff0c;可以先运行wsl --shutdown&#xff0c;然后再次升级。 微软商店打不开、…

[2025]基于微信小程序慢性呼吸系统疾病的健康管理(源码+文档+解答)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

CTFshow——萌新隐写(未完待续)

萌新隐写2 首先暴力破解密码&#xff0c;初始密码设为19000000即可 我用的是ziperello 萌新隐写3 萌新隐写4 word打开 - > 打开设置 - > 隐藏文字 - >flag出现 萌新隐写5 中文转unicode 16进制转字符串 base32解码 萌新隐写6 暂时不会。。。。 隐写1 打开就看到头是…