那里。我從 qt 中看到了這個示例:https ://doc.qt.io/qt-5/qtwidgets-widgets-calculator-example.html ,并且想知道為什么 qt 總是使用 new 創建小部件而不是在堆疊上分配
Button *Calculator::createButton(const QString &text, const char *member)
{
Button *button = new Button(text);
connect(button, SIGNAL(clicked()), this, member);
return button;
}
在堆疊上創建小部件更快,在這個問題中:QT-specific difference of stack vs. heap attributes? Mike 的回答說 create on stack 完全沒問題,但是為什么官方檔案大多使用 new 呢?
uj5u.com熱心網友回復:
這很簡單:因為您希望小部件即使在此功能范圍完成并且QObjects 不可復制之后仍然存在。以下代碼不起作用,因為您無法復制該物件。
Button Calculator::createButton(const QString &text)
{
Button button(text); // allocated on stack, it is OK so far...
return button; // nope, you cannot copy that! it does not compile.
}
以下代碼也不起作用,因為您將傳遞一個已洗掉的物件。
Button *Calculator::createButton(const QString &text)
{
Button button(text); // allocated on stack, it is OK so far...
return &button; // ... returning pointer, still OK...
// Oh no! End of scope here, the object gets murdered now.
// whoever holds the pointer to it, holds a dead, invalid object.
}
該物件在作用域結束時被洗掉,您將傳遞一個指向已釋放的指標,因此記憶體無效。
你提到的例子
class Widget2 : public QWidget
{
Q_OBJECT
public:
Widget2(QWidget* parent = nullptr);
private:
QPushButton button;
};
并不一定意味著button在堆疊上創建。這取決于實體的Widget2創建方式。如果它在堆疊上,那么它也在button堆疊上。如果它new在堆上被 ed,那么也在button堆上。與宣告并保存為指標的“正常”情況的區別button在于,在上面的示例中,它們在一個分配中一起分配。是的,它的速度快得可以忽略不計,但這只是一個過早的優化。相信我,小部件的分配永遠不會成為您的瓶頸。順便提一句。Qt 中的大多數物件都是使用 PIMPL 習慣用法來實作的,以保持二進制兼容性,因此無論如何它們在內部都在堆上分配它們的私有物件。
是的,您可以保留您button的非指標成員身份。這是合法的。但這并不明智。這種方法存在一個問題:您必須#include在標頭中包含所有子小部件,并且您不能前向宣告它們。這肯定會讓你的編譯時間更長。但這不是主要問題,您可以稍等片刻。更大的問題是您正在引入構建依賴項,這使您的代碼的可組合性降低。想象一下,您將自定義小部件拆分為多個庫 - 然后一切都是您的#include在其他模塊可見的標題中也需要對這些模塊可見。這很快就會變得難以管理,因為您將無法隱藏/封裝實作細節。您的小部件使用某些按鈕這一事實當然是一個實作細節。盡可能在標題中保留盡可能少的細節。保持一個指標(可以前向宣告)肯定比保持一個非指標成員(必須是#included!)。此外,如果你保留一個指標,那么你可以使用替換原則,這個指標實際上可以指向任何子類實體。在小玩具應用程式或學校作業中,這無關緊要,但是當您設計一個非常大的應用程式時,將許多模塊和子模塊組織為庫,或者當您撰寫供他人使用的庫時,這些事情真的很重要。這使得可維護性和完全不可維護的代碼之間存在差異。
在我的實踐中,我只遇到了兩個實際在堆疊上分配小部件的用例。首先是在main()您可以撰寫的位置創建它們時:
int main() {
QApplication app;
MainWindow w; // it is really on the stack
w.show();
return app.exec();
// here is the main window automatically destroyed
}
另一種情況是,當您使用exec().
void showSomeMessage() {
QMessageBox box; // yes, we should set a parent here, for sake of simplicity I omitted it
// etc. set text and button here
box.exec(); // blocking here, we are waiting for user interaction
// end of scope - we are done here, we do not need the message box any more
// so it is perfectly fine it gets automatically deleted here
}
這些情況都很好。實際上,正如您exec()在這兩種情況下看到的阻塞一樣,它們非常相似。
結論:最好通過指標宣告成員QObjects 或s 并始終在您的標頭中前向宣告它們而不是它們的標頭。通過這種方式,您可以設計更多可組合、可維護和面向未來的代碼。您不會在小型玩具專案中看到任何顯著差異,但是當您的代碼庫變得更大時,您將從這種良好實踐中受益。更好的習慣是讓它們通過(這是一個特殊的 Qt 弱指標),它有兩個好處:1)它會自動初始化和 2)當物件被銷毀時,指標會自動設定為,因此您可以輕松檢查是否它還活著。Plus提供對原始指標的隱式轉換,因此它不會影響您撰寫代碼的方式。這是一個非常方便的課程。QWidget#includeQPointernullptrnullptrQPointer
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/429974.html
