有没有办法使用Qt而不使用动态内存?

时间:2013-12-13 14:38:49

标签: c++ qt dynamic-memory-allocation

所以,我认为我已经对网络进行了相当彻底的搜索,发现没有什么真正有用的(最多只是令人困惑......)。

我想知道如何(如果可能的话)将Qt与非动态内存一起使用。我面临的问题是,对于许多小部件,我确切地知道我想要使用什么(这些子小部件,这些布局,固定数字等)。然而,当你不使用动态内存时,Qt中的所有内容似乎都会受到阻碍。一个简单的示例是QLayoutfrom 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);
}

...将在_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)的调试断言失败时(在MSVC上),因为在ThumbnailDialog的dtor中,布局试图删除按钮......显然不应该。

所以,我被迫在各处使用动态记忆,as this "Qt Expert"提倡者(虽然提到“堆”,但是......)?这似乎是错误的,因为这会阻止利用RAII(如果父子关系意味着会有删除,那么我不应该使用智能指针来做那个)。对于编译时已知的事情,使用动态内存也感觉非常错误...(但我可能错了,这只是我的感觉)。

那么:有没有办法使用Qt而不依赖于动态内存和每个小部件/布局的new

5 个答案:

答案 0 :(得分:3)

我认为你在这里误解了这个问题。你没有删除一些东西,你正在删除在堆栈上分配的对象:

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

当您将指向基于堆栈的对象的指针传递给QHBoxLayout时会发生这种情况,因此您的堆损坏调试断言。

Qt以这种方式管理QObjects,因为大多数GUI都有很多小部件,这使得管理GUI对象的生命周期更容易,它还允许跨线程等排队删除。此外,大多数类内部都使用PIMPL,因此您无法避免堆/动态分配即使您的代码有效。

说完这一切之后,如果你愿意,你可以在没有堆分配的情况下让它工作,但它比它的价值要多得多。在你的例子中,你必须从析构函数中的布局中删除小部件,但是要确保nullptr检查ctor抛出的情况,如果它们在析构函数被击中时不在布局中那么它将不会能够删除它们。

还有一件事需要考虑......如果Qt的设计是以这种方式工作,那么你很容易就会遇到在某些平台上溢出堆栈的情况。例如,Qt适用于Symbian / S60,它具有极其有限的堆栈,在那里运行代码很容易导致堆栈溢出:)。

答案 1 :(得分:2)

你看到的问题是你没有考虑到发生了什么。类中的QPushButton实例由类创建并拥有。删除类时,它将调用QPushButtons上的析构函数。

Qt提供有用的对象父级,删除父级将处理所有子对象的删除。

因此,在您的示例中,您有两件事要调用QPushButtons上的析构函数:1)删除ThumbnailDialog的类对象和2)删除buttonlayout,它将尝试删除它的子节点并且会失败,因为对象在栈中。

如果你真的想要,你可以做的是确保通过调用按钮布局上的removeWidget(),在ThumbnailDialog的析构函数中从按钮布局中删除QPushButton项。

但是,这很麻烦。动态分配是一种更好的方法,因为它在堆上而不是堆栈上分配对象。

另请注意,使用Qt的父母,这意味着您可以创建大量小部件而无需跟踪它们。例如,您可以这样做: -

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

在这种情况下,甚至不需要在标题中定义按钮,您可以确保它们将与父母一起删除。

答案 2 :(得分:1)

不,不是真的。我耽心。 Qt依赖于这些东西。

答案 3 :(得分: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);

     //...

答案 4 :(得分:1)

是的,有。(并且以相当干净的方式,我相信)

您只需要了解QObject层次结构的销毁顺序:

  • 正如Qt docs中所解释的那样,许多物品声称他们的孩子拥有所有权,因此他们希望确保他们在死亡时得到清理。
  • QObject的dtor解除了其所有权的父母。
  • 非静态数据成员的破坏从下到上发生。

这意味着,在您的情况下,您只需将布局移至顶部:

private:
    QHBoxLayout m_buttonsLayout;

    QPushButton m_confirm;
    QPushButton m_cancel;

所有布局的子节点都从布局中取消注册,因此当布局破坏发生时,没有注册的子节点。 以前布局的破坏是delete d那些首先不是new d的孩子。

初看起来似乎很乏味,但实际上,自定义小部件中的层次结构非常平坦。层次结构的一个层中的窗口小部件的顺序 - 在这种情况下是QPushButton s - 并不重要,这通常意味着您只需要在顶部正确排列布局。