Qt单例类模板,继承自QObject的类使用,方便复用,自动垃圾回收

3 篇文章 0 订阅

1. Qt使用标准c++单例类模板出现的问题

之前写过一篇c++单例类模板,但是那个只适用于标准c++的类。在Qt中使用时遇到了问题。

我的使用场景:有些界面参数配置或者内容需要全局访问或者要在多个地方显示,但是所有地方需要保持数据的一致性,所以使用单例保证进程全局只有一个实例。但是Qt的界面类顶层都是继承自QObject的,界面一旦被设置了parent之后,对象的控制权便交给了Qt,由Qt做内存管理。界面关闭的时候,Qt会自动按照父子顺序来销毁界面实例,也就是我的单例界面实例内存被系统回收,但是地址不为空。
如果我使用c++的类模板,程序退出时,gc也会进行内存释放,但此时Qt已经将实例释放掉了,这个地址空间已经被系统回收,无法再访问,此时就会导致重复析构,程序会报错。

正常释放顺序应该是下面这样:

init instance // 程序启动输出init
delete instance // 关闭window后,gc释放实例
delete myclass // gc中delete m_pInstance 会调用MyClass的析构函数。

但是当我使用c++的单例类模板给Qt的界面使用时,程序退出时,析构顺序是这样的:

init instance // 程序启动输出init
delete myclass ui // Qt根据对象树释放内存,会调用MyClassUI的析构函数。此时我的界面实例已经被销毁,内存被系统回收。
delete instance // 程序退出,gc释放实例,此时delete想要访问的是系统保护的内存,程序便会报错。

2. 解决方法

使用Qt的方法管理内存:QObjectCleanupHandler

QObjectCleanupHandler 可以监视多个QObject对象的生命周期。并且最大的优点是,当对象在别的地方被删除后,会自动从QObjectCleanupHandler 中移除,并且可以通过isEmpty()来判断当前QObjectCleanupHandler 中是否还有监视对象。然后可以使用clear()方法直接删除所有的监视对象,而且当QObjectCleanupHandler 对象析构后,也会自动删除所有监视对象。

3. 具体实现

#include <mutex>
#include <QObjectCleanupHandler>

template <class T>
class QSingleton
{
private:
    static T* m_pInstance;
    QSingleton(const QSingleton& other) {}
    QSingleton& operator=(const QSingleton& other) {}

//    class Gc
//    {
//    public:
//        ~Gc()
//        {
//            if (!QSingleton::m_cleanupHandler.isEmpty()) {
//                QSingleton::m_cleanupHandler.clear();
//            }
//        }
//    };

    static QObjectCleanupHandler m_cleanupHandler;  //声明变量

protected:
    QSingleton() {}
    ~QSingleton() {}

public:
    static T* getInstance()
    {
        static std::mutex lock;
        // static Gc gc; // m_cleanupHandler是静态全局变量,程序退出时会析构,并删除其所监视的对象,所以不需要gc

        if (m_pInstance == nullptr) {
            std::lock_guard<std::mutex> locker(lock);
            if (m_pInstance == nullptr)
            {
                m_pInstance = new T();
                m_cleanupHandler.add(m_pInstance);
            }
        }
        return m_pInstance;
    }
};

template <class T>
T* QSingleton<T>::m_pInstance = nullptr;
template <class T>
QObjectCleanupHandler QSingleton<T>::m_cleanupHandler; // 类外初始化变量,初始化后才可以使用,否则不起作用

使用方法和c++单例类模板相同,可以参考上一篇关于c++单例类模板的博文

4. 测试结果

输出结果:

  1. 不给单例界面类指定父对象:
clean up instance // 由m_cleanupHandler释放实例,触发界面的析构函数
myclass ui delete  // 执行界面析构函数输出
  1. 给界面类指定父对象:
parent main window delete  // 程序退出父界面析构输出,同时析构子对象
myclass ui delete  // myclassui作为子对象被析构,执行界面析构函数输出,此过程由Qt控制。再到gc释放,
					// m_cleanupHandler.isEmpty()判断为空,不会重复析构此单例的实例。

所以继承自QObject的类可以应用此版本的单例类模板,不能用标准c++的版本。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

VectorAL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值