在Qt中,我可以通过组合将子小部件嵌入到它们的父小部件中,还是必须使用new
创建它们?
class MyWindow : public QMainWindow
{
...
private:
QPushButton myButton;
}
MyWindow::MyWindow ()
: mybutton("Do Something", this)
{
...
}
文档说,任何派生自QObject
的对象都会在其父对象被销毁时自动销毁;这意味着对delete
的调用,在上面的例子中会崩溃。
我必须使用以下方法吗?
QPushButton* myButton;
myButton = new QPushButton("Do Something", this);
编辑
答案是多种多样的,基本上归结为三种可能性:
- 是的,构图还可以。Qt可以弄清楚对象是如何分配的,并且只
delete
堆分配的对象(这是如何工作的?
是的, - 组合是可以的,但不要指定父级,因为父级会在对象上调用
delete
(但是无父级的小部件不会变成顶级窗口吗? - 不,小部件始终必须进行堆分配。
哪一个是正确的?
当该特定对象的删除序列开始时,将删除非静态、非堆成员变量。只有当删除所有成员时,它才会转到基类的析构函数。因此,QPushButton myButton 成员将在调用 ~QMainWindow(( 之前被删除。来自QObject文档:"如果我们删除一个子对象在其父对象之前,Qt将自动从父对象的子对象列表中删除该对象"。因此不会发生崩溃。
对象树和所有权回答了你的问题。基本上,当在堆上创建子对象时,它将被其父对象删除。
另一方面,当在堆栈上创建子对象时,销毁顺序很重要。子项将在其父级之前销毁,并将自身从其父级列表中删除,以便其析构函数不会调用两次。
该链接中还有一个示例,显示了有问题的销毁顺序。
文档说,任何派生自 QObject 的对象都会在其父对象被销毁时自动销毁;这意味着要求删除
不。它意味着对该特定实体的析构函数的调用。
假设在您的示例中,如果MyWindow
被销毁,则表示已调用MyWindow
的析构函数。这反过来将调用析构函数myButton
该析构函数已在QPushButton
中实现。
如果您有复合实体,则只会在该实体上调用析构函数,而不会delete
因此它不会崩溃。
Qt中的父子关系不需要特别在堆栈或堆中。 它可以在任何东西中。
堆栈上的父子关系中的类似示例在这里结束。
呵呵。。。
仅当对象具有父指针时,才会销毁该对象,因此您可以使用:
MyWindow::MyWindow ()
: mybutton("Do Something", 0)
{
...
}
你应该在堆上创建它,因为 QObject 会破坏它:
class MyWindow : public QMainWindow
{
...
private:
QPushButton *myButton;
}
MyWindow::MyWindow ()
: mybutton( new QPushButton( "Do Something", this) )
{
...
}
调用delete
运算符不会使您的应用程序崩溃,您可以阅读以下引用
Qt的父子机制在QObject中实现。当我们使用父对象(小部件、验证器或任何其他类型(创建对象时,父对象会将该对象添加到其子对象列表中。删除父项时,它将遍历其子项列表并删除每个子项。然后,子项自己会递归删除其所有子项,依此类推,直到没有剩余项。父子机制极大地简化了内存管理,降低了内存泄漏的风险。我们必须调用 delete 的唯一对象是我们使用 new 创建的并且没有父对象的对象。如果我们在父对象之前删除一个子对象,Qt将自动从父对象的子对象列表中删除该对象。
请注意,默认情况下NULL
父参数(默认参数(这是QPushButton构造函数
QPushButton ( const QString & text, QWidget * parent = 0 )
所以你可以使用
MyWindow::MyWindow () : mybutton( new QPushButton( "Do Something") ){ ... }
您可以随时在任何组件上调用delete
Qt会注意这一点
让我在这里引用来源。
816 QObject::~QObject()
817 {
818 Q_D(QObject);
819 d->wasDeleted = true;
820 d->blockSig = 0; // unblock signals so we always emit destroyed()
821
...
924
925 if (!d->children.isEmpty())
926 d->deleteChildren();
927
928 qt_removeObject(this);
929
930 if (d->parent) // remove it from parent object
931 d->setParent_helper(0);
932
933 #ifdef QT_JAMBI_BUILD
934 if (d->inEventHandler) {
935 qWarning("QObject: Do not delete object, '%s', during its event handler!",
936 objectName().isNull() ? "unnamed" : qPrintable(objectName()));
937 }
938 #endif
939 }
...
1897 void QObjectPrivate::deleteChildren()
1898 {
1899 const bool reallyWasDeleted = wasDeleted;
1900 wasDeleted = true;
1901 // delete children objects
1902 // don't use qDeleteAll as the destructor of the child might
1903 // delete siblings
1904 for (int i = 0; i < children.count(); ++i) {
1905 currentChildBeingDeleted = children.at(i);
1906 children[i] = 0;
1907 delete currentChildBeingDeleted;
1908 }
1909 children.clear();
1910 currentChildBeingDeleted = 0;
1911 wasDeleted = reallyWasDeleted;
1912 }
如您所见,QObject
确实在析构函数中delete
其每个子项。此外,析构函数在任何成员的析构函数之前执行;因此,如果所讨论的复合等于父级 - 则成员QObject
将没有任何机会将自己从其父级的子列表中删除。
不幸的是,这意味着您无法将QObject
组合到其父级中。但是你可以组合到其他对象中,也可以在堆栈上分配 - 只要你保证在父对象开始破坏之前销毁对象或将其父对象重置为 0。