Qt 智能指针

embedded/2025/1/12 5:37:12/

Qt 智能指针

文章目录

  • Qt 智能指针
    • `QScopedPointer`
      • 1. 自动删除对象
      • 2. 转移所有权
      • 3. 管理私有数据
    • `QSharedPointer`
      • 关键特性
      • 注意事项
    • `QWeakPointer`
      • 注意事项
    • `QPointer`

QScopedPointer

QScopedPointer 是 Qt 提供的一个智能指针,主要用于简化资源管理,防止内存泄漏和悬挂指针问题。它属于 Qt 的内存管理工具,能够自动处理对象的生命周期,确保对象在超出作用域时被销毁。QScopedPointer 是基于 C++11 标准中的 std::unique_ptr 实现的,但它具有 Qt 的特点,通常用于局部对象的管理。

  • 自动删除对象:当 QScopedPointer 超出作用域时,它会自动释放所持有的对象。这意味着无需手动 delete 对象。
  • 不能复制:QScopedPointer 不支持复制操作,防止发生意外的多个指针指向同一个对象的问题。
  • 所有权转移:可以使用 reset() 或通过构造函数将 QScopedPointer 的所有权转移给另一个 QScopedPointer

1. 自动删除对象

QScopedPointer 最常见的用法是在函数或局部作用域内管理动态分配的对象。在作用域结束时,QScopedPointer 自动销毁对象,无需显式调用 delete

#include <QScopedPointer>
#include <QDebug>class MyClass {
public:MyClass() { qDebug() << "MyClass constructed"; }~MyClass() { qDebug() << "MyClass destructed"; }
};void testScopedPointer() {QScopedPointer<MyClass> ptr(new MyClass);// 当函数返回时,ptr 超出作用域,对象会被自动销毁
} // 在这里,MyClass 对象会被自动删除

2. 转移所有权

QScopedPointer 不支持复制操作,但可以通过 reset() 或构造函数转移所有权。这样,QScopedPointer 可以在不同的作用域之间传递资源。

#include <QScopedPointer>
#include <QDebug>class MyClass {
public:MyClass() { qDebug() << "MyClass constructed"; }~MyClass() { qDebug() << "MyClass destructed"; }
};void transferOwnership() {QScopedPointer<MyClass> ptr1(new MyClass);// 将所有权从 ptr1 转移到 ptr2QScopedPointer<MyClass> ptr2(ptr1.take());// 现在 ptr1 不再拥有 MyClass 对象,ptr2 拥有它// ptr1 不再指向对象,但对象仍然存在,由 ptr2 管理
} // 在这里,ptr2 超出作用域时,MyClass 对象会被自动删除

3. 管理私有数据

在 Qt 的许多类中,私有数据(通常是一个包含实现细节的类)被封装在一个 QScopedPointer 中。这样可以确保私有数据在类的析构函数中自动释放,同时保持代码的简洁性和安全性。

示例:QFile

class QFilePrivate : public QIODevicePrivate {// 私有数据成员
};class QFile : public QIODevice {
public:QFile();~QFile();private:QScopedPointer<QFilePrivate> d_ptr;
};

在这个例子中,QFile 类使用 QScopedPointer 来管理 QFilePrivate 对象。当 QFile 对象析构时,QScopedPointer 会自动删除 QFilePrivate 对象,确保内存被释放。

QSharedPointer

QSharedPointer 是通过引用计数来管理对象的生命周期的,多个 QSharedPointer 对象可以共享同一个资源。每当 QSharedPointer 的拷贝构造或赋值操作发生时,引用计数会增加,而当一个 QSharedPointer 被销毁时,引用计数会减少。当引用计数降到 0 时,所指向的对象会自动被删除。

#include <QSharedPointer>
#include <QDebug>class MyClass {
public:void print() { qDebug() << "Hello from MyClass!"; }
};int main() {// 创建 QSharedPointer 对象,管理 MyClass 对象的生命周期QSharedPointer<MyClass> ptr1(new MyClass);// 创建另外一个 QSharedPointer,并共享 ptr1 所管理的对象QSharedPointer<MyClass> ptr2 = ptr1;// 使用 ptr1 和 ptr2 都能访问同一个对象ptr1->print();ptr2->print();// 不需要手动释放内存,当最后一个 QSharedPointer 被销毁时,MyClass 对象会自动删除return 0;
}

关键特性

  • 引用计数:QSharedPointer 通过引用计数来管理对象的生命周期。每当有新的 QSharedPointer 对象指向相同的资源时,引用计数会增加;当某个 QSharedPointer 对象销毁时,引用计数会减少。
  • 自动销毁:当最后一个引用计数为 1 的 QSharedPointer 被销毁时,指向的对象会被自动删除,从而避免了内存泄漏。
  • 线程安全:QSharedPointer 的引用计数操作是线程安全的,但它本身并不保证被指向的对象本身是线程安全的。如果多个线程访问同一个 QSharedPointer 对象,必须确保其他线程同步访问该对象。

注意事项

  1. QSharedPointer 的引用计数机制在某些情况下可能导致循环引用问题,特别是当两个或更多的对象相互持有对方的 QSharedPointer 时。此时,即使这些对象不再使用,引用计数也不会降到零,因为它们互相引用,导致对象无法被销毁,从而产生内存泄漏。
    1. 解决方法:使用 QWeakPointer 来打破循环引用。QWeakPointer 是一种弱引用,持有一个 QSharedPointer 对象,但它不会增加引用计数。当 QSharedPointer 被销毁时,QWeakPointer 自动变为空指针。
  2. 不要混用裸指针和 QSharedPointer``QSharedPointer 需要确保它是唯一的内存管理者。如果你在程序中同时使用裸指针和 QSharedPointer 管理相同的内存,可能会导致双重释放或内存泄漏。因此,避免裸指针与智能指针共享同一资源,确保对象始终由智能指针管理。

QWeakPointer

QWeakPointerQSharedPointer 的一种补充,它本身不拥有对象的所有权。QWeakPointer 仅在 QSharedPointer 的引用计数为非零时提供访问该对象的能力,但不会阻止对象的销毁。换句话说,QWeakPointer 允许你引用一个对象而不会使得该对象无法销毁。

QWeakPointer 的主要特点:

  • 弱引用:QWeakPointer 不增加对象的引用计数,也就是说它不会阻止对象的销毁。
  • 防止循环引用:QWeakPointer 解决了 QSharedPointer 可能导致的循环引用问题。
  • 安全的访问方式:QWeakPointer 可以通过 toStrongRef() 方法转换为 QSharedPointer,从而安全地访问目标对象。

QWeakPointerQSharedPointer 的配合

QWeakPointer 通常与 QSharedPointer 一起使用,用于避免循环引用。在有些情况下,两个对象会互相引用,导致它们的引用计数始终不为零,进而导致内存泄漏。QWeakPointer 可以打破这个循环引用链,它允许对象 A 持有对象 B 的 QWeakPointer,而对象 B 可以持有对象 A 的 QSharedPointer,从而确保对象 A 和 B 的生命周期由 QSharedPointer 管理。

QWeakPointer 的常见用法

下面是一个使用 QWeakPointer 的具体示例:

class B;  // Forward declarationclass A {
public:QSharedPointer<B> b;  // B的共享指针
};class B {
public:QWeakPointer<A> a;  // A的弱引用
};int main() {QSharedPointer<A> a(new A);  // 创建A对象QSharedPointer<B> b(new B);  // 创建B对象a->b = b;  // A持有B的共享指针b->a = a;  // B持有A的弱引用return 0;  // 程序退出时,A和B会被自动销毁,避免内存泄漏
}

注意事项

使用QWeakPointer时候,一定要使用isNULL判断一下 资源是否释放

QSharedPointer<MyClass> shared(new MyClass(20));
QWeakPointer<MyClass> weak(shared);qDebug() << "Shared pointer value:" << shared->getValue();
qDebug() << "Weak pointer value:" << weak.data()->getValue();shared.clear(); // 删除 shared 指向的对象
// 此时,MyClass 对象的引用计数为 0,将被自动删除,而此时 QWeakPointer 对象 weak 也为 null。if (weak.isNull()) {  // 判断 weak 是否为 nullqDebug() << "Weak pointer is null - object has been deleted"; // 执行
}
else {qDebug() << "Weak pointer is not null - object still exists";
}

QPointer

QPointer 是一个用于指向 Qt 对象(例如 QObject 的子类)的模板类,它会自动管理对象的生命周期。当一个 QObject 被销毁时,QPointer 会将其指针设为 nullptr,这使得程序能够检测到所指向的对象已经被删除,从而避免访问已删除的对象,避免悬空指针问题。QPointer 只能用来管理 QObject 或其子类的对象。如果你需要管理其他类型的对象,可以考虑使用其他智能指针,如 std::shared_ptrstd::unique_ptr

#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>
#include <QDebug>int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);QWidget window;QVBoxLayout *layout = new QVBoxLayout(&window);QPushButton *button = new QPushButton("Click me");QPointer<QPushButton> pButton(button);layout->addWidget(button);window.show();QObject::connect(button, &QPushButton::clicked, [&] {if (pButton) {qDebug() << "Button exists, text:" << pButton->text();} else {qDebug() << "Button has been deleted";}});// 模拟按钮删除QObject::connect(button, &QPushButton::clicked, [&] {delete button;});return a.exec();
}

http://www.ppmy.cn/embedded/153219.html

相关文章

ETL的工作原理

ETL的工作原理 什么是ETL_云计算主题库-阿里云 ETL的工作原理可以分为三个主要的步骤&#xff1a;Extract&#xff08;提取&#xff09;、Transform&#xff08;转换&#xff09;、Load&#xff08;加载&#xff09;。 工作步骤 描述 Extract &#xff08;提取&#xff09;…

doris:手动分区

分区列​ 分区列可以指定一列或多列&#xff0c;分区列必须为 KEY 列。PARTITION 列默认必须为 NOT NULL 列&#xff0c;如果需要使用 NULL 列&#xff0c;应设置 session variable allow_partition_column_nullable true。对于 LIST PARTITION&#xff0c;支持真正的 NULL 分…

Table-Augmented Generation(TAG):Text2SQL与RAG的升级与超越

当下AI与数据库的融合已成为推动数据管理和分析领域发展的重要力量。传统的数据库查询方式&#xff0c;如结构化查询语言&#xff08;SQL&#xff09;&#xff0c;要求用户具备专业的数据库知识&#xff0c;这无疑限制了非专业人士对数据的访问和利用。为了打破这一壁垒&#x…

学习虚幻C++开发日志——创建Selection Widget及其应用

教程视频&#xff1a;脚本冒险 - YouTube 前提&#xff1a;此代码运用到Common UI插件&#xff0c;需将其开启&#xff0c;以免后序编写产生未定义结构体的报错信息&#xff01; 用C进行UI绑定 创建继承于CommonUserWidget的类&#xff0c;此处命名为SelectionBase Select…

【C++开源库】tinyxml2解析库使用介绍

TinyXML-2是一个在C中使用的轻量级、简单且高效的XML解析库。它由Lee Thomason开发&#xff0c;旨在提供快速解析和生成XML数据的功能&#xff0c;同时保持代码的简洁性和易于使用。TinyXML-2支持多种编译器和平台&#xff0c;包括Windows、Linux和macOS。 特点与优势 简单易用…

VUE3 VITE项目在 npm 中,关于 Vue 的常用命令有一些基础命令

如果你正在使用 Vite 构建的 Vue 3 项目&#xff0c;并且想要使用相关的 Vue 和 Vite 工具&#xff0c;下面是一些常用的命令和步骤来创建和管理 Vue 项目。 1. 使用 npm create 创建 Vue 3 项目&#xff08;Vite&#xff09; 如果你还没有创建项目&#xff0c;可以使用以下命…

UE5 打包要点

------------------------- 1、需要环境 win sdk &#xff0c;大约3G VS&#xff0c;大约10G 不安装就无法打包&#xff0c;就是这么简单。 ----------------------- 2、打包设置 编译类型&#xff0c;开发、调试、发行 项目设置-地图和模式&#xff0c;默认地图 项目…

小程序相关

1.右侧胶囊宽度&#xff0c;胶囊和文本重合问题 // #ifdef MP-WEIXIN // 获取胶囊左边界坐标 const { left } uni.getMenuButtonBoundingClientRect() this.rightSafeArea left px // #endif//给到你的内容宽度 <view :style"{max-width:rightSafeArea}"> …