C++ Qt父机制

C++ Qt父机制,c++,qt,C++,Qt,QWidget中有一个QPushButton,单击该按钮应打开另一个QWidget,如下所示: project.pro QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = untitled TEMPLATE = app CONFIG(debug, debug|release) { DESTDIR = debug } else { DESTDIR = release }

QWidget中有一个QPushButton,单击该按钮应打开另一个QWidget,如下所示:

project.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = untitled
TEMPLATE = app

CONFIG(debug, debug|release) {
    DESTDIR = debug
} else {
    DESTDIR = release
}

INCLUDEPATH += ..

SOURCES += ../main.cpp\
           ../widget.cpp \
           ../secondwidget.cpp

HEADERS  += ../widget.h \
            ../secondwidget.h
widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H
在注释行中将其传递给SecondWidget构造函数将在某个时候中断我的逻辑。因此,当单击按钮时,第二个小部件不再显示

发生了什么事?

如果 类是链接的 析构函数调用未正确调用 Makefile需要一些更改。

实际错误

需要使用cmd提示符或终端重新生成Makefile

确保析构函数不调用自身,这样就改变了执行的补救方法


除了析构函数和构造函数的问题以及由于第二个问题和项目文件而导致的内存泄漏之外,为了了解整个父级情况,您可能需要了解以下几点:

你不需要通过这个考试。分配父对象的目的是简化QObject实例的清理过程,包括从QObject类继承的所有类。Qt父子模型遵循C++标准,即析构函数以构造函数的相反顺序调用。简单来说,这意味着:

假设您创建小部件A、B和C。使用父属性,您将B和C指定为A的子项。因此,这里我们有创建顺序:

父母 B、 C儿童 现在,您需要销毁小部件示例:关闭应用程序。标准C++方式是将它们按以下顺序反向构造顺序:

B、 C儿童 父母 如果我们先去找父母,就会发生这种情况。然而,我们在这里处理QT,所以我们必须考虑库提供的一个附加特征——槽和信号。 每当QObject被破坏时,它就会发出一个信号。根据对象在父子关系中扮演的角色,结果如下:

只有被销毁的QObject才会被销毁——这就是当一个子对象在我们销毁其父对象之前被销毁时会发生的情况 该QoObject的所有子对象首先被销毁,然后是QoObject本身的销毁——这就是父对象被销毁时发生的情况。发射的信号被其所有子信号捕获,而子信号也被消除。 但是,分配父项不是强制性的。您可以自己手动进行清理。请阅读中有关父子模型的详细信息

除了所有权转移之外,一个小部件成为另一个小部件的子部件通常是自动发生的,因此无需显式指定小部件的父部件。这里也有例外。如果将小部件添加到布局,则所有权不会转移到布局本身,而是转移到它所属的QWidget

最后,有一个重要的例子是,如果不指定一位家长,事情会变得非常非常不同。在这里,我将引用关于QoObject的:

将parent设置为0将构造一个没有父对象的对象。如果对象是一个小部件,它将成为一个顶级窗口

因此,如果您有一个QWidget,并且没有将其添加到某个布局中,或者间接添加到具有该布局的QWidget中,例如,它将自动成为一个单独的窗口

编辑: 检查您的构造函数,特别是您使用secondWidget对象的方式。正如我上面提到的,如果您不想将其分配给父窗口小部件,您需要负责清理

您可以动态地为它分配内存

    SecondWidget *secondWidget = new SecondWidget();
甚至连上你的按钮

    connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
但是,您永远不会使用delete释放分配的内存,也不会将其分配给父窗口小部件,父窗口小部件将自动处理它。因此会造成内存泄漏。通过信号和插槽连接QObject与所有权转移无关

我个人认为,您实际上希望secondWidget成为屏幕上显示的额外窗口。在这种情况下,您需要创建SecondWidget类型的类成员

然后在构造函数中分配它,并连接您想要的插槽和信号

Widget::Widget(QWidget *parent) : QWidget(parent)
{
  //...
  secondWidget = new SecondWidget();
  connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
最后释放构造函数中的内存:

Widget::~Widget()
{
  delete secondWidget;
}
否则,正如我所说,您基本上是在创建一个对内存块的引用,并且在您离开构造函数之后,该引用将被销毁,因为它超出了范围

编辑2:

下面是一个小示例,如果您希望将secondWidget作为主widget的子部件,该如何操作:

main.cpp

secondwidget.hpp

它基本上遍历这个小部件或第二个小部件的所有子部件并显示其子部件。就我而言,我得到了

    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
一旦我启动了我的应用程序

编辑3:啊,我没有注意到您正在调用第二个小部件构造函数中的QWidgetparent。这也起到了作用,因此您不需要setParentparent。我已更改了第二次编辑。

SecondWidget,对不起,JU
未更正。实际问题是什么?请提供一个。例如,您可以将其作为基础。取消注释注释行并注释其上方的行。这将阻止SecondWidget打开。我们无法运行您的代码,因为它不完整。它不会编译。我提供的示例很有效,因此我只能假设问题出在我们没有的代码中。您是否介意明确指出析构函数的问题以及由此导致的内存泄漏?我很难做到secondWidget=new secondWidget这一点;忘记删除secondWidget;。所以不可能避免显式的deletessecondwidget;?我在括号里看不出这一点。如果您没有为QObject指定父对象,那么QWidget是从QObject派生的,正如我在帖子中所写的,您需要自己处理清理工作,因为您创建的实例都是自己创建的。如果不确定,您可以随时使用valgrind检查内存分配情况。因此,要么将secondWidget作为子部件添加,要么将其作为类成员外包,并负责析构函数内部的清理;?我正在尝试使用注释行,而不是它前面的一行。但这样下去,当我点击按钮时,什么也没发生。你也可以通过简单地输入代码,看看发生了什么,来检查类似的事情,这是学习东西的最好方法。例如,我刚刚在我的计算机代码中将其添加到secondWidget中。执行时,小部件被嵌入主小部件中。它看起来很奇怪,因为它不是布局的一部分,但它就在那里。您应该在主窗口小部件中使用“隐藏”,否则即使尚未触发“显示”,第二个窗口小部件也会在其上绘制。它基本上被视为QLabel或类似。您不需要显式显示标签:
SecondWidget *secondWidget;
Widget::Widget(QWidget *parent) : QWidget(parent)
{
  //...
  secondWidget = new SecondWidget();
  connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
Widget::~Widget()
{
  delete secondWidget;
}
#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  Widget w;
  w.show();

  return a.exec();
}
#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
#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;
}
#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
#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()
{

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