QT開發(十九)——QT內存泄漏問題

QT開發(十九)——QT內存泄漏問題

1、QT對象間的父子關係

    QT最基礎和核心的類是:QObject,QObject內部有一個list,會保存children,還有一個指針保存parent,當本身析構時,會本身從parent列表中刪除而且析構全部的children。ide

QT對象之間能夠存在父子關係,每個對象均可以保存它全部子對象的指針,每個對象都有一個指向其父對象的指針。函數

當指定QT對象的父對象時,父對象會在子對象鏈表中加入該對象的指針,該對象會保存指向其父對象的指針。spa

QT對象被銷燬時,將本身從父對象的子對象鏈表中刪除,將本身的子對象鏈表中的全部對象銷燬。指針

QT對象銷燬時解除和父對象之間的父子關係,並銷燬全部的子對象。對象

2、QT的半自動化內存管理

    一、QObject及其派生類的對象,若是其parent非0,那麼其parent析構時會析構該對象。若是父對象和子對象都分配在棧上,而且先釋放父對象的內存空間,釋放父對象的時候子對象的空間將會被釋放,當釋放子對象的空間時,子對象空間已經被釋放,會發生內存錯誤。繼承

    二、QWidget及其派生類的對象,能夠設置 Qt::WA_DeleteOnClose 標誌位(當close時會析構該對象)。隊列

    三、QAbstractAnimation派生類的對象,能夠設置 QAbstractAnimation::DeleteWhenStopped。事件

    四、QRunnable::setAutoDelete()、MediaSource::setAutoDelete()。內存

    五、父子關係:父對象、子對象、父子關係。這是Qt中所特有的,與類的繼承關係無關,傳遞參數是與parent有關(基類、派生類,或父類、子類,這是對於派生體系來講的,與parent無關)。開發

3、QT內存泄漏實例

1、實例一

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QLabel *label = new QLabel("Hello Qt!");

    label->show();

    return a.exec();

}

    label 沒有指定parent,也沒有對其調用delete,會形成內存泄漏。

    解決方案:

    A、分配對象到棧上而不是堆上

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QLabel label("Hello Qt!");

    label.show();

    return a.exec();

}

    B、設置標誌位,close()後會delete label。

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QLabel *label = new QLabel("Hello Qt!");

    label->show();

    label->setAttribute(Qt::WA_DeleteOnClose);

    return a.exec();

}

    C、在堆上new分配空間,delete手動釋放

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    int ret = 0;

    QApplication a(argc, argv);

    QLabel *label = new QLabel("Hello Qt!");

    label->show();

    ret = a.exec();

    delete label;

    return ret;

}

二、實例二

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QLabel label("Hello Qt!");

    label.show();

    label.setAttribute(Qt::WA_DeleteOnClose);

    return a.exec();

}

    label對象是在棧上分配的內存空間,delete棧上的地址會出錯。

三、實例三

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

  QApplication a(argc, argv);

  QLabel label("Hello Qt!");

    QWidget w;

    label.setParent(&w);

  w.show();

    return a.exec();

}

    w比label先被析構,當w被析構時,會刪除chilren列表中的對象label,但label是分配到棧上的,因delete棧上的對象而出錯。

    解決方案:

    A、調整父對象的位置,確保父對象析構時子對象已經析構

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QWidget w;

    QLabel label("Hello Qt!");

    label.setParent(&w);

    w.show();

    return a.exec();

}

    B、將子對象分配到堆空間

#include <QApplication>

#include <QLabel>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QLabel *label = new QLabel("Hello Qt!");

    QWidget w;

    label->setParent(&w);

    w.show();

    return a.exec();

}

四、實例四

    當一個QObject正在接受事件隊列時中途被銷燬了,會出現異常,因此QT中不要直接Delete掉一個QObject,若是必定要作,要使用QObject的deleteLater()函數,deleteLater()函數會讓全部事件都發送完一切處理好後清除這片內存,並且就算調用屢次的deletelater也不會有問題。

4、智能指針

一、QPointer

    QPointer是一個模板類,QPointer能夠監視動態分配空間的對象,而且在對象被 delete 的時候及時更新。

    QPointer的現實原理:在QPointer保存了一個QObject的指針,並把這個指針的指針(雙指針)交給全局變量管理,而QObject 在銷燬時(析構函數,QWidget是經過本身的析構函數的,而不是依賴QObject的)會調用QObjectPrivate::clearGuards 函數來把全局 GuardHash 的那個雙指針置爲零,由於是雙指針的問題,因此QPointer中指針固然也爲零了。用isNull 判斷就爲空了。

2std::auto_ptr

    auto_ptr被銷燬時會自動刪除它指向的對象。

5、垃圾回收機制

1QObjectCleanupHandler

    Qt 對象清理器是實現自動垃圾回收的很重要部分。QObjectCleanupHandler能夠註冊不少子對象,並在本身刪除的時候自動刪除全部子對象。同時,它也能夠識別出是否有子對象被刪除,從而將其從它的子對象列表中刪除。QObjectCleanupHandler類能夠用於不在同層次中的類的清理操做,例如,當按鈕按下時須要關閉不少窗口,因爲窗口的 parent 屬性不可能設置爲別的窗口的 button,此時使用QObjectCleanupHandler類就會方便。

#include <QApplication>

#include <QObjectCleanupHandler>

#include <QPushButton>

int main(int argc, char *argv[])

{

    QApplication a(argc, argv);

    QObjectCleanupHandler *cleaner = new QObjectCleanupHandler;

    QPushButton *w = new QPushButton("Remove me");

    w->show();

    cleaner->add(w);

    //點擊「remove me」按鈕刪除自身

    QObject::connect(w, SIGNAL(clicked()), w, SLOT(deleteLater()));

    w = new QPushButton("Nothing");

    cleaner->add(w);

    w->show();

    w = new QPushButton("Remove all");

    cleaner->add(w);

    w->show();

    //點擊「remove all」按鈕刪除全部QObject

    QObject::connect(w, SIGNAL(clicked()), cleaner, SLOT(deleteLater()));

    return a.exec();

}

    點擊Remove me」按鈕會刪除掉本身(經過 deleteLater() 槽),cleaner 會自動將其從本身的列表中清除。點擊Remove all」按鈕後會刪除cleaner,會同時刪除掉全部未關閉的窗口,即註冊在cleaner的QObject對象

相關文章
相關標籤/搜索