C++11之智能指针weak_ptr
C++11标准虽然将 weak_ptr 定位为智能指针的一种,但该类型指针通常不单独使用(没有实际用处),只能和 shared_ptr 类型指针搭配使用。甚至于,我们可以将 weak_ptr 类型指针视为 shared_ptr 指针的一种辅助工具,借助 weak_ptr 类型指针, 我们可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。也就是说,weak_ptr 类型指针并不会影响所指堆内存空间的引用计数。除此之外,weak_ptr<T> 模板类中没有重载 * 和 -> 运算符,这也就意味着,weak_ptr 类型指针只能访问所指的堆内存,而无法修改它。
一、weak_ptr模板类提供的成员方法
#include <QCoreApplication> #include <memory> #include "smart_pointer.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); /* 成员方法 功 能 operator=() 重载 = 赋值运算符,是的 weak_ptr 指针可以直接被 weak_ptr 或者 shared_ptr 类型指针赋值。 swap(x) 其中 x 表示一个同类型的 weak_ptr 类型指针,该函数可以互换 2 个同类型 weak_ptr 指针的内容。 reset() 将当前 weak_ptr 指针置为空指针。 use_count() 查看指向和当前 weak_ptr 指针相同的 shared_ptr 指针的数量。 expired() 判断当前 weak_ptr 指针为否过期(指针为空,或者指向的堆内存已经被释放)。 lock() 如果当前 weak_ptr 已经过期,则该函数会返回一个空的 shared_ptr 指针;反之,该函数返回一个和当前 weak_ptr 指向相同的 shared_ptr 指针。 */ std::shared_ptr<SmartPointer> p = std::make_shared<SmartPointer>("test"); std::shared_ptr<SmartPointer> p1(p); /* 并不影响shared_ptr的引用计数 */ std::weak_ptr<SmartPointer> p2 = p1; qDebug() << "p1.use_count:" << p1.use_count(); if (p2.lock() != nullptr) { qDebug() << (p2.lock())->get_info(); } qDebug() << p2.expired(); p2.reset(); qDebug() << "p2.use_count:" << p2.use_count(); qDebug() << p2.expired(); if (p2.lock() == nullptr) { qDebug() << "weak_ptr 已经过期"; } return a.exec(); }
打印结果如下:
二、weak_ptr解决shared_ptr循环引用的问题
定义两个类,每个类中又包含一个指向对方类型的智能指针作为成员变量,然后创建对象,设置完成后查看引用计数后退出
class TestB; class TestA { public: TestA() { qDebug() << "---TestA()---"; } ~TestA() { qDebug() << "---~TestA()---";} void set_ptr(std::shared_ptr<TestB> &ptr) {m_ptr_b = ptr;} void a_use_count() { qDebug() << "a use count : " << m_ptr_b.use_count(); } void print_inro() { qDebug() << "this is class TestA!"; } private: std::shared_ptr<TestB> m_ptr_b; }; class TestB { public: TestB() { qDebug() << "---TestB()---"; } ~TestB() { qDebug() << "---~TestB()---";} void set_ptr(std::shared_ptr<TestA> &ptr) {m_ptr_a = ptr;} void a_use_count() { qDebug() << "a use count : " << m_ptr_a.use_count(); } void print_inro() { qDebug() << "this is class TestB!"; } private: std::shared_ptr<TestA> m_ptr_a; };
void test() { std::shared_ptr<TestA> ptr_a = std::make_shared<TestA>(); std::shared_ptr<TestB> ptr_b = std::make_shared<TestB>(); qDebug() << "a use count : " << ptr_a.use_count(); qDebug() << "b use count : " << ptr_b.use_count(); ptr_a->set_ptr(ptr_b); ptr_b->set_ptr(ptr_a); qDebug() << "a use count : " << ptr_a.use_count(); qDebug() << "b use count : " << ptr_b.use_count(); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); test(); return a.exec(); }
打印结果:
通过结果可以看到,最后TestA和TestB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a和ptr_b时,只有1、2两条引用,然后调用函数set_ptr后又增加了3、4两条引用,当test()这个函数返回时,对象ptr_a和ptr_b被销毁,也就是1、3两条引用会被断开,但是2、4两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。
解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr
对象,因为weak_ptr
不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将TestB
中的成员变量改为weak_ptr
对象,代码如下:
class TestB { public: TestB() { qDebug() << "---TestB()---"; } ~TestB() { qDebug() << "---~TestB()---";} void set_ptr(std::shared_ptr<TestA> &ptr) {m_ptr_a = ptr;} void a_use_count() { qDebug() << "a use count : " << m_ptr_a.use_count(); } void print_inro() { qDebug() << "this is class TestB!"; } private: std::weak_ptr<TestA> m_ptr_a; };
打印结果如下:
通过这次结果可以看到,TestA和TestB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是4这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说TestA的对象只有一个引用计数,而TestB的对象只有2个引用计数,当test()这个函数返回时,对象ptr_a和ptr_b被销毁,也就是1、3两条引用会被断开,此时TestA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致TestB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?