2017-05-11 84 views
1

該代碼不會動態分配內存,不會在窗口上顯示任何標籤。爲什麼我必須動態分配內存給QLabel才能正常工作?

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel l; 
    l.setText ("cdsadsaf"); 
    l.setParent (this); 
} 

動態分配內存後,標籤顯示出來。

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel *label = new QLabel(this); 
    label->setText("first line\nsecond line"); 
} 

爲什麼QLabel需要動態內存分配?

回答

2

該代碼不會動態分配內存,不會在窗口上顯示任何標籤。

這是因爲一旦從構造函數返回,標籤就會超出範圍。標籤的使用期限在下面註明。 label a QLabel本身。

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel label;    // label is born 
    label.setText ("cdsadsaf"); // label is alive 
    label.setParent (this);  // label is alive 
}        // label dies 

動態分配內存後,標籤顯示出來。

這是因爲標籤沒有超出範圍。指向它的指針的確如此,但這並不重要。請注意,label僅僅是一個指針,並且該對象獨立於指向它的指針存在。

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel *label = new QLabel(this);   // label is born, QLabel is born 
    label->setText("first line\nsecond line"); // label is alive, QLabel is alive 
}            // label dies, QLabel is alive 

爲什麼是必要的動態內存分配QLabel工作?

不是。由於使用動態分配,您碰巧給QLabel留下了活力的機會,但這只是一個巧合。

您可以將標籤作爲父對象本身的一部分 - 它不需要單獨分配。編譯器會爲你管理內存。

#include <QtWidgets> 

class MainWindow : public QMainWindow { 
    QWidget m_central; 
    QGridLayout m_layout{&m_central}; 
    QLabel m_label{"Hello, World"}; 
public: 
    MainWindow(QWidget * parent = {}) : QMainWindow{parent} { 
    m_layout.addWidget(&m_label, 0, 0); 
    setCentralWidget(&m_central); 
    } 
}; 

int main(int argc, char ** argv) { 
    QApplication app{argc, argv}; 
    MainWindow w; 
    w.show(); 
    return app.exec(); 
} 
1

在堆棧上創建QLabel時,函數返回時將被刪除。當父窗口小部件更新其顯示時,QLabel不再存在。

在堆上創建它可以超越對函數的調用。

+1

爲什麼這會降低投票率? –

+1

因爲「堆棧」這個詞在C++標準中並不存在,所以不需要描述會發生什麼。每個人似乎都忘記的概念是**範圍**。該對象超出範圍並不復存在。它**無關緊要** C++編譯器如何實現超出範圍。編譯器可以兼容並生成將所有自動變量放在堆上的代碼。堆棧不需要解釋發生了什麼 - 這是一個實現細節。 –

+0

@KubaOber,我很欣賞你的觀點。感謝您加入澄清。 FWIW,C++ 11標準在很多地方提到了堆棧。 –

5

這不是必需的。這裏有典型的範圍問題。

第一個案例在堆棧上創建QLabel,當您退出構造函數時它「死亡」。

在第二個它繼續生活1),因爲它是動態分配和2)你實際上分配一個家長 - 它是你的主窗口。如果你不這樣做2),效果是一樣的,與第一種情況更糟糕 - 我們將創建一個內存泄漏:

內存泄漏

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel *label = new QLabel(); # no parent 
    label->setText("first line\nsecond line"); 
} 

由於沒有內存泄漏到父被分配到標籤

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    QLabel *label = new QLabel(this); # main window is the parent and will take care of destroying the label when its own destructor is called 
    label->setText("first line\nsecond line"); 
} 

可避免在堆中分配的QLabel,但仍然能夠通過僅僅將它移動到更廣的範圍來使用它。由於您的標籤將顯示在主窗口中,因此您可以創建一個班級成員標籤。不需要動態分配,因爲只要窗口實例處於活動狀態,它就會保持活動狀態。

class MainWindow : public QMainWindow 
{ 
... 
private: 
    QLabel l; 
} 

MainWindow::MainWindow(QWidget *parent) 
    : QMainWindow(parent) 
{ 
    this->l.setText ("cdsadsaf"); 
} 

正如在評論中提及了(再次感謝!)setParent(...),除非你想比內存管理別的東西的父子關係,在這裏不需要。見下面的評論。

+0

如果你在類成員的小部件上調用'setParent',它不會被刪除兩次嗎?一旦被指定的父母和曾經作爲普通物體破壞的手段?我認爲這值得澄清。 –

+0

你說得對。當我複製粘貼代碼時,我忘記刪除該行。它實際上會打破所有地獄,因爲調用delete,並且在堆棧上分配的東西做這件事是不好的舉動。感謝您指出了這一點! – rbaleksandar

+1

其實,它很好。子對象在刪除時會與父對象分離。當MainWindow被刪除時,首先調用成員變量的析構函數,所以'l'在被破壞之前從'MainWindow'解除鏈接,所以當調用'MainWindow'析構函數時,'l'不再是它的子節點。但是,值得指出的是,特別是因爲有一些挑剔。 [Qt文檔:對象樹](http://doc.qt.io/qt-5/objecttrees.html) –

相關問題