總結一下Qt內存泄漏檢測與處理策略

一、QtCreator編寫C++代碼,怎麼檢測內存泄漏?

請參見本人的另外一篇博文:http://www.javashuo.com/article/p-daoasbsj-cz.htmlhtml

 

二、Qt中控件new以後需不須要delete的問題

https://blog.csdn.net/Aidam_Bo/article/details/86303096
 * QT的父子對象機制是在 QWidget和QOject中實現的。當咱們使用父對象來建立一個對象的時候 ,父對象會把這個對象添加到本身的子對象列表中。當這個父對象被刪除的時候,它會遍歷它的子對象類表而且刪除每個子對象,而後子對象們本身再刪除它們本身的子對象,這樣遞歸調用直到全部對象都被刪除。
 * 這種父子對象機制會在很大程度上簡化咱們的內存管理工做,減小內存泄露的風險。
 * 咱們須要顯式刪除(就是用Delete刪除)的對象是那些使用new建立的而且沒有父對象的對象(切記是new的纔要delete,
 * 經過成員函數得到的對象,沒有特殊說明的,千萬不要隨便delete。
 * 若是咱們在刪除一個對象的父對象以前刪除它,QT會自動地從它的父對象的子對象列表中移除它的。
 * Qt自動回收不像Java這種,有垃圾回收機制。
 * Qt自動回收是靠父子關係。父親銷燬了。他的孩子也銷燬。
 * 因此爲何main函數裏面main widget/dialog/mainWindow是分配在棧上的緣由。
 * 其餘new出來的東西都以這個widget做爲父親。 當程序最後結束了,main widget彈棧。
 * 父類被銷燬。子類跟着被銷燬。 因此你本身new出來的控件,若是沒有父類,本身又不刪除,那就會形成內存泄漏。









安全

小結--Qt的半自動化的內存管理:app

(1)QObject及其派生類的對象,若是其parent非0,那麼其parent析構時會析構該對象。函數

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

(3)QAbstractAnimation派生類的對象,能夠設置 QAbstractAnimation::DeleteWhenStopped。this

(4)QRunnable::setAutoDelete()、MediaSource::setAutoDelete()。spa

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

 

三、Qt智能指針和QObject對象樹系統(父子系統)結合使用出現的問題

https://blog.csdn.net/gamesdev/article/details/8724090指針

 * 首先要弄明白爲何qt只new不delete,父子對象管理模式先弄清楚,另外QScopedPoint有個接口data,返回正在管理的原始指針。
 * QObject自有對象樹系統(父子系統),它在和其它QObject子類進行交互的時候會將對方的指針保存起來,造成父子關係,
 * 最終一個QObject子類指針會造成一個強大的樹狀結構,當父親銷燬的時候,會先銷燬它的孩子(若是它的孩子是經過new操做符在堆上建立的話)。可是智能指針在保有QObject子類的時候會自動調用它的析構函數,從而引發事實上的兩次delete,這個時候編譯器的就會報錯。
 * 那我熟悉的QScopedPointer來講,原本將它用在類的成員中是一個很好的選擇,可是因爲它保有的是QObject的子類,這個智能指針在和其它QObject子類交互的時候不免會被對方保有原始指針的值,在進入類的析構函數,QScopedPointer保有原始指針的值會被先於釋放並置爲「已刪除」的值0xfeeefeee,這個時候再經過智能指針的自動清理只可能會帶來運行錯誤。在qscopedpointer.h源碼中,咱們看到QScopedPointerDeleter類的cleanup靜態函數並不帶有在delete以前的指針的值檢測,因而在delete一個無效的指針時,錯誤發生了。


code


四、爲何官方的Qt示例和教程不使用智能指針?

目前得出了一個結論:只要加入了QObject對象樹系統(父子機制),那麼內存管理不是你的事兒了,你也不該該管,也不該該讓智能指針管。
 * 智能指針類std::unique_ptr和std::shared_ptr是內存管理。擁有這樣一個智能指針意味着,你擁有指針。
 * 可是,在QObject使用QObject父級建立或派生類型時,全部權(清理責任)將交給父級QObject。
 * 在這種狀況下,標準庫智能指針是沒必要要的,甚至是危險的,由於它們可能會致使雙重刪除。
 * 然而,當一個QObject在堆上建立而沒有父類時,QObject狀況就很是不一樣。在這種狀況下,你不該該只保存一個原始指針,
 * 而是一個智能指針,最好是一個std::unique_ptr對象。這樣你就能夠得到資源安全。
 * 若是你稍後將對象全部權交給QObject你可使用的父項std::unique_ptr<T>::release(),以下所示:
 * auto obj = std::make_unique<MyObject>();
 * // ... do some stuff that might throw ...
 * QObject parentObject;
 * obj->setParent( &parentObject );
 * obj.release();










 

五、qDeleteAll與clear

https://blog.csdn.net/yao5hed/article/details/81092139

typedef struct _Defs
{
    int a;
    double d;
    QString s;
} Def;

QList<Def*> defs;
for(int i=0;i<500000;i++)
{
    Def* dd = new Def;
    dd->a = 12;
    dd->d = 4.23;
    dd->s = "jda";
    defs.append(dd);
}

qDebug()<<"before qDeleteAll: "<<defs.size();
qDeleteAll(defs);
qDebug()<<"after qDeleteAll: "<<defs.size();
defs.clear();

運行結果發現,不調用qDeleteAll的狀況下,程序佔內存78M;加上以後,只佔內存12M。可是先後的size沒有變化。

當T的類型爲指針時,調用clear方法能置空,但並不能釋放其內存。qDeleteAll能夠釋放容器元素內存,但沒有對容器的置空操做,也就是size沒變。因此qDeleteAll以後必須加上clear方法。

 

六、deleteLater

https://blog.csdn.net/yao5hed/article/details/81092168

void QObject::deleteLater()
{
    QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}

bool QObject::event(QEvent *e)
{
    switch (e->type()) {
    ......
    case QEvent::DeferredDelete:
        qDeleteInEventHandler(this);
        break;
    }
}

void qDeleteInEventHandler(QObject *o)
{
    delete o;
}

Qt中不建議手動delete掉QObject對象。緣由一:不注意父子關係會致使某個對象析構兩次,一次是手動析構,還有一次是parent析構,後者可能會出現delete堆上的對象。delete是C++和QT共有的一個操做符即時使用實時就析構刪除了,而Qt裏的deletelater的原理是:QObject::deleteLater()並無將對象當即銷燬,而是向主消息循環發送了一個event,下一次主消息循環收到這個event以後纔會銷燬對象。 這樣作的好處是能夠在這些延遲刪除的時間內完成一些操做,壞處就是內存釋放會不及時。

應用例子:父窗體的子窗體在focusoutevent時deletelater();而後在主窗體綁定信號子窗體的destroy()信號和父窗體的槽函數,而後在槽函數中象父窗體調用父窗體的接口函數向父窗體發送子窗體的文本text();
 

七、QObjectCleanupHandler

https://blog.csdn.net/yao5hed/article/details/81092178

https://blog.csdn.net/luoyayun361/article/details/97250027

m_pCleanupHandler = new QObjectCleanupHandler();
m_pObj1 = new CObject();
m_pObj2 = new CObject();
m_pObj3 = new CObject();
m_pCleanupHandler->add(m_pObj1);
m_pCleanupHandler->add(m_pObj2);
m_pCleanupHandler->add(m_pObj3);
...
//最後只須要調用
m_pCleanupHandler->clear();

全部的對象都會所有釋放。而且,若是其中有些對象已經在別的地方進行釋放, 那就會自動從QObjectCleanupHandler管理列表中自動刪除,不會重複刪除。因此,即使是重複調用clear()也不會出問題。使用QObjectCleanupHandler進行資源管理很是方便。

 

x、參考文獻

Qt淺談之一:內存泄露(總結)

https://blog.csdn.net/taiyang1987912/article/details/29271549
http://www.javashuo.com/article/p-wgttqhnd-ch.html

Qt父子對象內存管理實現簡析

https://www.dushibaiyu.com/2014/07/qt-fuzi-neicun.html
Qt智能指針官方文檔

https://doc.qt.io/qt-5/qsharedpointer.html
https://doc.qt.io/qt-5/qscopedpointer.html
https://doc.qt.io/qt-5/qobjectcleanuphandler.html  

相關文章
相關標籤/搜索