QT6之类实例化——对象指针和对象

Qt的内存管理遵循C++的栈和堆分配,无须手动delete的特性源于其元对象系统和parent-child机制。栈中分配的对象在生命周期结束时自动析构,而heap分配需用new,但Qt的QObject子类通过parent关联实现自动删除,降低了内存泄漏风险。使用指针虽增加内存分配但允许更复杂的对象关系和类型转发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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可以毫无顾虑的去使用。

parent-child 机制:当建立一个控件(widge或者其他控件)时,若此控件伴随着一个 parent,则此 parent 就将此控件加入它的 children list。而当parent 被消除时,会根据 children list 将这些 child 消除掉。若这些 child 也有其 children,也会连同一起被消除。这个机制大大简化了记忆体管理,降低了内存泄漏的风险。因此,唯有那些没有 parent 的控件才使用 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 的开发人员广泛采用的原因。

### Qt指针创建与普通变量的区别 在 Qt 编程中,指针普通变量的使用方式以及它们的影响有着显著的不同。以下是两者的主要区别: #### 1. **内存分配位置** 当在一个中定义成员变量为指针时,该指针所指向的对象通常是动态分配于堆中的[^1]。这意味着宿主对象(如 `MainWindow` 对象)仅保存了指向实际对象的地址,而非整个对象的内容。这种方式可以有效防止单个宿主对象变得过大。 相比之下,如果直接定义为普通变量,则这些变量会被嵌入到宿主对象内部,在栈上分配空间。随着普通变量数量增加,宿主对象可能会占用大量内存资源。 #### 2. **生命周期管理** 对于普通变量而言,其生命周期完全依赖于宿主对象的存在时间——一旦宿主对象销毁,所有嵌套在其内的普通变量也会随之释放。然而,这种机制缺乏灵活性,无法单独清理某个子对象的内存。 而采用指针的方式则提供了更大的自由度。开发者可以在不需要某些对象时手动调用删除操作来释放对应的堆内存。此外,为了简化这一过程并减少潜在的风险,Qt 提供了一系列智能指针工具,比如 `QSharedPointer` `QScopedPointer` 等[^2]。利用这些智能指针能够实现更安全、便捷的记忆体管理方案。 #### 3. **初始化时机** 普通成员变量会在构造函数执行之前完成默认初始化;而对于未显式赋初值的内置型来说,默认情况下可能得到不确定的结果。相反地,由于指针本质上只是一个存储地址的数据结构,因此即使不立即实例化它所关联的目标实体也不会引发任何问题[^4]。 另外值得注意的是,当我们传递参数给方法或者返回复杂型的对象时,拷贝成本较高且容易引起不必要的性能开销。此时运用指针不仅可以降低复制频率还能更好地控制共享状态下的同步访问等问题。 #### 示例代码展示两种情况差异 ```cpp // 使用普通变量的情况 class MyClass { public: LargeObject obj; }; // 使用指针的情况 #include <memory> class MySmartClass { public: std::shared_ptr<LargeObject> pObj; explicit MySmartClass(): pObj(std::make_shared<LargeObject>()) {} }; ``` 上述例子展示了如何分别通过常规手段以及借助C++标准库里的smart pointer来进行设计考量上的取舍平衡点所在之处。 --- ### 总结 综上所述,在Qt项目开发过程中合理选用合适的策略至关重要。一方面要考虑具体应用场景下对效率的要求程度;另一方面也要兼顾维护便利性长期可扩展性的需求。选择恰当的技术路线可以帮助构建更加健壮的应用程序架构体系[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闫有尽意无琼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值