Qt - 堆栈上有父级的 QObject 如何被删除两次?
Qt - How a QObject with a parent on the stack might get deleted twice?
有人可以解释一下吗?
Stack or Heap?
In general, a QObject without a parent should be created on the stack or defined as an subobject of another class. A QObject with a parent should not be on the stack because then it might get deleted twice accidentally. All QObjects created on the heap should either have a parent, or be managed somehow by another object.
来源:LINK
抱歉,对已接受的答案投反对票。目前解释的两个版本都是错误的,尤其是结论是非常错误的。
Parent/child 用于内存管理
除其他外,Qt 使用 parent/child 概念来管理内存。当一个 object 被 parented 到另一个时,那么
- 删除 parent 也会删除(通过
operator delete
)它的所有 children。 当然,这是递归的;
- 删除 child 会 un-parent 它,这样 parent 就不会尝试双重删除。
deleteLater
这不是工作所必需的——任何删除都会un-parent它。
这允许您通过 operator new
重复动态分配来构建 QObject
的树,而不必手动删除所有分配的 object 的问题。只要给他们 parents,你就只需要删除树的根。您还可以随时删除 child(即子树),这将做正确的事™。
最后,你将无漏无双删。
这就是为什么在构造函数中您会看到类似以下内容的原因:
class MyWidget : public QWidget // a QObject subclass
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);
// default destructor is fine!
private:
// raw pointers:
// we won't own these objects through these pointers.
// we just need them to access the pointees
QTimer *m_timer;
QPushButton *m_button;
};
void MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// don't need to save the pointer to this child. because reasons
auto lineEdit = new QLineEdit(this);
auto validator = new QIntValidator(lineEdit); // a nephew
// but let's save the pointers to these children
m_timer = new QTimer(this);
m_button = new QPushButton(this);
// ...
}
默认析构函数将正确删除整棵树,尽管我们通过调用 operator new
分配了 children object,我们甚至懒得保存成员指向一些 children.
堆栈中的 QObjects
您可以(在某些情况下,这实际上是个好主意)将 parent 分配给分配在堆栈上的 object。
典型的例子是 QDialog
个子类:
void MyWidget::showOptionsDialog()
{
// OptionsDialog is a QDialog subclass;
// create an instance as a child of "this" object
OptionsDialog d(this);
// exec the dialog (i.e. show it as a modal dialog)
conts auto result = d.exec();
if (result == QDialog::Accept) {
// apply the options
}
// d gets destroyed here
// => it will remove itself as a child of this
}
将this
作为对话框的parent传递的目的是让对话框以parent的小部件为中心,共享任务托盘条目,并对其进行模态处理。 这在 QDialog
文档中进行了解释。此外,最终 d
只需要存在于该函数中,因此将其声明为自动变量(即分配在堆栈上)是个好主意。
给你:你有一个 QObject
,分配在堆栈上,parent。
那么堆栈上 QObject
的危险是什么?考虑这段代码:
QObject *parent = new QObject;
QObject child(parent);
delete parent;
如前所述,parent
将尝试在 child
上调用 operator delete
,而 object 是 而不是 使用 new
分配(它在堆栈上)。这是非法的(并且可能会崩溃)。
显然,没有人会这样写代码,但请再考虑一下上面的对话框示例。如果在调用 d.exec()
期间,我们设法以某种方式删除了 this
,即对话框的 parent,会怎样?发生这种情况的原因有很多,非常非常难以追踪——例如,数据到达套接字会导致 UI 中的小部件发生变化,创建一些小部件并破坏其他小部件。最终,你会删除一个堆栈变量,崩溃(并且很难重现崩溃)。
因此建议避免 首先创建这样的代码。这不违法,它可能有效,但也可能无效,没有人喜欢脆弱的代码。
有人可以解释一下吗?
Stack or Heap? In general, a QObject without a parent should be created on the stack or defined as an subobject of another class. A QObject with a parent should not be on the stack because then it might get deleted twice accidentally. All QObjects created on the heap should either have a parent, or be managed somehow by another object.
来源:LINK
抱歉,对已接受的答案投反对票。目前解释的两个版本都是错误的,尤其是结论是非常错误的。
Parent/child 用于内存管理
除其他外,Qt 使用 parent/child 概念来管理内存。当一个 object 被 parented 到另一个时,那么
- 删除 parent 也会删除(通过
operator delete
)它的所有 children。 当然,这是递归的; - 删除 child 会 un-parent 它,这样 parent 就不会尝试双重删除。
deleteLater
这不是工作所必需的——任何删除都会un-parent它。
这允许您通过 operator new
重复动态分配来构建 QObject
的树,而不必手动删除所有分配的 object 的问题。只要给他们 parents,你就只需要删除树的根。您还可以随时删除 child(即子树),这将做正确的事™。
最后,你将无漏无双删。
这就是为什么在构造函数中您会看到类似以下内容的原因:
class MyWidget : public QWidget // a QObject subclass
{
Q_OBJECT
public:
MyWidget(QWidget *parent = nullptr);
// default destructor is fine!
private:
// raw pointers:
// we won't own these objects through these pointers.
// we just need them to access the pointees
QTimer *m_timer;
QPushButton *m_button;
};
void MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// don't need to save the pointer to this child. because reasons
auto lineEdit = new QLineEdit(this);
auto validator = new QIntValidator(lineEdit); // a nephew
// but let's save the pointers to these children
m_timer = new QTimer(this);
m_button = new QPushButton(this);
// ...
}
默认析构函数将正确删除整棵树,尽管我们通过调用 operator new
分配了 children object,我们甚至懒得保存成员指向一些 children.
堆栈中的 QObjects
您可以(在某些情况下,这实际上是个好主意)将 parent 分配给分配在堆栈上的 object。
典型的例子是 QDialog
个子类:
void MyWidget::showOptionsDialog()
{
// OptionsDialog is a QDialog subclass;
// create an instance as a child of "this" object
OptionsDialog d(this);
// exec the dialog (i.e. show it as a modal dialog)
conts auto result = d.exec();
if (result == QDialog::Accept) {
// apply the options
}
// d gets destroyed here
// => it will remove itself as a child of this
}
将this
作为对话框的parent传递的目的是让对话框以parent的小部件为中心,共享任务托盘条目,并对其进行模态处理。 这在 QDialog
文档中进行了解释。此外,最终 d
只需要存在于该函数中,因此将其声明为自动变量(即分配在堆栈上)是个好主意。
给你:你有一个 QObject
,分配在堆栈上,parent。
那么堆栈上 QObject
的危险是什么?考虑这段代码:
QObject *parent = new QObject;
QObject child(parent);
delete parent;
如前所述,parent
将尝试在 child
上调用 operator delete
,而 object 是 而不是 使用 new
分配(它在堆栈上)。这是非法的(并且可能会崩溃)。
显然,没有人会这样写代码,但请再考虑一下上面的对话框示例。如果在调用 d.exec()
期间,我们设法以某种方式删除了 this
,即对话框的 parent,会怎样?发生这种情况的原因有很多,非常非常难以追踪——例如,数据到达套接字会导致 UI 中的小部件发生变化,创建一些小部件并破坏其他小部件。最终,你会删除一个堆栈变量,崩溃(并且很难重现崩溃)。
因此建议避免 首先创建这样的代码。这不违法,它可能有效,但也可能无效,没有人喜欢脆弱的代码。