Управление памятью в Qt?

96

Я новичок в Qt и меня интересуют некоторые основные вещи, связанные с управлением памятью и жизнью объектов. Когда мне нужно удалить и / или уничтожить мои объекты? Выполняется ли что-либо из этого автоматически?

В приведенном ниже примере, какие из созданных мной объектов мне нужно удалить? Что происходит с переменной экземпляра myOtherClassпри myClassуничтожении? Что произойдет, если я вообще не удалю (или не уничтожу) свои объекты? Будет ли это проблемой для памяти?

MyClass.h

class MyClass
{

public:
    MyClass();
    ~MyClass();
    MyOtherClass *myOtherClass;
};

MyClass.cpp

MyClass::MyClass() {
    myOtherClass = new MyOtherClass();

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

Как видите, это довольно легкая вещь для новичков, но где я могу легко об этом узнать?

Мартин
источник

Ответы:

100

Если вы создаете свою собственную иерархию с помощью QObjects, то есть инициализируете все вновь созданные QObjects с помощью родителя,

QObject* parent = new QObject();
QObject* child = new QObject(parent);

то достаточно , потому что s деструктор будет заботиться об уничтожении . (Он делает это путем выдачи сигналов, поэтому это безопасно, даже если вы удаляете вручную перед родительским.)deleteparentparentchildchild

Вы также можете сначала удалить ребенка, порядок не имеет значения. В качестве примера, где порядок имеет значение, вот документация о деревьях объектов .

Если вы MyClassне QObjectявляетесь потомком , вам придется использовать простой способ работы с C ++.

Также обратите внимание, что иерархия родитель-потомок QObjects обычно не зависит от иерархии иерархии классов / дерева наследования C ++. Это означает, что назначенный дочерний элемент не обязательно должен быть прямым подклассом своего родителя . Подойдет любой (подкласс) QObject.

Однако конструкторы могут накладывать некоторые ограничения по другим причинам; например, in QWidget(QWidget* parent=0), где родительский элемент должен быть другим QWidget, например, из-за флагов видимости и из-за того, что таким образом вы бы сделали базовый макет; но для системы иерархии Qt в целом вам разрешено иметь любого QObjectв качестве родителя.

Дебильски
источник
21
(It does this by issuing signals, so it is safe even when you delete child manually before the parent.)-> Это не причина, по которой это безопасно. В Qt 4.7.4 дочерние deleteэлементы QObject удаляются напрямую (через , см. Qobject.cpp, строка 1955). Причина, по которой сначала безопасно удалять дочерние объекты, заключается в том, что QObject сообщает своему родительскому объекту забыть его при удалении.
Мартин Хеннингс,
5
Я бы добавил, что вы должны убедиться, что деструкторы потомков виртуальны, чтобы это было правдой. Если ClassBнаследуется от QObjectи ClassCнаследует от ClassB, то ClassCбудет правильно уничтожен отношениями родитель-потомок Qt, только если ClassBдеструктор является виртуальным.
Phlucious 06
1
Ссылка в ответе теперь не работает (неудивительно, спустя почти 4 года ...), может, это было что-то вроде этого qt-project.org/doc/qt-4.8/objecttrees.html ?
PeterSW 06
2
Деструктор @Phlucious QObject уже является виртуальным, что автоматически делает деструктор каждого подкласса виртуальным.
rubenvb
1
Если у одного класса где-то в дереве наследования есть виртуальный деструктор, каждый дочерний класс ниже будет иметь виртуальный деструктор. Теперь, если существует конечный родительский класс вне такой цепочки виртуальных деструкторов без виртуального деструктора, я считаю, что у вас могут возникнуть проблемы, если вы удалите указатель на этот конкретный класс, когда фактический объект находится где-то дальше по этой цепочке. В случае дочернего класса QObject и удаления указателя QObject на экземпляр этого дочернего класса никогда не возникает проблем, даже если вы забыли ключевое слово virtual в объявлении деструктора этого подкласса.
rubenvb
47

Я хотел бы расширить ответ Дебильски, указав, что концепция владения очень важна в Qt. Когда класс A принимает на себя владение классом B, класс B удаляется при удалении класса A. Есть несколько ситуаций, когда один объект становится владельцем другого, а не только тогда, когда вы создаете объект и указываете его родителя.

Например:

QVBoxLayout* layout = new QVBoxLayout;
QPushButton someButton = new QPushButton; // No owner specified.
layout->addWidget(someButton); // someButton still has no owner.
QWidget* widget = new QWidget;
widget->setLayout(layout); // someButton is "re-parented".
                           // widget now owns someButton.

Другой пример:

QMainWindow* window = new QMainWindow;
QWidget* widget = new QWidget; //widget has no owner
window->setCentralWidget(widget); //widget is now owned by window.

Итак, часто проверяйте документацию, она обычно указывает, повлияет ли метод на владение объектом.

Как заявил Дебильски, эти правила применяются ТОЛЬКО к объектам, производным от QObject. Если ваш класс не является производным от QObject, вам придется справиться с разрушением самостоятельно.

Остин
источник
В чем разница между написанием: QPushButton * someButton = new QPushButton (); или QPushButton someButton = new QPushButton или просто QPushButton someButton;
Мартин
3
Эхх, есть огромная разница между QPushButton * someButton = new QPushButton; и QPushButton someButton ;. Первый будет размещать объект в куче, а второй - в стеке. Нет никакой разницы между QPushButton * someButton = new QPushButton (); и QPushButton someButton = new QPushButton;, оба они вызовут конструктор объекта по умолчанию.
Остин
Я очень новичок в этом, извините, что спрашиваю, но какая разница между «выделить объект в куче» и «разместить его в стеке»? Когда следует использовать кучу, а когда - стек? Спасибо!
Мартин
3
Вам нужно прочитать о динамическом распределении, области объекта и RAII. В случае простого C ++ по возможности следует размещать объекты в стеке, поскольку объекты автоматически уничтожаются, когда они выходят за пределы области видимости. Для членов класса лучше размещать объекты в куче из-за производительности. И всякий раз, когда вы хотите, чтобы объект «пережил» выполнение функции / метода, вы должны выделить объект в куче. Опять же, это очень важные темы, требующие некоторого чтения.
Остин,
@Austin Общее утверждение о том, что вы должны размещать члены класса в куче для повышения производительности, - это буллоки. Это действительно зависит, и вы должны предпочесть переменные с автоматической продолжительностью хранения, пока не обнаружите проблему с производительностью.
rubenvb
7

Родительский объект (объект QObject или его производный класс) имеет список указателей на его дочерние элементы (QObject / его производный). Родитель удалит все объекты в своем дочернем списке, в то время как родитель будет уничтожен. Вы можете использовать это свойство QObject для автоматического удаления дочерних объектов при удалении родительского объекта. Связь может быть установлена ​​с помощью следующего кода

QObject* parent = new QObject();
QObject* child = new QObject(parent);
delete parent;//all the child objects will get deleted when parent is deleted, child object which are deleted before the parent object is removed from the parent's child list so those destructor will not get called once again.

Есть и другой способ управления памятью в Qt с помощью smartpointer. В следующей статье описаны различные интеллектуальные указатели в Qt. https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/

Yesraaj
источник
-2

Чтобы добавить к этим ответам, для проверки, я бы порекомендовал вам использовать Visual Leak Detetorбиблиотеку для ваших проектов Visual c ++, включая проекты Qt, поскольку она основана на c ++, эта библиотека совместима с new, delete, free and mallocоператорами, она хорошо документирована и проста в использовании. Не забывайте, что когда вы создаете свой собственный QDialogили QWidgetунаследованный класс интерфейса, а затем создаете новый объект этого класса, не забывайте выполнять setAttribute(Qt::WA_DeleteOnClose)функцию вашего объекта.

Халед
источник