关于QWidget与QApplication的析构顺序问题

2022年4月14日 703点热度 3人点赞 0条评论

在项目中遇到这么一个问题,在程序退出时,在某个QWidget的派生类的析构函数中,会抛出一个 XXX访问XXX出现冲突的异常,当时第一反应是对象被重复delete了,但经过一番查验,又与大佬讨论,发现事情的真相并不简单,遂记录,以供查阅。

测试代码

异常抛出的位置是在一个单例的析构函数中,这个单例继承于QWidget。

在析构函数处设置断点,发现对象并没有被重复析构,这个时候我有点蒙圈了,于是写了一小段代码来复现这个问题。

class MyWidget :public QWidget {
public:
    QWidget *wt;
    MyWidget() {
        wt = new QWidget();
        wt->show();
    };
    ~MyWidget() {
        delete wt;
    };
protected:
    void closeEvent(QCloseEvent* event) {
        QApplication::exit();
    }
};
int main(int argc, char *argv[])
{
    QApplication *a = new QApplication(argc, argv);
    MyWidget* widget = new MyWidget;
    widget->show();
    a->exec();
    delete a;
    delete widget;
    return 0;
}

复现这个问题有两个关键点:

  1. wt对象,必须调用show()
  2. 在对象释放前,先释放app对象

那么显而易见,问题应该是出在app对象和widget对象的析构顺序上,但为什么必须要wt对象show()?

QWidget对象,在释放内存之前,如果处于显示的状态,那么widget会调用Close(),而默认情况下,CloseEvent()的处理方式是将窗口隐藏起来,然后在QWidget的源代码中,我找到了关于隐藏窗口的代码的实现,在这部分代码中,获取了QAppliction对象,然后做了一些操作,然而已这段代码为例,当wt对象被释放的时候,app对象已经被释放了,这个时候去访问那么必然会抛出访问冲突的异常。

问题是怎么发生的

正常来说app对象被释放,那么就意味着程序即将被关闭,什么情况下会在app对象都已经被释放的时间点后去释放一个QWidget对象呢?

在我的一个项目中,创建了一个单例,这个单例继承于QWidget。因为单例中的instance是一个静态对象,它将在程序结束时,被自动释放,而这个时间点app对象以及被释放掉了,所以就会抛出异常。

总结

QOject以及它派生类的对象,它们的某一些操作依赖于app对象提供的功能,所以 在app对象被释放之前一定要尽量将所有的QObject对象全部释放。

特别需要注意的就是静态对象,因为普通对象不太可能会在app对象释放之后才被释放。

大脸猫

这个人虽然很勤快,但什么也没有留下!

文章评论