Qt4中的析构函数

11

我很困惑如何在Qt4中使用析构函数,希望大家能帮助我。
当我有一个像这样的方法(其中“Des”是一个类):

void Widget::create() {
    Des *test = new Des;
    test->show();
}
如何确保小部件在关闭后被删除?
在"Des"类中,我有以下代码:
Des::Des()
{
    QPushButton *push = new QPushButton("neu");
    QHBoxLayout *layout = new QHBoxLayout;
    layout->addWidget(push);
    setLayout(layout);
}

我需要在哪里和如何删除*push和*layout?析构函数Des::~Des()应该包含什么?

5个回答

21

Qt使用他们所称的对象树,这与典型的RAII方法有些不同。

QObject构造函数接受一个指向父QObject的指针。当该父QObject被销毁时,它的子对象也将被销毁。这是Qt类中相当普遍的模式,你会注意到很多构造函数都接受一个*parent参数。

如果你查看一些Qt 示例程序,你会发现它们实际上在堆上构造大部分Qt对象,并利用这个对象树来处理销毁。我个人也发现这种策略很有用,因为GUI对象可以有奇怪的生命周期。

如果你没有使用QObjectQObject的子类(例如QWidget),Qt除了标准C++之外不提供任何额外的保证。


在您的特定示例中,没有任何保证会删除任何内容。
对于Des(假设Des是QWidget的子类),您需要像这样:
class Des : public QWidget
{
    Q_OBJECT

public:
    Des(QWidget* parent)
    : QWidget(parent)
    {
        QPushButton* push = new QPushButton("neu");
        QHBoxLayout* layout = new QHBoxLayout(this);
        layout->addWidget(push); // this re-parents push so layout 
                                 // is the parent of push
        setLayout(layout);
    }

    ~Des()
    {
        // empty, since when Des is destroyed, all its children (in Qt terms)
        // will be destroyed as well
    }
}

您可以这样使用类Des

int someFunction()
{
    // on the heap
    Des* test = new Des(parent); // where parent is a QWidget*
    test->show();
    ...
    // test will be destroyed when its parent is destroyed

    // or on the stack
    Des foo(0);
    foo.show();
    ...
    // foo will fall out of scope and get deleted
}

好的,这很有帮助,但我还有一个额外的问题: 当我只是关闭名为“test”的小部件时,这并不意味着它也会被销毁,对吗?我必须自己销毁/删除它。我该怎么做? - Berschi
2
如果小部件没有干净的生命周期,也没有父级可以关联它,最好的选择是使用cjhuitt在另一个答案中提到的关闭时删除功能。 - richardwb
仅供澄清,如果我们将“Pushbutton”设置为成员变量,那么在小部件析构函数中是否需要手动删除push-button,或者当父对象被删除时它会自动被删除。 - Naruto
如果您有一个 QPushButton * 作为成员变量,并给它一个合适的父对象(可以在其构造函数中或使用 setParent() 函数设置),那么当父对象被删除时,该按钮也会被删除。您也可以使用普通的 QPushButton(并让标准的 C++ 销毁规则生效),但是对于许多 Qt GUI 组件,您可能仍然需要一个父对象。 - richardwb

12

除了使用 deleteLater() 或者父元素,另一种选择是对小部件使用关闭时删除的功能。在这种情况下,Qt 将在小部件显示完毕后删除它。

Des *test = new Des;
test->setAttribute( Qt::WA_DeleteOnClose );
test->show();

我喜欢使用Qt保留的对象树,因此我设置了窗口的delete-on-close属性,并为窗口中的所有小部件指定了适当的父对象,这样它们也会被删除。


这似乎非常有效。谢谢。 但是,例如当我创建4个新的“test”小部件并再次关闭它们时,创建另一个“test”小部件不会消耗更多的内存,但应用程序仍然使用与4个“test”小部件仍然存在时一样多的内存。这正常吗? - Berschi
1
@Berschi,可能是Qt或您的操作系统正在进行一些内存优化。如果您在评论中提到的第五个小部件不会使用更多内存,那么我就不会太担心它了。另一个选择(如果您担心)是找到像valgrind这样的工具,并通过它运行您的程序。 - Caleb Huitt - cjhuitt

5

Richardwb的回答很好-但另一种方法是使用deleteLater槽,如下所示:

Des *test = new Des;
test->show();
connect(test, SIGNAL(closed()), test, SLOT(deleteLater()));

显然,closed() 信号可以替换成您想要的任何信号。

这个小部件没有SIGNAL(closed())。可以使用的SIGNALS有:customContextMenuRequested(QPoint), destroyed()和destroyed(QObject*)。 - Berschi
好的,这只是一个通用概念,而不是具体的例子。也许你可以添加一个信号,当你的对象完成工作时发出?这是一个有用的模式,它让你创建一些东西并忘记它,前提是你不存储任何对它的引用,并且它最终会完成。 - Thomi

3
这篇教程建议您不需要显式删除已添加到父控件的小部件。它还说即使删除它们也不会有影响。

(我没有测试过,但我猜只要在父控件被删除之前显式删除它们,这应该是可以的。)


2
+1,但尝试在父窗口小部件被删除后删除它们会导致双重删除和应用程序崩溃。 - David Rodríguez - dribeas

2

在大多数情况下,您应该在堆栈上创建小部件:

    QPushButton push("neu");

这样做可以使它们在超出范围时被删除。如果您确实想在堆上创建它们,那么在不再需要它们时调用删除是您的责任。

1
这比那要复杂一些。QT会在某些情况下触发一些对象的销毁(如果它们已经注册了一个父对象),因此,将它们堆栈分配可能会导致应用程序出现双重释放而崩溃。 - David Rodríguez - dribeas
3
我认为,当一个QObject被析构时,它会自动从父对象中注销。 - rpg
1
我一直使用QObjects的堆栈分配,它运行良好。感谢您未经事实核实就进行负面评价。 - static_rtti
1
@rpg:是的,关键是当子对象存在时,父对象不能被删除,或者你必须确保父对象不持有所有权。 - jpalecek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接