2013-12-13 47 views
2

所以,我認爲我已經在網上進行了相當徹底的搜索,沒有發現真正有用的東西(只是混淆了最多......)。有沒有什麼方法可以在不使用動態內存的情況下使用Qt?

我想知道我如何可能(如果可能)使用Qt與非動態內存。我面對的問題是,對於很多小部件,我確切知道我想要使用的是什麼(這些子小部件,這些佈局,固定數字等)。然而,當你不使用動態內存時,Qt中的所有東西似乎都會阻礙你。一個簡單的例子是QLayout,其中from the Qt documentation被設計爲擁有它添加的任何東西的所有權。所以基本上,下面的代碼:

//In header 
class ThumbnailDialog : public QDialog 
{ 
    Q_OBJECT 
public: 
    ThumbnailDialog(QWidget* parent = 0); 
    ~ThumbnailDialog(void); 
private: 
    QPushButton m_confirm; 
    QPushButton m_cancel; 

    QHBoxLayout m_buttonsLayout; 
}; 

//Implementation of ctor 
ThumbnailDialog::ThumbnailDialog(QWidget* parent): 
    QDialog(parent) 
{ 
    //... 
    m_buttonsLayout.addWidget(&m_confirm); 
    m_buttonsLayout.addWidget(&m_cancel); 
    //... 
    setLayout(&m_dialogLayout); 
} 

...將結束(在MSVC)在調試斷言失敗_BLOCK_TYPE_IS_VALID(pHead->nBlockUse),因爲在ThumbnailDialog的析構函數,佈局嘗試刪除按鈕...這顯然不應該。

那麼,我是否被迫在各處都使用動態內存,as this "Qt Expert"擁護者(雖然提到「堆」,雖然......)?這似乎是錯誤的,因爲這會阻止利用RAII(如果父子關係意味着會有刪除,那麼我不能用智能指針來做我想的)。使用動態內存來編譯時已知的事情也會感覺非常錯誤......(但我可能是錯的,那只是我的感覺)。

因此:有沒有任何方式使用Qt,而不訴諸動態內存和new s爲每個小工具/佈局?

+0

我認爲Qt是以這種方式設計的。但是,如果它保證在父母被銷燬時刪除子對象 - 這也是可接受的方法。 – vahancho

+0

@vahancho不,它不是,至少對我而言。讓「新」完全在野外使得代碼容易受到可能在ctors中發生的異常的影響。另外,「新」事物可能只是簡單的成員,但效率低下(雖然在我的情況下我可以克服它,因爲我沒有那麼多項目),但在概念上也是錯誤的。 – JBL

+0

@JBL,我認爲你是在反思這個。如果您正在使用QT(或任何其他UI庫),請說再見,以確保這些小事情的效率。它們建立在層層傳遞價值層上而不做任何事情,一對夫婦'新'不會改變任何東西。這在概念上也不是錯誤的。如果您可以即時分配大量對象,則無需將其設置爲靜態,並強制將其寫入可執行文件並存儲在磁盤上。更不用說後面你會後悔沒有能夠有一個更動態的用戶界面。 – Shahbaz

回答

1

不,不是真的。恐怕。 Qt依靠這個東西。

+0

所以基本上,爲了避免雙「刪除」和非動態內存「刪除」,我必須在野外動態分配所有東西?的Bleh。謝謝! – JBL

+0

如果你手動刪除所有的孩子(例如'QLayout :: removeWidget'),應該沒有更多的雙'刪除',我猜... –

2

你看到的問題是你沒有考慮發生了什麼。該類中的QPushButton實例是由該類創建和擁有的。當該類被刪除時,它將調用QPushButtons上的析構函數。

Qt提供了有用的對象父對象,並刪除父對象將處理刪除其所有子對象。因此,就你的例子而言,你有兩件事要調用QPushButtons上的析構函數:1)刪除ThumbnailDialog的類對象,2)刪除buttonlayout,它會嘗試刪除它的子項並將失敗,因爲對象在堆棧中。

如果你真的想要做什麼,可以通過在按鈕佈局上調用removeWidget()來確保QPushButton項從按鈕佈局中移除,在ThumbnailDialog的析構函數中。

但是,這是混亂。動態分配是一種更好的方法,因爲它在堆上分配對象而不是堆棧。

另請注意,使用Qt的父母教育,這意味着您可以創建很多小部件,而無需跟蹤它們。例如,你可以這樣做: -

ThumbnailDialog::ThumbnailDialog(QWidget* parent): 
    QDialog(parent) 
{ 
    //... 
    m_buttonsLayout.addWidget(new QPushButton("Confirm")); 
    m_buttonsLayout.addWidget(new QPushButton("Cancel")); 
    //... 
    setLayout(&m_dialogLayout); 
} 

在這種情況下,該按鈕甚至不需要在標題中被定義的,您可以放心,他們會與父母一起被刪除。

+0

我知道它消除了跟蹤它們的需要,儘管這使得它很痛苦地使用這些項目上的信號和插槽(我想你必須通過'itemAt()'或類似的東西檢索它們。 – JBL

+0

對於需要連接的項目,使用指向它們的項目創建項目,添加它們到佈局,然後使用指針來設置信號和插槽,或者只是將類中的智能(弱)指針保存到那些稍後需要訪問的指針上。 – TheDarkKnight

3

我想你在這裏誤解了這個問題。您沒有刪除一倍的東西,要刪除在棧上分配的對象:

int foo = 12345; 
int* pFoo = &foo; 
delete pFoo; 

這是當你傳遞一個指針到一個基於堆棧的對象QHBoxLayout,負責會發生什麼,所以你堆損壞的調試斷言。

Qt以這種方式管理QObjects,因爲大多數GUI具有很多小部件,這使得管理GUI對象的生命週期變得更加容易,它還允許在線程中排隊刪除等。而且在內部大多數類都使用PIMPL,因此您不會避免堆/動態即使你的代碼工作,也要分配。

說了這麼多,如果你願意,你可以在沒有堆分配的情況下工作,但它比它的價值更加努力。在你的例子中,你必須從析構函數中的佈局中移除小部件,但要確保nullptr在ctor拋出的情況下檢查事物,如果它們在析構函數被擊中時不在佈局中,那麼它將不會能夠刪除它們。

還有一件事要考慮..如果Qt被設計成以這種方式工作,那麼你可能很容易就會在某些平臺上溢出堆棧的情況下結束。例如Qt適用於Symbian/S60,它的堆棧非常有限,在那裏運行你的代碼很容易導致一個堆棧溢出:)。

+0

感謝您的回答,儘管我其實並不關心堆棧/堆棧,僅僅是存儲時間,而且我知道在這種情況下它不是雙重刪除(「佈局試圖刪除按鈕......顯然它不應該這樣做」)。 – JBL

+0

堆棧/堆*是*存儲時間 – paulm

1

如果您已經閱讀其他的答案,但您仍想使用靜態分配,可以釋放做addWidget()時所採取的所有權:

 //... 
    m_buttonsLayout.addWidget(&m_confirm); 
    m_buttonsLayout.addWidget(&m_cancel); 

    // release the ownership by setting no parent 
    m_confirm.setParent(0); 
    m_cancel.setParent(0); 

    //... 
1

你可以,你就必須要小心關於對象層次結構的破壞順序。
在你的情況下,你必須將佈局移動到頂部。

private: 
    QHBoxLayout m_buttonsLayout; 

    QPushButton m_confirm; 
    QPushButton m_cancel; 

銷燬從底部恰好頂部和所有的佈局兒童的dtors註銷自己從佈局兒童的,所以當佈局破壞發生,已經沒有註冊的孩子的。

qt docs

這似乎一見鍾情繁瑣還解釋,但在實踐中的自定義窗口小部件中的層次是非常平坦。層次結構的一層中的小部件的順序(在本例中爲QPushButton)並不重要,這通常意味着您只需將佈局正確安排在頂層。

相關問題