ChatGPT解决这个技术问题 Extra ChatGPT

Qt中的内存管理?

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

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

我的类.h

class MyClass
{

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

我的类.cpp

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

    MyOtherClass myOtherClass2;

    QString myString = "Hello";
}

正如你所看到的,这是非常简单的新手,但是我在哪里可以轻松地了解这一点?


u
user2567875

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

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

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

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

如果您的 MyClass 不是 QObject 的子级,则必须使用普通的 C++ 做事方式。

此外,请注意 QObject 的父子层次结构通常独立于 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,那么 ClassC 只有在 ClassB 的析构函数是虚拟的时才会被 Qt 的父子关系正确销毁。
答案中的链接现在已损坏(近 4 年后并不奇怪......),也许是这样的 qt-project.org/doc/qt-4.8/objecttrees.html
@Phlucious QObject 的析构函数已经是虚拟的,这使得每个子类的析构函数都自动成为虚拟的。
如果继承树中的某个类有一个虚拟析构函数,那么下面的每个子类都将有一个虚拟析构函数。现在,如果在这样的虚拟析构函数链之外有一个叶父类而没有虚拟析构函数,我相信如果在实际对象位于该链的某个位置时删除指向该特定类的指针,您可能会遇到问题。对于 QObject 的子类并删除指向该子类实例的 QObject 指针,即使您忘记了该子类的析构函数声明中的 virtual 关键字,也不会出现问题。
G
Georg Schölly

我想通过指出所有权的概念在 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您应该在堆上分配类成员以提高性能的一般声明是公牛。这真的取决于,您应该更喜欢具有自动存储持续时间的变量,直到您发现性能问题。
M
MLandgraf

父级(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


K
Khaled

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