QT 上下文菜單內(nèi)存泄露之QMainWindow
QT 上下文菜單內(nèi)存泄露之QMainWindow 是本人要介紹的內(nèi)容,先來看內(nèi)容。創(chuàng)建Qt工程,基于QMainwindow,什么也不做,程序會自帶一個上下文菜單。
不斷點擊鼠標右鍵,菜單將反復(fù)出現(xiàn),此時我用任務(wù)管理器查看其內(nèi)存變化,發(fā)現(xiàn)每次不斷增加,請問大家這是Qt的內(nèi)存泄漏嗎???我用MFC,CB均沒有發(fā)現(xiàn)類此錯誤。
在Qt 4.7.0 和 4.7.3下可以重現(xiàn)該問題,在Qt 4.6.3下不存在該問題??梢源_定是Qt的一個bug。
問題重現(xiàn)
在工具欄或??看翱谥悬c擊右鍵(彈出上下文菜單),多點擊幾次,然后點擊按鈕。觀察控制臺輸出,可以看到很多個 QMenu 對象。
- #include <QtGui>
 - class MainWindow : public QMainWindow
 - {
 - Q_OBJECT
 - public:
 - explicit MainWindow(QWidget *parent = 0);
 - private slots:
 - void onButtonClicked();
 - };
 - MainWindow::MainWindow(QWidget *parent)
 - {
 - addToolBar("ToolBar");
 - addDockWidget(Qt::LeftDockWidgetArea, new QDockWidget("DockWidget"));
 - QPushButton * btn = new QPushButton("dump object tree");
 - setCentralWidget(btn);
 - connect(btn, SIGNAL(clicked()), SLOT(onButtonClicked()));
 - }
 - void MainWindow::onButtonClicked()
 - {
 - dumpObjectTree();
 - }
 - #include "main.moc"
 - int main(int argc, char *argv[])
 - {
 - QApplication a(argc, argv);
 - MainWindow w;
 - w.show();
 - return a.exec();
 - }
 
原因
既然是QMainWindow的上下文菜單問題,直接看 contextMenuEvent 事件處理函數(shù)吧。
- void QMainWindow::contextMenuEvent(QContextMenuEvent *event)
 - {
 - event->ignore();
 - ...
 - QMenu *popup = createPopupMenu();
 - if (popup) {
 - if (!popup->isEmpty()) {
 - popup->setAttribute(Qt::WA_DeleteOnClose);
 - popup->popup(event->globalPos());
 - event->accept();
 - } else {
 - delete popup;
 - }
 - }
 - }
 
看仔細嘍,這兒設(shè)置了 Qt::WA_DeleteOnClose 屬性。
有什么用?設(shè)置該屬性后,當(dāng)我們調(diào)用該對象的 close() 成員時,隱藏(hide)窗口同時會刪除(delete)該對象
有什么問題?問題出在,實際上隱藏菜單時沒有 調(diào)用菜單的close(),而是 調(diào)用的hide()的成員。
調(diào)用hide()而不是close(),是的該屬性不能發(fā)揮任何作用,進而導(dǎo)致內(nèi)存泄露(Qt 之 show,hide,setVisible,setHidden,close 等小結(jié) )。
為了對比,我們看看Qt4.6.3的源碼部分:
- void QMainWindow::contextMenuEvent(QContextMenuEvent *event)
 - {
 - event->ignore();
 - ...
 - QMenu *popup = createPopupMenu();
 - if (popup && !popup->isEmpty()) {
 - popup->exec(event->globalPos());
 - event->accept();
 - }
 - delete popup;
 - }
 
而這個,也就是我們的比較理想的答案了。
進一步學(xué)習(xí)
前面說了,菜單隱藏時調(diào)用的是hide() 成員,而不是close() 成員。有神馬依據(jù)??
想想?如何讓菜單隱藏
鼠標:點擊菜單外區(qū)域
鍵盤:按下Esc鍵等
這樣就比較明朗了,對吧,直接看這兩個事件處理函數(shù)
鍵盤的按鍵事件(調(diào)用了hideMenu)
- void QMenu::keyPressEvent(QKeyEvent *e)
 - {
 - Q_D(QMenu);
 - d->updateActionRects();
 - int key = e->key();
 - ...
 - bool key_consumed = false;
 - switch(key) {
 - case Qt::Key_Escape:
 - key_consumed = true;
 - {
 - QPointer<QWidget> caused = d->causedPopup.widget;
 - d->hideMenu(this); // hide after getting causedPopup
 - if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
 - mb->d_func()->setCurrentAction(d->menuAction);
 - mb->d_func()->setKeyboardMode(true);
 - }
 - }
 - break;鼠標在菜單區(qū)域外按鍵,調(diào)用了hideUpToMenuBar(進而調(diào)用hideMenu)
 - void QMenu::mousePressEvent(QMouseEvent *e)
 - {
 - Q_D(QMenu);
 - ...
 - if (!rect().contains(e->pos())) {
 - if (d->noReplayFor
 - && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
 - setAttribute(Qt::WA_NoMouseReplay);
 - if (d->eventLoop) // synchronous operation
 - d->syncAction = 0;
 - d->hideUpToMenuBar();
 - return;
 - }
 - }
 
前面都調(diào)用了hideMenu,從名字也能猜猜它想干什么:
- void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
 - {
 - ...
 - menu->hide();
 - }
 
小結(jié):QT 上下文菜單內(nèi)存泄露之QMainWindow 的內(nèi)容介紹完了,希望本文對你有所幫助!















 
 
 












 
 
 
 