在项目中遇到这么一个问题,在程序退出时,在某个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; }
复现这个问题有两个关键点:
- wt对象,必须调用show()
- 在对象释放前,先释放app对象
那么显而易见,问题应该是出在app对象和widget对象的析构顺序上,但为什么必须要wt对象show()?
QWidget对象,在释放内存之前,如果处于显示的状态,那么widget会调用Close(),而默认情况下,CloseEvent()的处理方式是将窗口隐藏起来,然后在QWidget的源代码中,我找到了关于隐藏窗口的代码的实现,在这部分代码中,获取了QAppliction对象,然后做了一些操作,然而已这段代码为例,当wt对象被释放的时候,app对象已经被释放了,这个时候去访问那么必然会抛出访问冲突的异常。
问题是怎么发生的
正常来说app对象被释放,那么就意味着程序即将被关闭,什么情况下会在app对象都已经被释放的时间点后去释放一个QWidget对象呢?
在我的一个项目中,创建了一个单例,这个单例继承于QWidget。因为单例中的instance是一个静态对象,它将在程序结束时,被自动释放,而这个时间点app对象以及被释放掉了,所以就会抛出异常。
总结
QOject以及它派生类的对象,它们的某一些操作依赖于app对象提供的功能,所以 在app对象被释放之前一定要尽量将所有的QObject对象全部释放。
特别需要注意的就是静态对象,因为普通对象不太可能会在app对象释放之后才被释放。
文章评论