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";
}

如您所见,这是个非常容易上手的东西,但是我在哪里可以以简单的方式了解到呢?

Answers:


100

如果您使用QObjects 构建自己的层次结构,即QObject用父级初始化所有新创建的s,

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

那么就足够deleteparent,因为parents的析构函数将负责销毁child。(它是通过发出信号来完成的,因此即使在child父级之前手动删除,它也是安全的。)

您也可以先删除孩子,顺序无关紧要。对于将订单的示例事情这里是关于对象树的文档

如果您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中,直接删除了QObject子级(通过delete,请参见qobject.cpp,第1955行)。首先删除子对象是安全的,原因是QObject告诉其父对象在删除时忘记了它。
Martin Hennings

5
我要补充一点,您必须确保后代的析构函数是虚拟的,这样才能实现。如果ClassB继承自QObjectClassC继承自ClassB,则ClassC仅当Qt的父子关系被正确销毁时,ClassB的析构函数是虚拟的时。
富裕的

1
答案中的链接现在断开了(将近4年之后就不足为奇了……),也许是类似qt-project.org/doc/qt-4.8/objecttrees.html的东西?
PeterSW 2014年

2
@Phlucious QObject的析构函数已经是虚拟的,这使每个子类的析构函数自动进行虚拟化。
rubenvb

1
如果继承树中某处的一个类具有虚拟析构函数,则下面的每个子类都将具有一个虚拟析构函数。现在,如果在此类虚拟析构函数链之外没有虚拟析构函数的情况下有叶父类,我相信如果当实际对象位于该链的更下方时删除指向该特定类的指针,则可能会遇到问题。对于QObject的子类并删除指向该子类实例的QObject指针,即使您忘记了该子类的析构函数声明中的virtual关键字,也永远不会有问题。
rubenvb

47

我想通过指出所有权概念在Qt中非常重要来扩展Debilski的答案。当类别A拥有类别B的所有权时,则在删除类别A时删除类别B。在多种情况下,一个对象成为另一个对象的所有者,不仅是在创建对象并指定其父对象时。

例如:

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.

因此,经常检查文档,它通常指定方法是否会影响对象的所有权。

如Debilski所述,这些规则仅适用于从QObject派生的对象。如果您的类不是从QObject派生的,则必须自己处理销毁。


编写之间有什么区别:QPushButton * someButton = new QPushButton(); 或QPushButton someButton =新的QPushButton或只是QPushButton someButton;
马丁

3
恩,QPushButton * someButton = new QPushButton之间有很大的区别;和QPushButton someButton;。前者将在堆上分配对象,而后者将在堆上分配对象。QPushButton之间没有区别* someButton = new QPushButton(); 和QPushButton someButton = new QPushButton ;,它们都将调用对象的默认构造函数。
奥斯汀2010年

我对此很陌生,因此很抱歉询问,但是“在堆上分配对象”和“在堆栈上分配对象”之间有什么区别?什么时候应该使用堆,什么时候应该使用堆栈?谢谢!
马丁

3
您需要阅读有关动态分配,对象范围和RAII的信息。对于纯C ++,应尽可能在堆栈上分配对象,因为当对象超出范围时,对象将被自动销毁。对于类成员,由于性能原因,最好在堆上分配对象。而且,每当您希望对象“失效”函数/方法的执行时,都应在堆上分配该对象。同样,这些都是非常重要的主题,需要阅读。
奥斯汀2010年

@Austin关于性能应该在堆上分配类成员的一般说法是is子。这确实取决于您,您应该选择具有自动存储持续时间的变量,直到发现性能问题为止。
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.

还有其他方法可以使用smartpointer在Qt中管理内存。下面的文章介绍了Qt中的各种智能指针。 https://blog.qt.digia.com/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have/


-2

为了补充这些答案,为了验证,我建议您Visual Leak Detetor为Visual c ++项目使用库,包括Qt项目,因为它基于c ++,该库与new, delete, free and malloc语句兼容,文档齐全,易于使用。不要忘记,当您创建自己的QDialogQWidget继承的接口类,然后创建此类的新对象时,请不要忘记执行setAttribute(Qt::WA_DeleteOnClose)对象的功能。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.