1

我的程序有时会在主函数的最后一条语句中给出分段错误。

return a.exec();

我认为问题在于破坏顺序。Qt 文档

没有 QObject 被删除两次,无论销毁顺序如何。

但是当我尝试以下代码时,它给出了分段错误。

QWidget* first = new QWidget;
QWidget* second = new QWidget(first);
delete first;
delete second;

我知道什么时候first被删除,它也删除了它的孩子second

那么为什么文档说破坏顺序无关紧要?

我们是否应该始终小心删除子对象?

我正在使用 QNetworkAccessManager 下载文件,当文件下载完成后,我想删除Downloader继承QObject以释放内存的对象。但是如果我删除它会导致分段错误。

4

2 回答 2

12

那么为什么文档说破坏顺序无关紧要?

这意味着您不需要手动删除对象。Qt 将在父/子层次结构中注册对象并在必要时将其终止。

当然,它无法跟踪您可能存储在某处的所有指针。因此,如果您获取指向对象的指针,然后删除对象的父级,然后尝试删除您获取的指针,那将完全是您的错。

我们是否应该始终小心删除子对象?

不。当您删除子对象时,它将从其父对象中注销自己。如果您删除second,然后删除first,程序将正常工作。所以你应该记住,一旦你杀死了一个对象,它的所有孩子都消失了。

顺便说一句,Qt 有QPointer专门针对这种情况的类,所以你应该使用它而不是原始指针。QPointer当它指向的对象被销毁时,会将自己设置为零。

但是如果我删除它会导致分段错误。

这意味着您的程序有一个尚未修复的错误。最有可能(98% 的可能性)该错误在您的代码中,而不是 Qt 的错。调试段错误并查看它发生的位置。


解释

QWidget* first = new QWidget;
QWidget* second = new QWidget(first);
delete first;
delete second;

当您删除first它时,它也会删除second,因为second它是它的孩子。但是,您的指针second不会自动更新。它将变成悬空指针(指向不再存在的对象),并尝试删除它,将触发未定义的行为,在您的情况下,这将使程序崩溃。

如果您坚持在(再次)之后保留单独的second 删除指针,您可以使用这样的东西(不要忘记):secondfirst#include <QPointer>

QPointer<QWidget> first = new QWidget,
    second = new QWidget(first);
delete first;
delete second;

然而,并没有真正的需要,通常人们只是这样做:

QWidget *first = new QWidget,
    *second = new QWidget(first);
delete first;//second is deleted automatically

通常,在 C++ 中,您必须手动分配(使用)delete您自己分配的每个对象。new这很容易出错,需要你编写析构函数,有时你可能会忘记它,这会导致内存泄漏。但是,QObject 会自动删除其析构函数中的所有子对象,这意味着这样做完全可以:

 QObject *a = new QObject();
 QObject *b = new QObject(a);
 b = new QObject(a);
 b = new QObject(a);

只要你记得删除顶层对象,它的所有子资源都会被自动释放。

但是,Qt 无法跟踪您存储的指针。因此,如果您执行以下操作:

QObject *a = new QObject(), *b = a;
delete a;

它不会更新现有的原始指针。顺便说一下,这是标准的 C++ 行为。

这就是 QPointers 的用途:

QObject *a = new QObject(), *b = a;
QPointer<QObject> c = a;
delete a; 
//at this point c is set to zero. a and b  still point at previous object locations.
于 2013-07-18T17:43:12.710 回答
2

Qt 无法控制您是否尝试访问delete已被销毁的对象。对象被销毁second时被删除。first

但是,文档确实包含了使用 删除 QObject 的情况QObject::deleteLater(),该情况是为这种情况设计的。我想不出你会使用这种方法而不是使用这种方法的delete情况QWidget。唯一不起作用的是当您在事件循环之外时,在这种情况下,您无论如何都不应该破坏/创建小部件。

于 2013-07-18T17:48:59.023 回答