2012-05-01 22 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,而不管它何时发生。 –