【问题标题】:Memory management in Qt?Qt中的内存管理?
【发布时间】:2011-01-30 07:21:54
【问题描述】:

我对 Qt 还很陌生,想了解一些关于内存管理和对象生命周期的基本知识。我什么时候需要删除和/或销毁我的对象?这些都是自动处理的吗?

在下面的示例中,我需要删除我创建的哪些对象?当myClass 被销毁时,实例变量myOtherClass 会发生什么?如果我根本不删除(或销毁)我的对象会发生什么?这会是记忆的问题吗?

MyClass.h

class MyClass
{

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

MyClass.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

如您所见,这是非常适合新手的东西,但我在哪里可以轻松了解这一点?

【问题讨论】:

    标签: c++ qt memory object


    【解决方案1】:

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

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

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

    您也可以先删除孩子,顺序无关紧要。例如,此处的顺序确实很重要the documentation about object trees

    如果您的 MyClass 不是 QObject 的子代,则必须使用普通的 C++ 处理方式。

    另外,请注意QObjects 的父子层次结构通常独立于 C++ 类层次结构/继承树的层次结构。这意味着,分配的子类不需要是其父类的直接子类QObject 的任何(子类)都足够了。

    但是,由于其他原因,构造函数可能会施加一些限制;例如在QWidget(QWidget* parent=0) 中,父级必须是另一个QWidget,因为例如可见性标志,因为您会以这种方式进行一些基本布局;但是对于 Qt 的层次系统,一般来说,您可以将任何QObject 作为父级。

    【讨论】:

    • (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告诉它的父对象在它被删除时忘记它。
    • 我要补充一点,你必须确保后代的析构函数是虚拟的,才能做到这一点。如果ClassB 继承自QObject 并且ClassC 继承自ClassB,那么只有ClassB 的析构函数是虚拟的,ClassC 才会被Qt 的父子关系正确销毁。
    • 答案中的链接现在已损坏(近 4 年后并不奇怪......),也许是这样的 qt-project.org/doc/qt-4.8/objecttrees.html
    • @Phlucious QObject 的析构函数已经是虚拟的,这使得每个子类的析构函数都自动成为虚拟的。
    • 如果继承树中的某个类有一个虚拟析构函数,那么下面的每个子类都会有一个虚拟析构函数。现在,如果在这样的虚拟析构函数链之外有一个叶父类而没有虚拟析构函数,我相信如果在实际对象位于该链的某个位置时删除指向该特定类的指针,您可能会遇到问题。对于 QObject 的子类并删除指向该子类实例的 QObject 指针,即使您忘记了该子类的析构函数声明中的 virtual 关键字,也不会出现问题。
    【解决方案2】:

    我想通过指出所有权概念在 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 = new QPushButton 或只是 QPushButton someButton;
    • 呃,QPushButton *someButton = new QPushButton; 之间有很大的不同。和 QPushButton someButton;。前者将在堆上分配对象,而后者将在堆栈上分配它。 QPushButton *someButton = new QPushButton(); 之间没有区别和QPushButton someButton = new QPushButton;,它们都会调用对象的默认构造函数。
    • 我对此很陌生,很抱歉问这个问题,但是“在堆上分配对象”和“在堆栈上分配”有什么区别?什么时候应该使用堆,什么时候应该使用堆栈?谢谢!
    • 您需要了解动态分配、对象范围和 RAII。在纯 C++ 的情况下,您应该尽可能在堆栈上分配对象,因为对象在超出范围时会自动销毁。对于类成员,出于性能考虑,最好在堆上分配对象。并且无论何时您希望一个对象“比”一个函数/方法的执行更长寿,您应该在堆上分配该对象。同样,这些都是非常重要的主题,需要阅读。
    • @Austin 您应该在堆上分配类成员以提高性能的一般声明是公牛。这真的取决于您应该更喜欢具有自动存储持续时间的变量,直到您发现性能问题。
    【解决方案3】:

    父级(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 中还有其他方法可以使用智能指针来管理内存。下面的文章描述了 Qt 中的各种智能指针。 https://www.qt.io/blog/2009/08/25/count-with-me-how-many-smart-pointer-classes-does-qt-have

    【讨论】:

      【解决方案4】:

      为了补充这些答案,为了验证,我建议您将 Visual Leak Detetor 库用于您的 Visual c++ 项目,包括 Qt 项目,因为它基于 c++,该库与 new, delete, free and malloc 语句兼容,它是有据可查且易于使用。不要忘记,当你创建自己的QDialogQWidget继承接口类,然后创建这个类的新对象时,不要忘记执行你的对象的setAttribute(Qt::WA_DeleteOnClose)函数。

      【讨论】:

        猜你喜欢
        • 2011-04-30
        • 1970-01-01
        • 1970-01-01
        • 2012-02-27
        • 1970-01-01
        • 2014-06-02
        • 2014-06-05
        • 2019-09-27
        • 1970-01-01
        相关资源
        最近更新 更多