除了析构函数和构造函数的问题(以及由于第二个而导致的内存泄漏)以及这里的项目文件之外,您可能还需要了解一些内容,以便了解整个父级情况:
你不需要通过this
。分配父级的目的是能够简化清理过程QObject
实例(包括所有继承自QObject
班级)。 Qt 父子模型遵循 C++ 标准,即析构函数以相反的顺序调用构造函数。简单来说,这意味着以下内容:
想象你创造小部件 A、B 和 C。使用您分配的父属性B and C成为...的孩子A。所以这里我们有创建的顺序:
- 家长)
- B、C(儿童)
现在在某个时候您想要销毁您的小部件(例如:关闭应用程序)。标准 C++ 方法是按以下顺序(与构造相反)销毁它们:
- B、C(儿童)
- 家长)
如果我们首先寻找父母,这确实会发生。然而,我们在这里处理的是 Qt,因此我们必须考虑该库提供的一项附加功能 - 槽和信号。
每当一个QObject
被摧毁时会发出信号。根据对象在父子关系中扮演的角色,结果是以下之一:
-
只有
QObject
被摧毁的被摧毁- 这就是当我们销毁其父级之前子级被销毁时会发生的情况
-
那个的所有孩子
QObject
首先被摧毁,然后是被摧毁 QObject
itself- 这就是当父母被摧毁时会发生的情况。发出的信号会被所有子级捕获,而这些子级也会被消除。
然而指定父母并不是强制性的。您可以自己手动进行清理。阅读有关亲子模型的更多信息Qt 文档 http://doc.qt.io/qt-4.8/objecttrees.html.
除此之外,所有权转移(一个小部件成为另一个小部件的子部件)通常会自动发生,因此无需显式指定小部件的父部件。再次把东西从Qt 文档 http://doc.qt.io/qt-4.8/layout.html#tips-for-using-layouts这里有一个例外。如果将小部件添加到布局中,所有权不会转移到布局本身,而是转移到QWidget
它是的一部分。
最后,还有一个重要的情况,如果不指定父级,事情就会变得非常非常不同。这里我要引用Qt 文档 http://doc.qt.io/qt-4.8/qobject.html#QObject on QObject
:
将parent 设置为0 将构造一个没有父对象的对象。如果该对象是一个小部件,它将成为一个顶级窗口。
所以如果你有一个QWidget
并且不要将其添加到某些布局中(以及间接添加到QWidget
具有该布局)例如它将自动成为一个单独的窗口。
EDIT:检查你的构造函数,特别是你使用你的构造函数的方式secondWidget
目的。正如我上面提到的,如果您不想将其分配给父小部件you需要注意清洁。
您为其动态分配内存
SecondWidget *secondWidget = new SecondWidget();
甚至将它连接到您的按钮
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
但是你永远不会使用释放分配的内存delete
或者将其分配给父小部件,父小部件将自动处理它。因此你会造成内存泄漏。连接一个QObject
通过信号和槽与所有权转移没有任何关系。
我个人认为你实际上想要一个secondWidget
成为屏幕上显示的额外窗口。在这种情况下,您需要创建一个类型的类成员SecondWidget
SecondWidget *secondWidget;
然后在你的构造函数中分配它并连接你想要的任何插槽和信号
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//...
secondWidget = new SecondWidget();
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
最后释放构造函数中的内存:
Widget::~Widget()
{
delete secondWidget;
}
否则,正如我所说,您基本上是在创建对内存块的引用,并且在您离开构造函数之后,该引用就会被销毁,因为它超出了范围。
EDIT 2:
这是一个小例子,如果您愿意,如何做到这一点secondWidget
作为主小部件的子项:
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
小部件.hpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "secondwidget.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
小部件.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
SecondWidget *secondWidget = new SecondWidget(this); # secondWidget is now officially adopted by Widget
# If you skip the parent assignment inside SecondWidget you can call secondWidget->setParent(this) here
connect(ui->pushButton, SIGNAL(clicked(bool)), secondWidget, SLOT(show()));
}
Widget::~Widget()
{
delete ui;
}
第二个小部件.hpp
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QObject>
#include <QDialog>
class SecondWidget : public QDialog
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
~SecondWidget();
};
#endif // SECONDWIDGET_H
第二个小部件.cpp
#include "secondwidget.h"
#include <QFormLayout>
#include <QLabel>
SecondWidget::SecondWidget(QWidget *parent) : QDialog(parent)
{
# If you don't call the constructor of your superclass you can still assign a parent by invoking setParent(parent) here
QFormLayout *layout = new QFormLayout();
QLabel *label = new QLabel("SecondWidget here");
layout->addWidget(label); # Transfer ownership of label to SecondWidget
setLayout(layout);
}
SecondWidget::~SecondWidget()
{
}
请注意setParent(parent)
在 - 的里面SecondWidget
的构造函数。您要么必须调用超类构造函数(就像您所做的那样),要么手动调用setParent(parent). If you don't do that your
第二个小部件will not be assigned as a child to your main widget where you create it and thus you will produce a memory leak. You can also invoke
在主窗口小部件的构造函数中使用 secondaryWidget->setParent(this)` 来设置父窗口。
以下是检查父子层次结构是否正常的方法:
To each QObject
你有(QWidget
, QLayout
等等都是以下的子类QObject
)使用分配对象名称QObject::setObjectName("some name")
-
在两个构造函数的末尾添加:
for(int i = 0; i < this->children().count(); i++)
std::cout << this->children()[i]->objectName().toStdString() << std::endl; // Add #include <iostream> to get the output
基本上遍历了所有的孩子this
(Widget
or SecondWidget
)并显示其子项。就我而言,我得到了
label // Printing out children of SecondWidget
formLayout // Printing out children of SecondWidget
gridLayout // Printing out children of Widget
pushButton // Printing out children of Widget
main second widget // Printing out children of Widget
一旦我启动了我的应用程序。
EDIT 3:啊,我没注意到你正在打电话QWidget(parent)
in you SecondWidget
构造函数。这也可以解决问题,所以你不需要setParent(parent)
。我改变了我的第二次编辑。