从0到1学习Qt -- 内存管理与乱码问题
1. 内存管理问题引入
在上篇文章中,通过代码的方式来实现在对话框中输出Hello World。但是我们在构造函数 new 完一个对象之后,并没有进行析构那么不会造成资源泄露问题吗?答案是不会的,因为Qt中存在对象树,可以自动的对我们new出来的对象进行管理。
2. 对象树
Qt 中的对象树是一种自动化的对象管理机制。它通过父子关系将 QObject 及其子类的对象组织成一棵树状结构。当一个对象被指定为另一个对象的子对象时,它们之间就建立了父子关系。
2.1. 对象树机制的核心优势
对象树机制起作用的时间点是在构造函数和析构函数的时候。
在构造函数的时候我们传入一个指定类型的parent指针将当前对象挂载到对象树上并且指定父节点对象;在析构函数调用时会自动遍历以当前节点为根节点的树状结构将其子节点一下的对象析构。
2.2. 对象树机制的好处
简化内存管理:程序员无需跟踪每一个动态分配的子对象,只要保证父对象被正确销毁,其下的整个子树都会被自动清理。这是防止内存泄漏的强大工具。
方便查找对象:可以通过父对象找到其所有子对象。
QObject::findChild()
:查找一个指定名称和类型的子对象。
QObject::findChildren()
:查找所有指定名称和类型的子对象。实现事件传播:Qt 的事件系统可以利用对象树。例如,如果子对象没有处理某个事件(如鼠标点击),这个事件可能会传递给它的父对象,依此类推。
2.3. 对象树机制在使用的时候的注意事项
- 注意要在堆上开辟空间:在栈上构建的对象,会遵循C++规则出了作用域就自动销毁掉了。
- 一个对象在对象树上可以改变父节点对象:一个对象的父对象是可以动态改变的。使用
setParent()
方法可以将其从一个父对象移动到另一个父对象。原父对象会将其从子对象列表中移除,而新的父对象会将其加入。- 不要手动的delete掉对象:如果一个对象有父对象,你不应该手动调用
delete
来销毁它。因为当父对象析构时,它会再次尝试删除这个子对象,导致程序崩溃(重复释放)
2.4. 代码样例
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>int main(int argc, char *argv[])
{QApplication app(argc, argv);// 主窗口 - 对象树的根QWidget window;window.setWindowTitle("对象树示例");window.resize(300, 200);// 创建一个布局管理器,并设置给窗口QVBoxLayout *layout = new QVBoxLayout(&window);// 创建一个标签,父对象是 window(通过布局间接设置)QLabel *label = new QLabel("你好,对象树!");layout->addWidget(label); // addWidget() 会设置 label 的父对象为 window// 创建一个按钮,父对象是 window(通过布局间接设置)QPushButton *button = new QPushButton("退出");layout->addWidget(button);// 连接信号和槽QObject::connect(button, &QPushButton::clicked, &window, &QWidget::close);window.show();// 进入事件循环return app.exec();// 当 window.close() 被调用,app退出后,window 对象(栈上)析构。// 它会自动销毁其子对象:layout, label, button。// 我们不需要写任何 delete 语句!
}
3. 乱码问题的引入
查看以下代码我们在析构函数内部通过标准输出打印了中文字符“你好你好”,但是在应用程序输出中出现了乱码的情况,展示为什么呢?
4. 乱码情况出现的缘由和解决方式
4.1. 为什么会出现乱码的情况
当我们用记事本打开widget.cpp这个文件的时候看到了右下角的编码格式为utf-8
但是应用程序输出终端中采用的编码格式是GBK,两者的编码格式不一样导致了出现乱码的情况。
4.2. 解决方式
包含QDebug头文件调用qDebug()接口打印信息