2011-06-03 66 views
14

在Qt中,我可以通过编写将子窗口小部件嵌入其父项中,还是必须使用new创建它们?Qt:子对象可以在他们的父对象中组成吗?

class MyWindow : public QMainWindow 
{ 
    ... 
private: 
    QPushButton myButton; 
} 

MyWindow::MyWindow() 
: mybutton("Do Something", this) 
{ 
    ... 
} 

文档说,当它的父被破坏从QObject派生的任何对象会自动销毁;这意味着致电delete,在上面的例子中会崩溃。

我必须使用以下吗?

QPushButton* myButton; 

myButton = new QPushButton("Do Something", this); 

编辑

的答案是多种多样的,基本上可以归结为三种可能性:

  • ,构图OK。 Qt可以找出对象是如何分配和仅delete堆中分配的对象(这是如何工作的?)
  • ,成分是好的,但不指定父,因为父原本叫delete上该对象(但是不会将无母公司的小部件变成顶级窗口?)
  • ,小部件总是需要堆分配。

哪一个是正确的?

回答

7

当特定对象的删除序列开始时,非静态非堆成员变量被删除。只有当所有成员都被删除后,它才会进入基类的析构函数。因此,在调用〜QMainWindow()之前,QPushButton myButton成员将被删除。从QObject文档:“如果我们删除其父对象之前的子对象,Qt会自动从父对象的子对象列表中删除该对象”。因此不会发生崩溃。

+1

你的回答几乎是正确的,但销毁顺序不正确。见例如http://msdn.microsoft.com/en-us/library/8183zf3x%28v=vs.100%29.aspx – hmuelner 2012-04-24 10:11:05

+0

@hmuelner修复。 – Oktalist 2014-07-26 20:49:47

2

对象将只毁坏了,当它有一个父指针,所以你可以使用:

MyWindow::MyWindow() 
: mybutton("Do Something", 0) 
{ 
    ... 
} 
+4

如果'QWidget'没有父亲,是不是会变成顶级窗口? – 2011-06-03 08:10:23

+0

是的,这是真的。如果您希望QWidget成为另一个QWidget的子项,则必须给父项,这意味着您必须在堆上创建它。但是还有其他的QObject派生类,它们将它们用作成员变量是有意义的。 – hmuelner 2011-06-03 11:33:27

+0

-1:您的评论比实际答案更多。正如其他人所指出的那样,根本没有暗示'QObject'必须放在堆上,因为它有一个父对象。 – Troubadour 2012-04-23 18:59:01

0

您应该堆上创建它,因为QObject的必摧之:

class MyWindow : public QMainWindow 
{ 
    ... 
private: 
    QPushButton *myButton; 
} 

MyWindow::MyWindow() 
: mybutton(new QPushButton("Do Something", this)) 
{ 
    ... 
} 
+0

-1:正如其他人指出的,没有必要在堆上创建它。 – Troubadour 2012-04-23 18:59:43

0

通话delete运营商不会崩溃你的应用程序,你可以阅读以下报价

Qt的亲子机制是在QObject中实现。当我们用一个父对象创建一个对象(一个控件,验证器或任何其他类型)时,父对象将其添加到其子对象列表中。当父母被删除时,它遍历其子列表并删除每个孩子。然后孩子们自己删除他们所有的孩子,等等递归直到没有人留下。父子机制大大简化了内存管理,降低了内存泄漏的风险。我们必须调用delete的唯一对象是我们用new创建的对象,并且没有父对象。如果我们删除父对象之前的子对象,Qt会自动从父对象的子对象列表中删除对象。

注意,父参数是NULL在默认情况下(默认参数) 这是QPushButton构造

QPushButton (const QString & text, QWidget * parent = 0) 

所以u可以使用

MyWindow::MyWindow() : mybutton(new QPushButton("Do Something")){ ... } 

和u可以在任何组件上调用delete并且任何时候都可以使用

Qt会照顾这个po int

4

该文档说,从QObject派生的任何对象都会在其父对象被销毁时自动销毁; 这意味着调用删除

号这意味着到特定实体的析构函数通话。

在你的例子中说,如果MyWindow被销毁,这意味着已经调用了MyWindow的析构函数。而这又将调用已在QPushButton中实施的析构函数myButton

如果您有复合实体,只需要在该实体上调用析构函数,但不是delete,因此它不会崩溃。

在Qt中的父子关系并不需要特定的堆栈或堆。它可以是任何东西。

堆栈上的父子关系中的类似示例超过here

HTH ..

+2

父母如何知道孩子是否被堆分配?据我了解,调用析构函数不会释放对象占用的堆空间(“delete”是必需的) - 或者我错了吗? – 2011-06-03 08:43:53

+0

@ Jen,是的。如果它被分配给'新',你必须'删除'它。我认为它是由Qt元对象系统,该应用程序能够识别孩子的分配..无法找到任何有关的文档。将更新,如果我找到了什么.. – liaK 2011-06-03 08:54:15

+0

@仁,但是不会崩溃。你试过了吗?它力量崩溃吧? – liaK 2011-06-03 08:57:31

4

Object trees & ownership回答你的问题。基本上,当在堆上创建子对象时,它的父对象将被删除。

另一方面,当在堆栈上创建子对象时,销毁顺序很重要。这个孩子将在之前销毁其父,并将从其父母的列表中删除,以便其析构函数不会被调用两次。

该链接中还有一个示例,显示有问题的销毁顺序。

+0

所以,如果我们有(堆栈或堆)复合,并且它被破坏(通过'delete'或者当堆栈对象的生命期结束时),首先调用复合的析构函数(并且执行它想做的任何事情,因为这个逻辑是在QObject中实现的),那么组合的子/成员就会被破坏(使用它的析构函数调用)并从对象中继续保留它们(再次使用QObject的功能),然后QObject的析构函数终于被调用,并且看不到任何孩子 - 因此不需要双重删除。 – mlvljr 2012-05-26 14:44:05

0

让我只需quote the source在这里。

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它的每一个在析构函数的孩子。另外,析构函数被执行的任何成员的析构函数都是before;所以如果问题组合等于父母 - 那么成员QObject将没有任何机会从其父母的子女列表中删除自己。

很不幸,这意味着你不能撰写其父QObject。但是,您可以编写其他对象,以及在堆栈上进行分配 - 只要您保证破坏对象或将其父对象重置为之前父对象开始破坏。

相关问题