Qt 之 智能指针汇总

article/2025/9/19 12:54:45

来源
还有其他一些,做了一些汇总和测试,就不全列了。

文章目录:
一、垂悬指针的问题
二、Qt中的智能指针
       1、QPointer
       2、QSharedPointer & QWeakPointer
       3、QScopedPointer
       4、其他智能指针
三、实践记录
四、用法举例
       1、QWeakPointer举例
       2、QScopedPointer 与 std::unique_ptr 举例
       3、测试样例

代码中出现一个bug,最终发现是由于在某个特殊情况下出现了使用垂悬指针,造成了程序崩溃,进而学习了解了Qt的智能指针机制。

一、悬垂指针的问题

如图,有两个指针a和b指向同一片内存,如果删除其中一个指针a,再去使用指针b的话,程序会崩溃。因为指针b此时已经是一个垂悬指针(Dangling pointer)了,它指向的内存已经被释放不再有效。
在这里插入图片描述

垂悬指针使用指针b之前先判断b是否为空,这个做法在这里是不起作用的。问题的本质是通过指针a去释放内存时,指针b没有同步地置为空。
假如指针b能随内存的释放而自动置为空就好了,这正是智能指针所要解决的问题。

二、Qt中的智能指针

Qt提供了若干种智能指针:QPointer、QSharedPointer、QWeakPointer、QScopedPointer、QScopedArrayPointer、QSharedDataPointer、QExplicitlySharedDataPointer。
注:1、笔者Qt版本为4.8; 2、下述示例代码中"Plot"为"QObject"类的子类。

1、QPointer

QPointer只用于QObject的实例。如果它指向的对象被销毁,它将自动置空。
如图:
在这里插入图片描述

QPointer这是Qt体系下的专门用于QObject的智能指针。常见使用方法:

QPointer<Plot> a(new T());   	//构造
QPointer<Plot> a(b);		//构造
a.isNull();			//判空
a.data();			//返回裸指针

2、QSharedPointer & QWeakPointer

QSharedPointer是引用计数(强)指针,当所有强指针销毁时,实际对象才会销毁。QWeakPointer是弱指针,可以持有对QSharedPointer的弱引用。它作为一个观察者,不会引起实际对象销毁,当对象销毁时会自动置空。
这两种指针同时都有以下3个成员:强引用计数strongRef,弱引用计数weakRef和数据data。
Qt引用计数指针
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
QWeakPointer两种指针分别对应于C++中的std::shared_ptr和std::weak_ptr。常见使用方法://构造

QSharedPointer<Plot> a(new Plot()); 
QSharedPointer<Plot> b = a;
QWeakPointer<Plot> c = a;  //强指针构造弱指针
QWeakPointer<Plot> d(a);     

//使用

c.clear();	//清除
a.isNull();	//判空
a->func(...); 	//(按常规指针来使用 "->")
QSharedPointer<Plot> e = d.toStrongRef();   //弱指针转为强指针。注意,弱指针无法操纵数据,必须转为强指针
QWeakPointer<Plot> f = e.toWeakRef();//强指针显式转为弱指针
QSharedPointer<Plot> g = e.dynamicCast<T>();  //动态类型转换

3、QScopedPointer

QScopedPointer保证当当前范围消失时指向的对象将被删除。它拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能在本作用域里使用,不希望被转让,因为它的拷贝构造和赋值操作都是私有的。相当于C++中的std::unique_ptr,实例代码:

func(){Plot* plot = new Plot();//QScopedPointer出作用域自动销毁内存QScopedPointer<Plot>qsp(plot);//plot没有内存泄漏
}

4、其他智能指针

QScopedArrayPointer:一个QcopedPointer,默认删除它指向Delete []运算符的对象。为方便起见,还提供了操作符[]。QSharedDataPointer/QExplicitySharedDataPointer搭配QSharedData类一起使用,以实现自定义隐式共享或显式共享类。

三、实践记录

1、通常,要使用弱指针,必须将其转换为强指针,因为这样的操作确保了只要您使用它就会生存。这相当于“锁定”访问的对象,并且是使用弱指针指向的对象的唯一正确方法。并且转换后使用前需要判空。

QSharedPointer<Plot> qsp = qwp.toStrongRef();     //qwp是QWeakPointer
if(!qsp.isNull()){ 
qDebug() << qsp->getName(...);  //使用指向的对象//...
}

2、最好在new的时候就用QSharedPointer封装,并管理起来。

QSharedPointer<Plot> qsp = QSharedPointer(new Plot());

3、使用智能指针包装后,不要直接去删除指针对象。

Plot* plot = new Plot();
QSharedPointer<Plot> qsp1(plot);
delete plot;  //运行时会提示:"shared QObject was deleted directly. The program is malformed and may crash."

4、不要多次使用同一裸指针构造QSharedPointer。Plot *plot = new Plot();

QSharedPointer<Plot> qsp(plot);
QSharedPointer<Plot> qsp_ok = qsp;
QSharedPointer<Plot> qsp_error(plot);   //崩溃,输出: “pointer 0x1f0a8f0 already has reference counting”

5、不要使用new直接构造QWeakPointer对象,它只能通过QSharedPointer的赋值来创建。QWeakPointer<Plot> a(new Plot()); //error: 引用计数:强-1 弱2
6、由于智能指针对象是值语义,参数传递时尽可能用const引用兼顾效率
7、关于动态转换。使用QSharedPointer::dynamicCast()方法。
8、关于智能指针与QVariant转换,并关联到qobject的userdata。//注册到元对象

Q_DECLARE_METATYPE(QWeakPointer<Plot>) 				
//设置数据
item->setData(QVariant::fromValue(plot.toWeakRef()), Qt::UserRole);	
//取数据
QWeakPointer<Plot> plot = treeModel->data(index, Qt::UserRole).value<QWeakPointer<BasePlot> >();

9、关于Qt元对象系统自动析构和Qt智能指针自动析构相冲突的问题,经初步实验Qt4.8中应该已经解决了?不过实际中,可以让数据用智能指针管理,不用父子层级;窗体控件用父子层级,不用智能指针。

四、 用法举例

1、QWeakPointer举例

QWeakPointer不能用于直接取消引用指针,但它可用于验证指针是否已在另一个上下文中被删除。并且QWeakPointer对象只能通过QSharedPointer的赋值来创建。

需要注意的是,QWeakPointer不提供自动转换操作符来防止错误发生。即使QWeakPointer跟踪指针,也不应将其视为指针本身,因为它不能保证指向的对象保持有效。

说了那么多,QWeakPointer到底有什么用呢?
答案就是:解除循环引用。

在概述中我们说到,QWeakPointer 是为配合 QSharedPointer 而引入的一种智能指针。而什么叫循环引用,就是说:两个对象互相使用一个 QSharedPointer成员变量指向对方(你中有我,我中有你)。由于QSharedPointer是一个强引用的计数型指针,只有当引用数为0时,就会自动删除指针释放内存,但是如果循环引用,就会导致QSharedPointer指针的引用永远都不能为0,这时候就会导致内存无法释放。
所以QWeakPointer诞生了,它就是为了打破这种循环的。并且,在需要的时候变成QSharedPointer,在其他时候不干扰QSharedPointer的引用计数。它没有重载 * 和 -> 运算符,因此不可以直接通过 QWeakPointer 访问对象,典型的用法是通过 lock() 成员函数来获得 QSharedPointer,进而使用对象。

示例
首先,我们来看一个QSharedPointer循环使用的示例:

#include <QWidget>
#include <QDebug>
#include <QWeakPointer>
#include <QSharedPointer>class Children;
class Parent
{
public:~Parent(){qDebug() << __FUNCTION__;}QSharedPointer<Children> m_pChildren;
};
class Children
{
public:~Children(){qDebug() << __FUNCTION__;}QSharedPointer<Parent> m_pParent;
};class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = 0);~Widget();void test();
};
#include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent)
{test();
}Widget::~Widget()
{qDebug() << __FUNCTION__;
}void Widget::test()
{QSharedPointer<Parent> parent(new Parent());QSharedPointer<Children> children(new Children());if(parent && children){parent->m_pChildren = children;children->m_pParent = parent;}
}

在构造函数中调用test()函数,执行完过后应该会自动释放parent和children对象,但是由于相互引用,在退出之前,引用计数为2,退出之后引用计数还是1,所以导致不能自动释放,并且此时这两个对象再也无法访问到。运行过后发现并没有进入到Parent和Children的析构函数中。这就导致了内存泄漏。

那么该如何解决这个问题呢?
很简单,直接将上述代码中的

QSharedPointer<Children> m_pChildren;
QSharedPointer<Parent> m_pParent;

改成

QWeakPointer<Children> m_pChildren;
QWeakPointer<Parent> m_pParent;

这时候再次运行,就会看到输出:

~Children
~Parent

这是因为在test()退出之前引用计数是1,函数退出之后就自动析构,这就解除了上面的循环引用。

2、QScopedPointer 与 std::unique_ptr

它们概念上应该是是一样的。下面不再区分:

这是一个很类似auto_ptr的智能指针,它包装了new操作符在堆上分配的动态对象,能够保证动态创建的对象在任何时候都可以被正确地删除。但它的所有权更加严格,不能转让,一旦获取了对象的管理权,你就无法再从它那里取回来。

无论是QScopedPointer 还是 std::unique_ptr 都拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能在本作用域里使用,不希望被转让。因为它的拷贝构造和赋值操作都是私有的,这点我们可以对比QObject及其派生类的对象哈。

用法 (来自Qt的manual):

考虑没有智能指针的情况,void myFunction(bool useSubClass){MyClass *p = useSubClass ? new MyClass() : new MySubClass;QIODevice *device = handsOverOwnership();if (m_value > 3) {delete p;delete device;return;}try {process(device);}catch (...) {delete p;delete device;throw;}delete p;delete device;}

我们在异常处理语句中多次书写delete语句,稍有不慎就会导致资源泄露。采用智能指针后,我们就可以将这些异常处理语句简化了:

 void myFunction(bool useSubClass){QScopedPointer<MyClass> p(useSubClass ? new MyClass() : new MySubClass);QScopedPointer<QIODevice> device(handsOverOwnership());if (m_value > 3)return;process(device);}

另,我们一开始的例子,也是使用这两个指针的最佳场合了(出main函数作用域就将其指向的对象销毁)。

注意:因为拷贝构造和赋值操作私有的,它也具有auto_ptr同样的“缺陷”——不能用作容器的元素。

3、测试样例

#include <QCoreApplication>
#include <QSharedDataPointer>
#include <QSharedPointer>
#include <QWeakPointer>
#include <QTimer>
#include <QDebug>
int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);//raw pointer//QString *p = new QString("hello");QSharedPointer<QString> a(new QString("hello"));//QSharedPointer<QString> b = a;QWeakPointer<QString> c = a;  //强指针构造弱指针QWeakPointer<QString> d(a);//使用a.clear();//c.clear();	//清除if(!c.toStrongRef().isNull()){qDebug() << c.isNull() << c.toStrongRef()->length();}else {qDebug() << "is null";}qDebug() << a.isNull();	//判空qDebug() << a->length()<<*(a.data()); 	//(按常规指针来使用 "->")QSharedPointer<QString> e = d.toStrongRef();   //弱指针转为强指针。注意,弱指针无法操纵数据,必须转为强指针QWeakPointer<QString> f = e.toWeakRef();//强指针显式转为弱指针QSharedPointer<QString> g = e.dynamicCast<QString>();  //动态类型转换
//    QScopedPointer<QString> t2(p);
//    QScopedPointer<QString> t3(p);//    t3.reset();
//    qDebug() << t3.data();
//    qDebug() << *(t1.data());
//    qDebug() << *(t2.take()->data());//Implements non-reference-counted strong pointer
//    QScopedPointer<QString> pScopedPointer(new QString("Scoped"));
//    // Build error, can NOT be shared and reference-counted
//    //QScopedPointer<QString> pScopedPointerpScopedPointer2 = pScopedPointer;
//    //Implements reference-counted strong sharing of pointers
//    QSharedPointer<QString> pSmart(new QString("Smart"));
//    QSharedPointer<QString> pSmart2;
//    pSmart2 = QSharedPointer<QString>(new QString("smart 2"));
//    QSharedPointer<QString> pSharedPoninter;
//    // can be shared safely and reference-counted
//    pSharedPoninter = pSmart;
//    qDebug() << *(pSmart.data());
//    qDebug() << *(pSmart2.data());
//    qDebug() << *(pSharedPoninter.data());
//    qDebug() << pSharedPoninter.stro
//    QTimer *t = new QTimer;
//    QSharedPointer<QTimer> timer(t);
//    QWeakPointer<QTimer> pWeakPointer = timer;
//    pWeakPointer.data()->start(2000);
//    //Weak pointer's resources can be deleted from outside world
//    delete t;
//    qDebug() << timer.data();
//    if (pWeakPointer.isNull())
//    {
//        qDebug() << "contained QObject has been deleted";
//    }
}

http://chatgpt.dhexx.cn/article/cfwAcQ5T.shtml

相关文章

智能指针用法及其使用代码详解

网络上大多有关智能指针的解析只停留于简单的字面理解&#xff0c;今天来详细解析一下三种智能指针的用法以及具体的代码。 目录 概念 RAII机制介绍 智能指针雏形 shared_ptr原理介绍 shared_ptr使用方法 unique_ptr weak_ptr 概念 智能指针不是一个指针&#xff0c;它…

C++ 智能指针

shared_ptr 智能指针也是模板类&#xff0c;因此当我们创建一个智能指针是要提供额外的信息——指针可以指向的类型。默认初始化的智能指针保存着一个空指针。shared_ptr允许多个指针指向同一对象。 shared_ptr<string> p1; //可指向string shared_ptr<list<int&…

【C++】智能指针详解

今天我们来讲一下c中的智能指针。 目录 1. 智能指针初识1.1 什么是智能指针1.2 智能指针发展历史1.3 为什么需要智能指针 3. 智能指针原理3.1 RALL3.2 智能指针的分类3.2.1 auto_ptr3.2.2 unique_ptr3.2.3 shared_ptr3.2.3.1 shared_ptr 原理3.2.3.2 shared_ptr 的模拟实现3.2.…

智能指针详解

目录 前言 1、为什么需要智能指针&#xff1f; 2、智能指针的原理 3、智能指针的分类 3.1 auto_ptr 3.2 unique_ptr 3.3 shared_ptr 前言 C11中引入了智能指针的特性&#xff0c;本文将详细介绍智能指针的使用。 1、为什么需要智能指针&#xff1f; 我们来看一段代码&…

【c++复习笔记】——智能指针详细解析(智能指针的使用,原理分析)

&#x1f482; 个人主页:努力学习的少年&#x1f91f; 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 目录 一. 智能指针的基本概念 二. 智能指针的定义和使用 三. a…

C++ 智能指针 - 全部用法详解

为什么要学习智能指针&#xff1f; 咳咳&#xff0c;这个问题不是问大家的&#xff0c;是询问我自己的&#xff01; 我依稀记得刚离校出来找实习工作那会儿(2020年7月)&#xff0c;去面试一份工作&#xff0c;面试官问了我许多问题&#xff0c;其中有一个问题就是问&#xff1a…

AndroidStudio编译时 CXX1405 错误解决

AndroidStudio 编译带C的native库项目时报错&#xff0c; [CXX1405] error when building with cmake using D:\WorkSpace\AndroidXXXX\app\src\main\cpp\CMakeLists.txt: Build command failed.解决方法&#xff1a; cmd 到 Android SDK的cmake路径下&#xff0c;执行 cmake …

硬件速攻-AT24CXX存储器

AT24C02是什么&#xff1f; AT24CXX是存储芯片&#xff0c;驱动方式为IIC协议 实物图&#xff1f; 引脚介绍&#xff1f; A0 地址设置角 可连接高电平或低电平 A1 地址设置角 可连接高电平或低电平 A2 地址设置角 可连接高电平或低电平 1010是设备前四位固定地址 &#xf…

【dbus-cxx】libsigc++ 和 dbus-cxx 在 Ubuntu 中的编译和配置

文章目录 参考资料配置环境cmakelibsigcdbus-cxx 例程server.cppclient.cppMakefile运行结果 此帖作为自己学习记录使用&#xff0c;尚未使用交叉编译&#xff0c;仅在本地使用 参考资料 需先了解DBUS基础知识&#xff0c;建议看DBUS官方文档 DBUS-CXX也是建议看官方文档&…

正点原子IIC例程讲解笔记(三)——24cxx.c中函数理解

目录 一、24C02 简介 二、在 AT24CXX 指定地址写入一个数据&#xff1a; 三、在ATC24XX指定地址读出一个数据 四、检查AT24CXX是否正常&#xff1a;u8 AT24CXX_Check(void) 五、在 AT24CXX 里面的指定地址开始写入长度为 Len 的数据 六、在 AT24CXX 里面的指定地址开始读出…

windows用VS2019下编译log4cxx日志库

一、下载相关库文件 获取log4cxx源码包&#xff1a;http://logging.apache.org/log4cxx/index.html 获取依赖库apr和apr-util源码包:http://archive.apache.org/dist/apr/apr-1.2.11-win32-src.zip http://archive.apache.org/dist/apr/apr-util-1.2.10-win32-src.zip 编译apr…

log4cxx编译

本人进行过win7 64位操作系统和win10家庭版的log4cxx编译&#xff0c;使用的是vs2015&#xff0c;下面是详情。 1.sed下载 sed-4.2.1-bin.zip、sed-4.2.1-dep.zip下载地址&#xff1a;http://gnuwin32.sourceforge.net/packages/sed.htm 下载后&#xff0c;将sed的两个压缩包解…

【RT-Thread Master】at24cxx软件包使用笔记

硬件介绍 RT-Thread版本&#xff1a;V4.1.0软件包名称&#xff1a;at24cxxMCU型号&#xff1a;AT32F407VET7EEPROM型号&#xff1a;AT24C16 使用说明 1、使用menuconfig将软件包添加进入工程&#xff0c;路径如下所示。 2、把IIC总线打开&#xff0c;这里使用软件IIC&#…

linux下编译和安装log4cxx,ubuntu下log4cxx安装使用

需要安装log4cxx&#xff0c;安装的过程中可是充满了坎坷。。。最大的问题是在make log4cxx时&#xff0c;总是报undefined XML什么什么的错误&#xff0c;查了一下也没解决了&#xff0c;然后把apr-utils删了重新装了一下就好了。。 log4cxx现在是apache的一个项目&#xff0c…

linux下编译和安装log4cxx,RedHat如何安装log4cxx日志库

log4cxx日志库是一种动态库&#xff0c;用于记录c的日志&#xff0c;那么RedHat系统下要如何安装log4cxx日志库呢&#xff1f;下面小编就给大家介绍下RedHat安装log4cxx日志库的步骤&#xff0c;感兴趣的朋友不妨来了解下吧。 首先&#xff0c;我得到信息&#xff0c;安装这个库…

AT24Cxx读写全面理解

AT24Cxx - 电可擦可写E2PROM 芯片介绍 基础介绍\引脚介绍 AT24Cxx系列EEPROM是由美国Mcrochip公司出品&#xff0c;1-512K位的支持I2C总线数据传送协议的串行CMOS E2PROM&#xff0c;可用电擦除&#xff0c;可编程自定时写周期&#xff08;包括自动擦除时间不超过10ms&#…

mongodb-cxx-driver使用

mongocxx driver 是构建在 MongoDB C driver 之上的 1.首先需要安装mongo-c-driver wget https://github.com/mongodb/mongo-c-driver/releases/download/ 1.23.1/mongo-c-driver-1.23.1.tar.gz tar xzf mongo-c-driver-1.23.1.tar.gz cd mongo-c-driver-1.23.1 mkdir cmak…

老胡的周刊(第095期)

老胡的信息周刊[1]&#xff0c;记录这周我看到的有价值的信息&#xff0c;主要针对计算机领域&#xff0c;内容主题极大程度被我个人喜好主导。这个项目核心目的在于记录让自己有印象的信息做一个留存以及共享。 &#x1f3af; 项目 tabby[2] 自托管的 AI 编码助手&#xff0c;…

程序员养生指北

吴小胖第八次推送 阅读时间预计3分钟~ 熬夜篇 互联网人熬夜是不能避免的&#xff0c;原因却各不相同。 不加班的时候&#xff0c;总会对自己说&#xff0c;今天一定早睡&#xff0c;然鹅... 午休篇 熬夜的程序员总想依靠午休补觉&#xff0c;然鹅... 更不幸的是&#xff0c;互联…

老杨说运维 | 中国IT运维市场的现状与趋势

文章内容来源《第一新声》 对擎创科技CEO杨辰(老杨)的专访 前言&#xff1a; 中国目前正面临百年未有之大变局&#xff0c;在这个变局中&#xff0c;不稳定性和不确定性正在增强。疫情持续反复、国际形势变化多端&#xff0c;导致国内多个行业出现发展增速下降、产供销节奏打…