Qt完全遵循C++ 中类的实例化动作按存储位置可以分为栈中分配内存和堆中分配内存两种,分别对应不用 new 实例化类和用 new 实例化类。
一、实例化两种方式
1、栈中分配;
如下图是qt非常常见的操作,将m_view声明为对象,它完全表明该对象的生命周期与MyWidget的生命周期相关联。
此时m_view是在栈中分配内存的,使用完后你不需要再手动进行内存的释放,类的析构函数会自动执行内存释放的动作。此种方式优势时不需要new也更不需要指针。
//MyWidget.h
class MyWidget : public QWidget {Q_OBJECT
public:explicit MyWidget(QWidget *parent = nullptr);private:QTreeView m_view;;
};//MyWidget.cpp
MyWidget::MyWidget(QWidget *parent): QWidget(parent)
{
}
2、堆中分配
如下图,使用指向拥有对象的指针,将m_view声明为对象指针。使用 new 进行类的实例化,m_view就是指向类 QTreeView的指针,这个时候内存申请在堆上。
备注:此时其实与C++指针实例化稍微有些区别,你看到只有new却没有delete,传统C++认为堆上用完必须手动使用 delete 释放掉内存,因为 delete 和 new 是一对好伙伴,有 new 的地方就得有 delete,不然会造成内存泄漏。那为什么qt这里没有delete呢?
Qt引入了元对象做内存管理(qt特有),QObject的类及其继承的类,如果设置了parent,当parent被delete时,这个parent的相关所有child都会自动delete,不用用户手动处理,当所有者被删除时,所有从属于它的对象也将被删除。
在QObject的类及其继承的类下,Qt 代码通常仍会使用原始指针,并依赖父/子关系管理所有权,其实是在使用指针来访问对象而不是管理它们的生命周期。它极大的方便了使用指针,在好多人对待指针的看法中“如果可以避免,请不要使用它们”,而qt的这种内存管理方式使好多原则都被忽略了,你new了完全不用delete可以毫无顾虑的去使用。
//MyWidget.h
class MyWidget : public QWidget {Q_OBJECT
public:explicit MyWidget(QWidget *parent = nullptr);private:QTreeView *m_view;;};//MyWidget.cpp
MyWidget::MyWidget(QWidget *parent): QWidget(parent)
{// Children parented to `this`m_view= new QTreeView(this); }
二、指向还是不指向?
那么,到底这两张方式有什么区别,哪个更好?
1、使用指针会显着增加内存分配的数量;
2、指针允许用户在MyWidget的标头中转发声明指向的数据类型。这意味着MyWidget的用户不必包含定义QTreeView等的标头。这缩短了编译时间;
3、指针允许你建立“孙子”和更复杂的关系,而不仅仅是直接的孩子。孙子将被其他人(其父母)删除,而不是直接由MyWidget实例删除。如果孙子是MyWidget的子对象,这将意味着通过需要delete销毁一个子对象。
4、指针强制用户正确地为分配的对象设置父级。这在少数情况下会产生影响,例如,如果跨线程移动父对象。当使用完整的对象作为数据成员时,重要的是要记住通过父子关系来建立父/子关系。
所以,一般来说,使用指针似乎利大于弊,这就是为什么它们被使用 Qt 的开发人员广泛采用的原因。