【问题标题】:destructors in Qt4Qt4 中的析构函数
【发布时间】:2010-11-29 01:03:38
【问题描述】:

我对在 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() 应该是什么?

【问题讨论】:

    标签: c++ qt qt4 destructor


    【解决方案1】:

    Qt 使用他们所谓的object trees,它与典型的 RAII 方法有点不同。

    QObjectconstructor 接受一个指向父 QObject 的指针。当父 QObject 被破坏时,它的孩子也将被破坏。这是整个 Qt 类中非常普遍的模式,您会注意到很多构造函数都接受 *parent 参数。

    如果您查看一些 Qt example programs,您会发现它们实际上在堆上构造了大多数 Qt 对象并利用此对象树来处理破坏。我个人发现这个策略也很有用,因为 GUI 对象可以有特殊的生命周期。

    如果您不使用QObjectQObject 的子类(例如QWidget),Qt 不提供超出标准C++ 的额外保证。


    在您的特定示例中,不能保证任何内容都会被删除。

    对于Des,你会想要这样的东西(假设DesQWidget 的子类):

    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”的小部件时,这并不意味着它也会被销毁,是吗?我必须为自己销毁/删除它。我该怎么做?
    • 如果小部件没有干净的生命周期并且没有与之关联的父级,最好的选择是使用 cjhuitt 在另一个答案中提到的关闭时删除功能。跨度>
    • 只是为了澄清一下,如果我们将“按钮”作为成员变量,那么我们是否需要在小部件析构函数中手动删除按钮,否则它会在父级被删除时被删除。 .
    • 如果你有一个QPushButton * 作为成员变量并给它一个合适的父级(带有它的构造函数或带有setParent()),它会在父级被删除时被删除。您也可以使用普通的QPushButton(并让标准的 C++ 销毁规则生效),但您可能会发现,对于很多 Qt GUI 的东西,无论如何您都需要一个父级。
    【解决方案2】:

    使用deleteLater() 或父母的另一个选择是使用小部件的关闭时删除功能。在这种情况下,Qt 会在小部件显示完成后删除它。

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

    我喜欢将它与 Qt 保留的对象树一起使用,因此我为窗口设置了 delete-on-close,并且窗口中的所有小部件都指定了适当的父级,因此它们也都被删除了。

    【讨论】:

    • 这似乎工作得很好。谢谢。但是,当我创建 4 个新的“测试”小部件并再次关闭它们时,创建另一个“测试”小部件不会占用更多内存,但应用程序仍然使用与 4 个“测试”小部件时一样多的内存会存在。这正常吗?
    • @Berschi,Qt 或您的操作系统可能正在做一些内存优化。如果您在评论中提到的第五个小部件导致不再使用内存,我不会太担心。如果您担心,另一种选择是找到像 valgrind 这样的工具并通过它运行您的程序。
    【解决方案3】:

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

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

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

    【讨论】:

    • 此小部件没有 SIGNAL(closed())。 SIGNALS 是:customContextMenuRequested(QPoint)、destroyed() 和 destroy(QObject*)
    • 好吧,这只是一个一般概念,而不是一个具体的例子。也许您可以添加一个在对象完成工作时发出的信号?无论如何,它是一个有用的模式,它可以让你创建一些东西而忘记它,前提是你不存储对它的任何引用,并且它最终会完成。
    【解决方案4】:

    This tutorial 建议您不需要显式删除已添加到父小部件的小部件。它还说删除它们也没有什么坏处。

    (这个我没测试过,不过我猜只要你在父widget被删除之前显式删除它们就可以了。)

    【讨论】:

    • +1,但试图在 父窗口小部件被删除后删除它们最终会导致双重删除并且应用程序死亡。
    【解决方案5】:

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

        QPushButton push("neu");
    

    这样,它们会在超出范围时被删除。如果您真的想在堆上创建它们,那么您有责任在不再需要它们时对它们调用 delete。

    【讨论】:

    • 比这要复杂一些。 QT 在某些情况下会触发一些对象的销毁(如果它们是向父对象注册的),因此,将它们分配到堆栈会导致应用程序因双重释放而终止。
    • dribeas,我认为当 QObject 被破坏时,它会自动从父级注销。
    • 我一直使用 QObjects 的堆栈分配,它工作正常。感谢您不知情的downmod。
    • @rpg: 是的,关键是,当孩子存在时,不能删除父母,否则你必须确保父母不拥有所有权。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-09
    • 2021-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-11-04
    • 2016-12-19
    • 2013-03-17
    相关资源
    最近更新 更多