目前来看QMenu的使用存在这几个问题。
1、如果使用了临时变量的QMenu,并且没有指定父窗体,例如:
QMenu menuDemo;
menuDemo.addAction(“test”);
menuDemo.exec();
那么会存在一个问题,就是如果使用某个快捷键将QMenu所在的Widget给关闭掉,QMenu并不会随着消失,这样子在流程上就存在着问题。
2、如果使用了临时变量的QMenu,并且指定父窗体,例如:
QMenu menuDemo(this);
menuDemo.addAction(“test”);
menuDemo.exec();
那么会存在一个问题,就是如果使用某个快捷键将QMenu所在的Widget给关闭掉,QMenu的父窗体会delete一次QMenu,然后QMenu作为临时变量又会被delete一次,就会导致crash的问题。
3、如果使用了指针QMenu,并且没有指定父窗体,例如:
QMenu *menuDemo = new QMenu;
menuDemo->addAction(“test”);
menuDemo->exec();
那么情况将会和1一样,使用某个快捷键将QMenu所在的Widget关闭掉,QMenu不会随着消失,改变了原本的流程,并且因为使用了指针,还会造成内存泄露。
综上所述,所以在使用QMenu的时候一定要使用指针的QMenu, 并且需要明确指定他的父窗体。例如在Preview这个Widget右键弹出一个QMenu,就需要用如下的方式进行创建
QMenu *menuDemo = new QMenu(Preview);
menuDemo->addAction(“test”);
menuDemo->exec();
这样子就可以保存在使用快捷键关闭Preview的时候,可以将menuDemo关闭,且销毁,不会造成内存泄露。
但是目前所说都是在关闭QMenu父窗体的时候,连带的将QMenu 销毁,不会造成内存泄露,但是如果没有销毁QMenu的父窗体,而是点击QMenu的时候造成的QMenu关闭,那么上面的写法依赖会造成内存泄露,所以此时就要使用QMenu的一个属性setAttribute(Qt::WA_DeleteOnClose), 这样就可以保证QMenu关闭的时候一定会被销毁,不会造成内存泄漏。
之所以这么修改就是为了解决目前再使用QMenu中存在的两个问题,一个是内存泄露,一个重复delete造成crash。如下的两个错误示例:
该示例中,如果在弹出QMenu的时候,如果使用快捷键(Ctrl+w)关闭文档,就会造成QMenu被析构两次,出现crash。
该示例就是会造成QMenu的内存泄露。
总结:
所以我们在使用QMenu的时候需要注意:
-
采用指针。
-
明确QMenu的父窗体
-
设置属性WA_DeleteOnClose
就像这样:
QMenu *menuDemo = new QMenu(Preview);
menuDemo->setAttribute(Qt::WA_DeleteOnClose);
menuDemo->addAction(“test”);
menuDemo→exec();
补充:如果QMenu中存在存在子Menu,需要将子Menu的parent设置为父Menu,否则可能会出现父Menu关闭了,但是子Menu没有关闭。
QMenu *menuDemo = new QMenu(Preview);
QMenu *subMenu = new QMenu("subMenu", menuDemo);
menuDemo->setAttribute(Qt::WA_DeleteOnClose);
menuDemo→addAction(“test”);
menuDemo->addMenu(subMenu);
menuDemo→exec();
补充:QMenu的exec中可能会存在的问题,假如现在有一个场景:存在一个Widget,做了右键操作,代码如下:
QMenu *menuDemo = new QMenu(Widget);
menuDemo->setAttribute(Qt::WA_DeleteOnClose);
menuDemo->addAction(“test”);
menuDemo→exec();
Widget.update();
但是你最后的在exec后还有使用到该Widget,但是实际上通过快捷键的方式可以再QMenu执行exec的时候将Widget销毁,这个时候就会造成Widget野指针,导致crash问题,对于该问题目前有两种想法,一种可以采用QMenu的popup这种异步的方式,然后将Widget.update()放入到对应的test的action中执行,这种方式对流程上可能会有所改变,所以需要对应的模块具体问题具体进行分析。
另外一种就是可以考虑采用只能指针的方式,代码如下:
QMenu *menuDemo = new QMenu(Widget);
QPointer<QWidget> pointer= Widget;
menuDemo->setAttribute(Qt::WA_DeleteOnClose);
menuDemo->addAction(“test”);
menuDemo→exec();
if(!pointer)
return;
Widget.update();
就是采用智能指针来判断该Widget是否已经被释放,如果已经被释放则不再往下执行。