qt-C++笔记之自定义继承类初始化时涉及到parents的初始化
code review!
参考笔记
1.qt-C++笔记之父类窗口、父类控件、对象树的关系
2.qt-C++笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理
3.qt-C++笔记之自定义类继承自 QObject
与 QWidget
及开发方式详解
4.qt-C++笔记之自定义继承类初始化时涉及到parents的初始化
文章目录
- qt-C++笔记之自定义继承类初始化时涉及到parents的初始化
- 一.声明和实现在一起
- 1. 构造函数中传递 `parent`
- 2. 父类 `parent` 的作用
- 3. 子类中未显式初始化父类 `parent` 的情况
- 4. 动态设置父对象
- 5. 使用智能指针管理对象
- 6. 完整示例
- 二.声明和实现分开
- 1. 头文件(声明部分)
- 2. 源文件(实现部分)
- 3. 完整示例
- 头文件(`MyWidget.h`)
- 源文件(`MyWidget.cpp`)
- 主程序(`main.cpp`)
- 4. 关键点解析
- 1. 头文件中声明构造函数
- 2. 源文件中通过初始化列表传递 `parent`
- 3. 使用 Qt 的对象树
- 4. 在构造函数中初始化子对象
- 5. 运行效果
- 6. 总结
在 Qt 的 C++ 开发中,当我们创建一个类继承自 Qt 的某个类(比如
QObject
或者
QWidget
)时,通常需要在构造函数中对父类的
parent
指针进行初始化。这是 Qt 的对象树管理机制的核心部分之一。
Qt 中的对象树通过 parent
指针来自动管理对象的生命周期。当一个父对象被销毁时,它会自动销毁所有的子对象。因此,合理地设置 parent
是很重要的。
以下是如何在继承类中初始化父类的 parent
的一些说明和示例:
一.声明和实现在一起
1. 构造函数中传递 parent
当定义自己的继承类时,可以在构造函数中接受一个 parent
参数,并将其传递给父类的构造函数。
#include <QWidget>class MyWidget : public QWidget {
public:explicit MyWidget(QWidget *parent = nullptr) : QWidget(parent) // 调用父类的构造函数,初始化 parent{// 其他初始化代码}
};
在上面的代码中:
MyWidget
是从QWidget
继承的。- 构造函数接受一个
QWidget *parent
参数,并将其传递给QWidget
的构造函数来初始化父类的parent
。
2. 父类 parent
的作用
parent
参数的作用是将当前对象附加到指定的父对象上,从而形成一个 Qt 对象树。例如:
QWidget *mainWindow = new QWidget;
MyWidget *childWidget = new MyWidget(mainWindow); // 设置 mainWindow 为 parent
在这种情况下:
mainWindow
是父对象。childWidget
被添加为mainWindow
的子对象。- 当
mainWindow
被销毁时,childWidget
会被自动销毁。
3. 子类中未显式初始化父类 parent
的情况
如果子类没有显式初始化父类的 parent
参数,默认情况下,Qt 对象的父类指针会被设置为 nullptr
:
class MyWidget : public QWidget {
public:MyWidget() {// 未显式传递 parent,parent 默认为 nullptr}
};
这意味着:
- 对象不会自动附加到任何父对象。
- 必须手动管理该对象的生命周期。
4. 动态设置父对象
即使在构造函数中没有传递 parent
,也可以通过调用 setParent
方法在运行时动态设置父对象:
MyWidget *childWidget = new MyWidget;
childWidget->setParent(mainWindow); // 动态设置 parent
这种方法在需要在对象创建后再决定其父对象时非常有用。
5. 使用智能指针管理对象
如果你不想依赖 Qt 的对象树,也可以使用标准的 C++ 智能指针(如 std::unique_ptr
或 std::shared_ptr
)来管理对象的生命周期。但是,如果使用智能指针,就不要设置 parent
,以免 Qt 和智能指针同时试图管理对象的生命周期,导致潜在的问题。
6. 完整示例
以下是一个完整的例子,展示如何正确初始化和使用 parent
:
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>class MyWidget : public QWidget {
public:explicit MyWidget(QWidget *parent = nullptr): QWidget(parent) // 初始化父类的 parent{QVBoxLayout *layout = new QVBoxLayout(this);QPushButton *button = new QPushButton("Click Me", this);layout->addWidget(button);setLayout(layout);}
};int main(int argc, char *argv[]) {QApplication app(argc, argv);QWidget *mainWindow = new QWidget;MyWidget *childWidget = new MyWidget(mainWindow);mainWindow->resize(400, 300);mainWindow->show();return app.exec();
}
在这个例子中:
MyWidget
是一个自定义的QWidget
子类。MyWidget
的父对象是mainWindow
。- 当
mainWindow
被销毁时,MyWidget
也会被自动销毁。
二.声明和实现分开
当我们在 C++ 中将继承类的声明和实现分离时,涉及到 Qt 的 parent
初始化时,依然需要通过构造函数初始化列表在实现文件中将 parent
参数传递给父类的构造函数。以下是详细的分离步骤和示例。
1. 头文件(声明部分)
在头文件中声明类及构造函数,通常会为 parent
参数提供一个默认值(通常为 nullptr
),这样在使用时可以选择是否显式传递父对象。
#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>class MyWidget : public QWidget {Q_OBJECT // 如果使用信号和槽机制,必须添加 Q_OBJECT 宏public:// 构造函数声明,带 parent 参数,默认值为 nullptrexplicit MyWidget(QWidget *parent = nullptr);// 其他成员函数声明(如果有)
};#endif // MYWIDGET_H
2. 源文件(实现部分)
在源文件中实现构造函数时,使用初始化列表将 parent
参数传递给父类的构造函数。
#include "MyWidget.h"// 构造函数实现
MyWidget::MyWidget(QWidget *parent): QWidget(parent) // 将 parent 传递给 QWidget 的构造函数
{// 在这里编写其他初始化代码
}
3. 完整示例
以下是一个完整的例子,展示了如何分离声明和实现,同时正确初始化 parent
。
头文件(MyWidget.h
)
#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr); // 构造函数声明private:QVBoxLayout *layout; // 布局管理器QPushButton *button; // 按钮
};#endif // MYWIDGET_H
源文件(MyWidget.cpp
)
#include "MyWidget.h"// 构造函数实现
MyWidget::MyWidget(QWidget *parent): QWidget(parent) // 将 parent 传递给父类 QWidget 的构造函数
{// 初始化布局和按钮layout = new QVBoxLayout(this); // 将布局设置为当前 MyWidget 的子对象button = new QPushButton("Click Me", this); // 将按钮设置为当前 MyWidget 的子对象layout->addWidget(button); // 将按钮添加到布局中setLayout(layout); // 应用布局到当前 widget
}
主程序(main.cpp
)
#include <QApplication>
#include "MyWidget.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QWidget *mainWindow = new QWidget; // 创建主窗口MyWidget *childWidget = new MyWidget(mainWindow); // 将 mainWindow 作为 parentmainWindow->resize(400, 300);mainWindow->show(); // 显示主窗口return app.exec();
}
4. 关键点解析
1. 头文件中声明构造函数
explicit MyWidget(QWidget *parent = nullptr);
- 使用
explicit
关键字可以防止隐式类型转换。 parent
参数的默认值为nullptr
,这样在不需要父对象时可以直接创建孤立的对象。
2. 源文件中通过初始化列表传递 parent
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
QWidget(parent)
是父类构造函数的调用,它会将parent
初始化为当前对象的父对象。- 这是遵循 C++ 的构造函数初始化列表的标准写法。
3. 使用 Qt 的对象树
通过传递 parent
,可以让 MyWidget
成为其父对象的一部分,Qt 的对象树将自动管理子对象的生命周期。例如:
- 如果
MyWidget
的父对象是mainWindow
,销毁mainWindow
会自动销毁其所有子对象,包括MyWidget
。
4. 在构造函数中初始化子对象
在 MyWidget
的构造函数中,布局和按钮都通过 new
创建,并将当前对象(this
)作为它们的父对象:
layout = new QVBoxLayout(this); // 布局的父对象是 MyWidget
button = new QPushButton("Click Me", this); // 按钮的父对象是 MyWidget
这确保了这些子对象会被 MyWidget
自动管理,无需手动释放。
5. 运行效果
运行上述程序后:
mainWindow
是主窗口。MyWidget
是主窗口的子对象。- 当你关闭
mainWindow
时,MyWidget
会被自动销毁。 - 按钮和布局也会被自动销毁,因为它们是
MyWidget
的子对象。
6. 总结
- 在继承类中,通常需要在构造函数中通过调用父类的构造函数来初始化
parent
。 - 合理设置
parent
可以让 Qt 对象树自动管理对象的生命周期。 - 如果不使用
parent
,需要手动管理对象的生命周期。 - 动态设置
parent
或结合智能指针管理对象是可行的,但需要小心避免冲突。 - 在声明与实现分离时,
parent
的初始化通过构造函数的初始化列表实现。 - 在头文件中声明构造函数时,可以为
parent
提供默认值nullptr
。 - 在源文件中通过
: QWidget(parent)
调用父类构造函数进行初始化。 - 合理使用 Qt 的对象树机制,可以自动管理对象的生命周期,简化内存管理。