2012-05-01 21 views
3

我知道Qt文檔中QLayout對象是否擁有其小部件的所有權。但就QLayout對象而言,可以安全地在堆棧上創建它,然後使用setLayout函數將它傳遞給小部件?還是必須在堆上創建?在堆棧上創建QLayout是否安全?

#include <iostream> 

#include <QtGui/QApplication> 
#include <QPushButton> 
#include <QVBoxLayout> 

class LoudPushButton : public QPushButton 
{ 
public: 
    virtual ~LoudPushButton(){std::cout << "~LoudPushButton()" << std::endl;} 
}; 

class LoudQVBoxLayout : public QVBoxLayout 
{ 
public: 
    virtual ~LoudQVBoxLayout(){std::cout << "~LoudQVBoxLayout()" << std::endl;} 
}; 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    QWidget window; 

    // On the heap 
    LoudQVBoxLayout* mainlayout = new LoudQVBoxLayout; 
    mainlayout->addWidget(new LoudPushButton); 
    mainlayout->addWidget(new LoudPushButton); 
    window.setLayout(mainlayout); 
    /* 
    // On the stack 
    LoudQVBoxLayout mainlayout; 
    mainlayout.addWidget(new LoudPushButton); 
    mainlayout.addWidget(new LoudPushButton); 
    window.setLayout(&mainlayout); 
    */ 
    window.show(); 

    return a.exec(); 
} 

兩種選擇//在堆棧和//在堆產生相同的結果在出口處:

~LoudQVBoxLayout() 
~LoudPushButton() 
~LoudPushButton() 

但我可以肯定,這是不是不確定的行爲? window是否在其佈局上調用delete

編輯:

鑑於貓加上另外的答案我想這:

LoudPushButton button; 
mainlayout->addWidget(&button); 
mainlayout->addWidget(new LoudPushButton); 

可生產即使button*mainlayout保證在同一時間被刪除未定義的行爲。這是真的?

+0

我認爲你應該重新考慮接受的答案。我已經編輯了我的帖子,詳細解釋了Cat Plus Plus的答案與Qt的文檔相矛盾。 – cgmb

回答

1

在Qt中,對象樹的設計使QWidgets可以在堆棧上構建。只要父母在孩子面前被創造出來,他們就會正常地被摧毀。你的例子都不是未定義的行爲。

Qt文檔甚至gives an example,並解釋爲什麼它是合法與家長建立在棧上的小部件:

int main() 
{ 
    QWidget window; 
    QPushButton quit("Quit", &window); 
    ... 
} 

此代碼是正確的:不幹析構函數不調用兩次,因爲C++語言標準(ISO/IEC 14882:2003)規定,本地對象的析構函數按其構造函數的相反順序調用。因此,首先調用子對象的析構函數quit,並在調用窗口的析構函數之前將其自身從其父窗口中移除。

佈局應該也是正確的,因爲它們被設計爲隨時銷燬。該QWidget::setLayoutdocumentation提到:

如果已經有安裝了這個小部件佈局管理器,QWidget的不會讓你安裝另一個。您必須先刪除現有佈局管理器(由layout()返回),然後才能使用新佈局調用setLayout()。

Qt佈局系統會跟蹤已在QWidgets上設置的QLayout對象的生命週期,並將正確處理銷燬,正如本文檔所暗示的。 QLayout的析構函數包含從其設置的QWidget中取消註冊的代碼。

+0

感謝這篇詳細的文章,但我認爲必須給你的解釋增加一點:雖然在這個具體的例子中,使用堆棧是安全的,但通常在使用Qt時非常危險,因爲如果出現令人討厭的問題孩子和父母的建設是倒立的。因爲這很容易發生在更大的功能中,我會建議使用堆來遠離這些微妙之處 – Martin

4

QObject刪除其子女。只有沒有父對象的對象纔能有自動存儲。並QWidget::setLayout reparents佈局。所以,不,你不能這樣做,QLayout

+0

謝謝!請你能檢查我的編輯,看看它是真的嗎? –

+1

該文檔說「QWidget將取得佈局的所有權」。您不能讓堆棧負責刪除,並且小部件負責刪除。即使你解開了這個細節並且在這個實現中找到了一個解決方法(對於代碼現在編寫的一些奇怪的深奧原因),你應該遵循合同... – HostileFork

+1

@MartinDrozdik:佈局補充你添加到它的小部件,所以同樣的規則適用。在指向自動存儲對象的指針上調用'delete'是UB,而不管它何時發生。 –