Qtdelete&deletelater設(shè)計


在C++中學(xué)習(xí)過程中,我們都知道:

  • delete 和 new 必須 配對使用(一 一對應(yīng)):delete少了,則內(nèi)存泄露,多了麻煩更大。

    成都創(chuàng)新互聯(lián)公司專注于封丘網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供封丘營銷型網(wǎng)站建設(shè),封丘網(wǎng)站制作、封丘網(wǎng)頁設(shè)計、封丘網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造封丘網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供封丘網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

Qt作為C++的庫,顯然是不會違背C++的前述原則的??墒?

  • 在Qt中,我們很多時候都瘋狂地用new,卻很少用delete,缺少的 delete 去哪兒了?!

注:本文暫不涉及智能指針(smart pointer)相關(guān)的東西,你可以考慮 Qt 智能指針學(xué)習(xí) 一文

Qt半自動的內(nèi)存管理

    在Qt中,以下情況下你new出的對象你可以不用 親自去delete (但你應(yīng)該清楚delete在何處被Qt調(diào)用的,怎么被調(diào)用的):

  • QObject及其派生類的對象,如果其parent非0,那么其parent析構(gòu)時會析構(gòu)該對象(本文內(nèi)容圍繞這一點展開 )

除此之外,有些類的對象可以接收設(shè)置一些特別的標(biāo)記,比如:

  • QWidget及其派生類的對象,可以設(shè)置 Qt::WA_DeleteOnClose 標(biāo)志位(當(dāng)close時會析構(gòu)該對象)

  • QAbstractAnimation派生類的對象,可以設(shè)置 QAbstractAnimation::DeleteWhenStopped

  • QRunnable::setAutoDelete()

  • MediaSource::setAutoDelete()

  • ...

注意:這些用法會有些陷阱 ,請注意看本文最后的3個小例子。

Qt中,最基礎(chǔ)和核心的類是:QObject 。它的魔力很大,本文只關(guān)注兩點:

  • 父子關(guān)系

  • deleteLater

父子關(guān)系

Qt中,每個 QObject 內(nèi)部都有一個list,用來保存所有的 children,還有一個指針,保存自己的parent。當(dāng)它自己析構(gòu)時,它會將自己從parent的列表中刪除,并且析構(gòu)掉所有的children。

  • 注意:在 Qt 中,我們經(jīng)常會遇到

    • 基類、派生類,或父類、子類。 這是對于派生體系來說的,和在C++相關(guān)書中看到的完全一樣,與這的parent無關(guān)

    • 父對象、子對象、父子關(guān)系。 這是Qt中所特有的,也就是這兒的parent所引入的,與類的繼承關(guān)系無關(guān)

建立與解除

Q_INVOKABLE QObject::QObject ( QObject * parent = 0 )
  • 創(chuàng)建一個QObject對象時,如果指定了父對象,它就會將自己添加到父對象的 children 列表中

QObject::~QObject () [virtual]
  • 當(dāng)一個QObject對象析構(gòu)時,它會將自己從父對象的 children 列表中移除(parent非0的話)

void QObject::setParent ( QObject * parent )
  • 通過該函數(shù),將自己從原父對象的children中刪除,添加到新parent的children列表中

注:這三個函數(shù)都是通過一個內(nèi)部私有函數(shù)來實現(xiàn)的,這就是

QObjectPrivate::setParent_helper(QObject *o)

獲取父、子對象

每個QObject只有一個父對象:

QObject * QObject::parent () const

子對象可以有多個

const QObjectList & QObject::children () const

所以可以根據(jù)條件來查找嘍:

T QObject::findChild ( const QString & name = QString() ) const
QList<T> QObject::findChildren ( const QString & name = QString() ) const

deleteLater

deleteLater 包含兩層意思了

  • delete

  • later

呵呵,似乎這是廢話哈。

刪除自己

在去年春節(jié)前的時候吧,有人對 

obj-> deleteLater()

會像下面一樣調(diào)用delete:

delete obj;

感到不解。然后我寫了這樣一個C++例子:

class A
{
  public:
  A(){}
  void deleteMe()
  {
      delete this;
  }
};

int main()
{
  A * a = new A;
  a->deleteMe();
  return 0;
}

應(yīng)該不需要解釋吧

later

Qt 是事件驅(qū)動的,所以發(fā)送一個刪除事件到事件系統(tǒng)就可以啦:

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

事件循環(huán)稍后看到該事件就會將其派發(fā)會這個widget:

bool QObject::event(QEvent *e)
{
   switch (e->type()) {
...
   case QEvent::DeferredDelete:
        ...

一些例子

無關(guān)痛癢?

很簡短、很熟悉的一個例子是不?但是 如果你發(fā)現(xiàn)對象的析構(gòu)函數(shù)始終不被成功調(diào)用 ,會有什么感覺?

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel("Hello Qt!");
label->show();
return app.exec();
}

這是 C++ GUI Programming with Qt 4 一書的第一個例子。我們注意到這兒的 label 既沒有指定parent,也沒有對其調(diào)用delete。

所以,這兒會造成內(nèi)存泄露。

書中解釋說,對于這種小例子,這點內(nèi)存泄露不算什么。不清楚官方這個例子的意圖是什么,或許是一開始就讓大家用指針吧。

三種改進(jìn)方式

  • 分配對象到stack而不是heap中

    QLabel label("Hello Qt!");

label.show();
  • 設(shè)置標(biāo)志位,這樣,當(dāng)我們點擊關(guān)閉按鈕時,close()函數(shù)將會調(diào)用deleteLater 

label->setAttribute(Qt::WA_DeleteOnClose);
  • 動手調(diào)用delete(不就是少了一個么,我們補上還不行么)

    int ret = app.exec();
    delete label;
    return ret;

單獨列一個吧

強化一下對前一個例子的了解

#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel label("Hello Qt!");
label.show();
label.setAttribute(Qt::WA_DeleteOnClose);
return app.exec();
}

運行正常,退出時會崩潰 ,因為label被close時,將會 delete 這兒label對象,但label對象卻不是通過new分配到heap中的。

為了使得用戶減少自己顯式使用delete,Qt將delete隱藏的比較深。這樣一來,不使用new為對象分配空間時,反倒需要多多小心了。

隱蔽很深?

看個小例子:這個程序退出時會直接崩潰 。

#include <QtGui>
int main(int argc, char* argv[])
{
   QApplication app(argc, argv);
   QLabel label(tr"Hello Qt!");
   QWidget w;
   label.setParent(&w);
   w.show();
   return app.exec();
}
  • 問題出在哪兒呢?因為退出時,w 比 label 先被析構(gòu),當(dāng) w 被析構(gòu)時,會刪除chilren列表中的對象,也就是這兒的 label。但 label 卻不是通過new分配在heap中,而是在stack中,可想而知,delete 一個再stack中的對象會怎么樣了。相當(dāng)于

QLabel label();
delete &label;
  • 兩種改進(jìn)辦法:

    • 一是,將label分配到heap中

   QLabel *label = new QLabel("Hello Qt!");
  label.setParent(&w)
  • 再一種就是,確保label先于其parent被析構(gòu)(調(diào)整一下順序),這樣,label析構(gòu)時將自己從父對象的列表中移除自己,w析構(gòu)時,children列表中就不會有分配在stack中的對象了。

   QWidget w;
  QLabel label(tr"Hello Qt!");

Qt 對象的父子關(guān)系的引入,簡化了我們對內(nèi)存的管理,但是,由于它會在你不太注意的地方調(diào)用 delete,所以,使用時還是要當(dāng)心。

  • http://doc.qt.nokia.com/4.7/qobject.html

  • http://www.cuteqt.com/blog/?p=824

文章名稱:Qtdelete&deletelater設(shè)計
網(wǎng)站網(wǎng)址:http://bm7419.com/article18/jdcedp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計、軟件開發(fā)ChatGPT、定制開發(fā)網(wǎng)站導(dǎo)航、品牌網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司