Qt父机制

时间:2016-02-20 12:24:56

标签: c++ qt

QPushButton中只有一个QWidgetclick该按钮应打开另一个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

widget.cpp

#include "widget.h"

#include <QVBoxLayout>
#include <QPushButton>

#include "secondwidget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    resize(400, 300);
    QVBoxLayout *vLayout = new QVBoxLayout(this);
    QPushButton *bt = new QPushButton("Open 2nd Widget");
    vLayout->addWidget(bt, 1, Qt::AlignRight);

    SecondWidget *secondWidget = new SecondWidget();
//    SecondWidget *secondWidget = new SecondWidget(this);
    connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}

Widget::~Widget()
{

}

secondwidget.h

#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H

#include <QWidget>

class SecondWidget : public QWidget
{
    Q_OBJECT
public:
    explicit SecondWidget(QWidget *parent = 0);

signals:

public slots:
};

#endif // SECONDWIDGET_H

secondwidget.cpp

#include "secondwidget.h"

SecondWidget::SecondWidget(QWidget *parent) : QWidget(parent)
{
    setAttribute( Qt::WA_QuitOnClose, false );
}

this构造函数传递SecondWidget(如在注释行中)将在某个时刻制造我的逻辑。因此,当单击按钮时,SecondWidget不再显示。

发生了什么事?

3 个答案:

答案 0 :(得分:2)

除了析构函数和构造函数的问题(以及由于第二个而导致的内存泄漏),此处的项目文件是您可能想要了解的一些内容,以便了解整个父级情况:

您不需要通过this。分配父级的目的是为了简化QObject实例的清理过程(包括从QObject类继承的所有类)。 Qt父子模型遵循C ++标准,析构函数构造函数的相反顺序调用。简单来说,这意味着以下内容:

想象一下,您创建小部件A,B和C 。使用父属性,您将 B C 指定为 A 的子级。所以我们在这里有创造的顺序:

  1. A(父母)
  2. B,C(儿童)
  3. 现在在某些时候你想破坏你的小部件(例如:关闭应用程序)。标准的C ++方法是在下面(与构造相反)顺序销毁它们:

    1. B,C(儿童)
    2. A(父母)
    3. 如果我们先找到父母,那确实会发生这种情况。但是我们在这里处理Qt所以我们必须考虑库提供的另一个功能 - 插槽和信号。

      每当QObject被摧毁时,它就会发出一个信号。根据对象在父子关系中扮演的角色,结果是以下之一:

      • 只有被销毁的QObject被销毁 - 这是在我们销毁其父级之前儿童被销毁时发生的事情
      • QObject 的所有孩子首先被销毁,然后销毁 QObject 本身 - 这当父母被摧毁时会发生什么。发出的信号被所有孩子捕获,反过来也被消除了。

      但是,分配父母不是强制性的。您可以自己手动清理。阅读Qt documentation中有关父子模型的更多信息。

      除了所有所有权转移(小部件成为另一个小部件的子级)之外,通常会自动发生,因此不必明确指定小部件的父级。再次从Qt documentation中取出一个例外。如果您将小部件添加到布局,则所有权不会转移到布局本身,而是转移到它所属的QWidget

      最后有一个重要的案例,如果不指定父母会让事情变得非常非常不同。在这里,我将在QObject上引用Qt documentation

        

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

      因此,如果您有QWidget并且没有将其添加到某个布局(并间接添加到具有该布局的QWidget),例如它将自动成为一个单独的窗口。< / p>

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

      您为它动态分配内存

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

      否则正如我所说,你基本上是在创建对内存块的引用,并且在你离开构造函数之后,引用会因为超出范围而被销毁。

      编辑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();
      }
      

      <强> widget.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
      

      <强> widget.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;
      }
      

      <强> secondwidget.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
      

      <强> secondwidget.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 secondWidget 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 secondWidget-&gt; setParent(this)`以设置父节点。

      以下是检查父子层次结构是否正常的方法:

      • 对于您拥有的QObject QWidgetQLayout等都是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
        

        基本上遍历thisWidgetSecondWidget)的所有孩子,并显示其子女。在我的情况下,我得到了

            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:啊,我没有注意到你在调用QWidget(parent)构造函数中的SecondWidget。这也可以解决问题,因此您不需要setParent(parent)。我改变了我的第二个编辑。

答案 1 :(得分:1)

这通常发生在

  • 类已链接
  • 析构函数调用未被正确调用
  • Makefile需要进行一些更改。

  • 答案 2 :(得分:1)

    实际错误?

    1. 需要使用cmd提示符或终端

    2. 重新制作Makefile
    3. 确保析构函数不会调用自身,这样就改变了执行的补救措施。