Qt: Могут ли дочерние объекты быть составлены в их родительском объекте? - PullRequest
14 голосов
/ 03 июня 2011

В 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);

EDIT

Ответы весьма разнообразны, и в основном сводятся к трем возможностям:

  • Да , композиция в порядке. Qt может выяснить, как был выделен объект и только delete объекты, выделенные в куче (Как это работает?)
  • Да , композиция в порядке, но не указывайте родителя, так как родитель в противном случае вызовет delete для объекта (но не превратится ли виджет без родительского элемента в верхний окно уровня?)
  • Нет , виджеты всегда должны быть выделены в куче.

Какой из них правильный?

Ответы [ 7 ]

7 голосов
/ 03 июня 2011

Нестатические переменные, не являющиеся членами кучи, удаляются при запуске последовательности удаления этого конкретного объекта.Только когда все члены будут удалены, он перейдет к деструктору базового класса.Следовательно, член myPutton QPushButton будет удален до вызова ~ QMainWindow ().А из документации QObject: «Если мы удалим дочерний объект перед его родителем, Qt автоматически удалит этот объект из списка потомков родителя».Следовательно, никакого сбоя не произойдет.

4 голосов
/ 03 июня 2011

Деревья объектов и собственность отвечает на ваш вопрос.В основном, когда дочерний объект создается в куче, он будет удален его родителем.

С другой стороны, когда дочерний объект создается в стеке, важен порядок уничтожения.Дочерний объект будет уничтожен за до своего родителя и удалит себя из списка своих родителей, чтобы его деструктор не вызывался дважды.

В этой ссылке также есть пример, который показывает проблемный порядокразрушение.

4 голосов
/ 03 июня 2011

В документации сказано, что любой объект, полученный из QObject, будет автоматически уничтожен при уничтожении его родителя; это подразумевает вызов на удаление

Нет. Это подразумевает вызов деструктора этой конкретной сущности.

Скажем, в вашем примере, если MyWindow уничтожен, это означает, что был вызван деструктор MyWindow. Который в свою очередь вызовет деструктор myButton, который реализован уже в QPushButton.

Если у вас есть составная сущность, для этой сущности будет вызван только деструктор, но не delete, поэтому он не потерпит крах.

Родительские дочерние отношения в Qt не требуют специально находиться в стеке или куче. Это может быть что угодно.

Аналогичный пример родительских дочерних отношений в стеке более здесь .

НТН ..

2 голосов
/ 03 июня 2011

Объект будет уничтожен только при наличии родительского указателя, поэтому вы можете использовать:

MyWindow::MyWindow ()
 : mybutton("Do Something", 0)
{
   ...
}
0 голосов
/ 01 апреля 2013

Позвольте мне просто процитировать источник здесь.

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 до того, как родитель начнет разрушать.

0 голосов
/ 03 июня 2011

звонящий delete оператор не вылетит из вашего приложения, вы можете прочитать следующую цитату

Механизм «родитель-потомок» Qt реализован в QObject. Когда мы создаем объект (виджет, валидатор или любой другой вид) с родителем, родитель добавляет объект в список его потомков. Когда родитель удаляется, он просматривает свой список потомков и удаляет каждого потомка. Затем сами дети удаляют всех своих детей и т. Д. До тех пор, пока их не останется. Механизм «родитель-потомок» значительно упрощает управление памятью, снижая риск утечек памяти. Единственные объекты, которые мы должны вызвать delete, это объекты, которые мы создаем с помощью new и у которых нет родителя. И если мы удалим дочерний объект перед его родителем, Qt автоматически удалит этот объект из списка потомков родителя.

обратите внимание, что родительский аргумент по умолчанию NULL (аргумент по умолчанию) это конструктор QPushButton

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

так что вы можете использовать

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

и вы можете звонить delete на любом компоненте и в любое время

Qt позаботится об этом

0 голосов
/ 03 июня 2011

Вы должны создать его в куче, так как QObject уничтожит его:

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

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