qt-C++笔记之父类窗口、父类控件、对象树的关系
code review!
参考笔记
1.qt-C++笔记之父类窗口、父类控件、对象树的关系
2.qt-C++笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理
3.qt-C++笔记之自定义类继承自 QObject
与 QWidget
及开发方式详解
一.第一次笔记
1. 父类窗口与父类控件是什么?
父类窗口(Parent Window)
和父类控件(Parent Widget)
指的是在Qt的对象树(Object Tree)中,某个窗口或控件的直接上级对象。具体来说:
- 父类窗口通常指的是一个顶层窗口(如
QMainWindow
、QDialog
),它可以包含多个子控件(widgets)。 - 父类控件是指一个控件(如按钮、标签等)的直接父级控件。通过父子关系,子控件会在父控件的坐标系统内进行定位和显示。
父子关系的作用包括:
- 内存管理:在Qt中,父对象会自动管理其子对象的生命周期。当父对象被销毁时,所有子对象也会被自动销毁,避免内存泄漏。
- 层次结构:通过父子关系,Qt能够正确地渲染和管理窗口及控件的显示层级。
例如
声明:MyObject(QObject *parent = nullptr);
定义:MyObject::MyObject(QObject *parent) : QObject(parent) {}
声明:MyWidget(QWidget *parent = nullptr);
定义:MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {}
在这里,MyObject
继承自QObject
,而MyWidget
继承自QWidget
。每个构造函数都接受一个指向父对象的指针,并通过初始化列表将其传递给基类构造函数。这意味着创建MyObject
或MyWidget
实例时,可以指定它们的父对象,从而建立父子关系。
2. 传入的 this
和传入的父类窗口
在Qt中,创建子对象时通常需要传递一个父指针,以建立父子关系。常见的做法包括:
传入
this
:- 当你在一个类的成员函数中创建子对象时,通常会传入
this
指针作为父对象。 - 例如,在一个窗口类中创建一个按钮:
QPushButton *button = new QPushButton("Click Me", this);
- 这里,
this
指向当前窗口对象,按钮将成为该窗口的子控件。
- 当你在一个类的成员函数中创建子对象时,通常会传入
传入具体的父类窗口:
- 有时需要将子对象附加到特定的父窗口,而不是当前对象。
- 例如,在一个对话框中创建一个控件,并将主窗口作为父对象:
QPushButton *button = new QPushButton("Click Me", mainWindow);
- 这样,按钮的生命周期将与
mainWindow
绑定,而不是当前对话框。
3. 跨层级的父子关系
跨层级的父子关系指的是子对象的父对象不在直接的层级结构中。例如:
- 一个顶层窗口(如
MainWindow
)包含一个中间层的控件(如QWidget
),该控件再包含其他子控件。 - 或者,一个对象的父对象在不同的逻辑层级中,如业务逻辑对象的父对象是一个UI对象。
示例:
// MainWindow 是顶层窗口
MainWindow *mainWindow = new MainWindow();
// 中间层控件
QWidget *centralWidget = new QWidget(mainWindow);
// 子控件
QPushButton *button = new QPushButton("Click Me", centralWidget);
在这个例子中,button
的父对象是centralWidget
,而centralWidget
的父对象是mainWindow
。这种多层级的父子关系有助于组织复杂的界面结构,并确保对象的生命周期由顶层对象统一管理。
4. 纯代码开发
a. 创建 MyObject
并传入 this
作为父对象
// MyClass.h
#include <QObject>
class MyObject : public QObject {
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr);
};
// MyClass.cpp
#include "MyClass.h"
MyObject::MyObject(QObject *parent) : QObject(parent) {}
// Usage in another class
#include "MyClass.h"
class AnotherClass : public QObject {
Q_OBJECT
public:
AnotherClass() {
MyObject *obj = new MyObject(this); // 'this' 是 AnotherClass 的实例
}
};
说明: 在 AnotherClass
中创建 MyObject
实例时,将 this
作为父对象传递,确保 MyObject
的生命周期与 AnotherClass
绑定。
b. 创建 MyWidget
并传入父窗口
// MyWidget.h
#include <QWidget>
class MyWidget : public QWidget {
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
};
// MyWidget.cpp
#include "MyWidget.h"
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {}
// Usage in MainWindow
#include "MyWidget.h"
#include <QMainWindow>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() {
MyWidget *widget = new MyWidget(this); // 'this' 是 MainWindow 的实例
setCentralWidget(widget);
}
};
说明: 在 MainWindow
中创建 MyWidget
实例时,将 this
作为父窗口传递,使 MyWidget
成为 MainWindow
的中央控件。
5. 使用 Qt Designer 拖拽控件开发:通过 Qt Designer 创建 UI,并在代码中设置父对象
假设你使用 Qt Designer 创建了一个 MainWindow
,并在其中放置了一个自定义控件 MyWidget
。
// mainwindow.ui
<!-- 在 Qt Designer 中拖拽一个 QWidget 并提升为 MyWidget -->
<widget class="QMainWindow" name="MainWindow">
<widget class="MyWidget" name="myWidget" />
</widget>
// mainwindow.h
#include <QMainWindow>
#include "MyWidget.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Qt Designer 已经自动将 MyWidget 的父对象设置为 MainWindow
}
MainWindow::~MainWindow() {
delete ui;
}
说明: 使用 Qt Designer 拖拽 MyWidget
到 MainWindow
,Designer 自动将 MyWidget
的父对象设置为 MainWindow
,无需手动传递父指针。
6. 传入 this
作为父对象:在自定义控件中创建子控件
// MyWidget.cpp
#include "MyWidget.h"
#include <QPushButton>
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {
QPushButton *button = new QPushButton("Click Me", this); // 'this' 是 MyWidget 的实例
button->setGeometry(10, 10, 100, 30);
}
说明: 在 MyWidget
的构造函数中创建一个 QPushButton
,并将 this
作为父对象,使按钮成为 MyWidget
的子控件。
7. 传入具体的父类窗口:从一个窗口创建子控件,并指定不同的父窗口
#include <QMainWindow>
#include "MyWidget.h"
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() {
QWidget *central = new QWidget(this);
setCentralWidget(central);
MyWidget *widget = new MyWidget(central); // 指定 central 为父对象
}
};
说明: MyWidget
的父对象被指定为 central
,而不是直接是 MainWindow
,使其生命周期与 central
相关联。
8. 跨层级的父子关系
a. 多层级父子关系示例
#include <QMainWindow>
#include "MyWidget.h"
#include "MyObject.h"
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() {
// 创建顶层控件
MyWidget *widget = new MyWidget(this);
setCentralWidget(widget);
// 在 MyWidget 中创建 MyObject
MyObject *obj = new MyObject(widget); // widget 作为 MyObject 的父对象
}
};
说明: 这里 MyObject
的父对象是 MyWidget
,而 MyWidget
的父对象是 MainWindow
。这种跨层级的父子关系确保了对象的生命周期由顶层对象统一管理。
b. 跨不同逻辑层级的父子关系
假设有一个业务逻辑类 BusinessLogic
继承自 MyObject
,需要将其父对象设置为 UI 层的 MainWindow
。
// BusinessLogic.h
#include "MyObject.h"
class BusinessLogic : public MyObject {
Q_OBJECT
public:
explicit BusinessLogic(QObject *parent = nullptr);
};
// BusinessLogic.cpp
#include "BusinessLogic.h"
BusinessLogic::BusinessLogic(QObject *parent) : MyObject(parent) {}
// Usage in MainWindow
#include "BusinessLogic.h"
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow() {
BusinessLogic *logic = new BusinessLogic(this); // 'this' 是 MainWindow
}
};
说明: BusinessLogic
的父对象是 MainWindow
,尽管它属于业务逻辑层,但通过这种方式可以确保其生命周期与 UI 层关联。
二.第二次笔记
示例一:头文件和源文件分离
// MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr); // 构造函数声明,默认 parent 为 nullptr
};
#endif // MYOBJECT_H
// MyObject.cpp
#include "MyObject.h"
MyObject::MyObject(QObject *parent) : QObject(parent) {} // 构造函数定义,初始化基类 QObject 并传递 parent
// MyWidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr); // 构造函数声明,默认 parent 为 nullptr
};
#endif // MYWIDGET_H
// MyWidget.cpp
#include "MyWidget.h"
MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {} // 构造函数定义,初始化基类 QWidget 并传递 parent
// main.cpp
#include <QApplication>
#include "MyObject.h"
#include "MyWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyObject *obj = new MyObject(); // 实例化 MyObject,没有指定父对象
MyObject *objWithParent = new MyObject(nullptr); // 实例化 MyObject,并指定 parent 为 nullptr(等同于不指定)
MyWidget *widget = new MyWidget(); // 实例化 MyWidget,没有指定父窗口
MyWidget *widgetWithParent = new MyWidget(nullptr); // 实例化 MyWidget,并指定 parent 为 nullptr(等同于不指定)
widget->show(); // 显示 MyWidget 窗口
return app.exec(); // 运行应用程序事件循环
}
示例二:一个完整的main.cpp
// main.cpp
// 这个程序演示了 Qt 中 QObject 和 QWidget 的父子关系
#include <QApplication>
#include <QObject>
#include <QWidget>
#include <QDebug>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr) : QObject(parent) { // 构造函数,默认 parent 为 nullptr
if (parent)
qDebug() << "MyObject 被创建,父对象为:" << parent;
else
qDebug() << "MyObject 被创建,没有父对象";
}
~MyObject() { qDebug() << "MyObject 被销毁"; } // 析构函数
};
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr) : QWidget(parent) { // 构造函数,默认 parent 为 nullptr
if (parent)
qDebug() << "MyWidget 被创建,父窗口为:" << parent;
else
qDebug() << "MyWidget 被创建,没有父窗口";
}
~MyWidget() { qDebug() << "MyWidget 被销毁"; } // 析构函数
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyObject *obj1 = new MyObject(); // 创建一个没有父对象的 MyObject 实例,obj1 没有父对象,需手动删除
MyObject *obj2 = new MyObject(obj1); // 创建一个指定父对象为 obj1 的 MyObject 实例
MyWidget *widget1 = new MyWidget(); // 创建一个没有父窗口的 MyWidget 实例
widget1->setWindowTitle("没有父窗口的 MyWidget"); // 设置窗口标题
widget1->resize(300, 200); // 调整窗口大小
widget1->show(); // 显示窗口
MyWidget *widget2 = new MyWidget(widget1); // 创建一个指定父窗口为 widget1 的 MyWidget 实例
widget2->setWindowTitle("widget1 的子窗口 MyWidget");
widget2->resize(200, 150);
widget2->show();
MyObject *obj3 = new MyObject(nullptr); // 创建一个没有父对象的 MyObject 实例,传递 nullptr,obj3 没有父对象,需手动删除
MyObject *obj4 = new MyObject(widget1); // 创建一个指定父对象为 widget1 的 MyObject 实例
MyWidget *widget3 = new MyWidget(nullptr); // 设置一个父窗口为 nullptr 的 MyWidget 实例
widget3->setWindowTitle("没有父窗口的另一个 MyWidget");
widget3->resize(250, 180);
widget3->show();
return app.exec(); // 运行应用程序事件循环
// 注意:obj1, obj3 没有父对象,程序退出时会自动销毁
// 在更复杂的应用中,应手动删除没有父对象的堆对象以避免内存泄漏
}
#include "main.moc"
示例三:入参为this指针的情况
项目结构
MyQtApp/
├── main.cpp
├── ParentWidget.h
├── ParentWidget.cpp
├── MyWidget.h
├── MyWidget.cpp
├── MyObject.h
├── MyObject.cpp
└── MyQtApp.pro
-
MyQtApp.pro
QT += widgets
CONFIG += c++11
SOURCES += main.cpp \
ParentWidget.cpp \
MyWidget.cpp \
MyObject.cpp
HEADERS += ParentWidget.h \
MyWidget.h \
MyObject.h
-
main.cpp
#include <QApplication>
#include "ParentWidget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
ParentWidget parentWidget;
parentWidget.setWindowTitle("ParentWidget 主窗口");
parentWidget.resize(400, 300);
parentWidget.show();
return app.exec();
}
-
ParentWidget.h
#ifndef PARENTWIDGET_H
#define PARENTWIDGET_H
#include <QWidget>
#include "MyObject.h"
#include "MyWidget.h"
class ParentWidget : public QWidget
{
Q_OBJECT
public:
explicit ParentWidget(QWidget *parent = nullptr);
};
#endif // PARENTWIDGET_H
-
ParentWidget.cpp
#include "ParentWidget.h"
#include <QDebug>
ParentWidget::ParentWidget(QWidget *parent) : QWidget(parent)
{
MyWidget *childWidget = new MyWidget(this);
childWidget->setWindowTitle("ParentWidget 的子窗口 MyWidget");
childWidget->resize(200, 150);
childWidget->show();
MyObject *childObject = new MyObject(this);
qDebug() << "创建了 MyObject:" << childObject;
}
-
MyWidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
};
#endif // MYWIDGET_H
-
MyWidget.cpp
#include "MyWidget.h"
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
// 初始化内容(可选)
}
-
MyObject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject
{
Q_OBJECT
public:
explicit MyObject(QObject *parent = nullptr);
};
#endif // MYOBJECT_H
-
MyObject.cpp
#include "MyObject.h"
MyObject::MyObject(QObject *parent) : QObject(parent)
{
// 初始化内容(可选)
}
预期效果
运行程序后,将显示一个标题为“ParentWidget 主窗口”的主窗口,同时弹出一个标题为“ParentWidget 的子窗口 MyWidget”的子窗口。控制台会输出创建 MyObject
的信息。