目前來看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是否已經被釋放,如果已經被釋放則不再往下執行。