文章目录

  • 前言
  • 使用环境
  • 测试过程
  • 现象分析
  • 总结
  • 测试源码

前言

weak_ptr这个指针天生一副“小弟”的模样,也是在C++11的时候引入的标准库,它的出现完全是为了弥补它老大shared_ptr天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr来说,新进老大shared_ptr可以说近乎完美,但是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptrweak_ptr一起引入了标准库,用来解决循环引用的问题。

weak_ptr本身也是一个模板类,但是不能直接用它来定义一个智能指针的对象,只能配合shared_ptr来使用,可以将shared_ptr的对象赋值给weak_ptr,并且这样并不会改变引用计数的值。查看weak_ptr的代码时发现,它主要有lockswapresetexpiredoperator=use_count几个函数,与shared_ptr相比多了lockexpired函数,但是却少了get函数,甚至连operator*operator->都没有,可用的函数数量少的可怜,下面通过一些例子来了解一下weak_ptr的具体用法。

使用环境

  1. VS2015 + Windows7(应该是C++11标准)
  2. 头文件#include <memory>
  3. 命名空间using namespace std;

测试过程

  1. weak_ptr解决shared_ptr循环引用的问题
    定义两个类,每个类中又包含一个指向对方类型的智能指针作为成员变量,然后创建对象,设置完成后查看引用计数后退出,看一下测试结果:
class CB;
class CA
{public:CA() { cout << "CA() called! " << endl; }~CA() { cout << "~CA() called! " << endl; }void set_ptr(shared_ptr<CB>& ptr) { m_ptr_b = ptr; }void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }void show() { cout << "this is class CA!" << endl; }
private:shared_ptr<CB> m_ptr_b;
};class CB
{public:CB() { cout << "CB() called! " << endl; }~CB() { cout << "~CB() called! " << endl; }void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }void show() { cout << "this is class CB!" << endl; }
private:shared_ptr<CA> m_ptr_a;
};void test_refer_to_each_other()
{shared_ptr<CA> ptr_a(new CA());shared_ptr<CB> ptr_b(new CB());cout << "a use count : " << ptr_a.use_count() << endl;cout << "b use count : " << ptr_b.use_count() << endl;ptr_a->set_ptr(ptr_b);ptr_b->set_ptr(ptr_a);cout << "a use count : " << ptr_a.use_count() << endl;cout << "b use count : " << ptr_b.use_count() << endl;
}

测试结果如下:

CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 2
b use count : 2

通过结果可以看到,最后CACB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_aptr_b时,只有①③两条引用,然后调用函数set_ptr后又增加了②④两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_aptr_b被销毁,也就是①③两条引用会被断开,但是②④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。

解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:

class CB
{public:CB() { cout << "CB() called! " << endl; }~CB() { cout << "~CB() called! " << endl; }void set_ptr(shared_ptr<CA>& ptr) { m_ptr_a = ptr; }void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }void show() { cout << "this is class CB!" << endl; }
private:weak_ptr<CA> m_ptr_a;
};

测试结果如下:

CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 1
b use count : 2
~CA() called!
~CB() called!

通过这次结果可以看到,CACB的对象都被正常的析构了,引用关系如下图所示,流程与上一例子相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_aptr_b被销毁,也就是①③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。

2. 测试weak_ptr对引用计数的影响
其实weak_ptr本身设计的很简单,就是为了辅助shared_ptr的,它本身不能直接定义指向原始指针的对象,只能指向shared_ptr对象,同时也不能将weak_ptr对象直接赋值给shared_ptr类型的变量,最重要的一点是赋值给它不会增加引用计数:

void test1()
{// 编译错误 // error C2665: “std::weak_ptr<CA>::weak_ptr”: 3 个重载中没有一个可以转换所有参数类型// weak_ptr<CA> ptr_1(new CA());shared_ptr<CA> ptr_1(new CA());cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 1shared_ptr<CA> ptr_2 = ptr_1;cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2weak_ptr<CA> wk_ptr = ptr_1;cout << "ptr_1 use count : " << ptr_1.use_count() << endl; // 输出:ptr_1 use count : 2cout << "ptr_2 use count : " << ptr_2.use_count() << endl; // 输出:ptr_1 use count : 2// 编译错误// error C2440 : “初始化”: 无法从“std::weak_ptr<CA>”转换为“std::shared_ptr<CA>”// shared_ptr<CA> ptr_3 = wk_ptr;
}
  1. 测试weak_ptr常用函数的用法
    weak_ptr中只有函数lockexpired两个函数比较重要,因为它本身不会增加引用计数,所以它指向的对象可能在它用的时候已经被释放了,所以在用之前需要使用expired函数来检测是否过期,然后使用lock函数来获取其对应的shared_ptr对象,然后进行后续操作:
void test2()
{shared_ptr<CA> ptr_a(new CA());     // 输出:CA() called!shared_ptr<CB> ptr_b(new CB());     // 输出:CB() called!cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1weak_ptr<CA> wk_ptr_a = ptr_a;weak_ptr<CB> wk_ptr_b = ptr_b;if (!wk_ptr_a.expired()){wk_ptr_a.lock()->show();        // 输出:this is class CA!}if (!wk_ptr_b.expired()){wk_ptr_b.lock()->show();        // 输出:this is class CB!}// 编译错误// 编译必须作用于相同的指针类型之间// wk_ptr_a.swap(wk_ptr_b);         // 调用交换函数wk_ptr_b.reset();                   // 将wk_ptr_b的指向清空if (wk_ptr_b.expired()){cout << "wk_ptr_b is invalid" << endl;  // 输出:wk_ptr_b is invalid 说明改指针已经无效}wk_ptr_b = ptr_b;if (!wk_ptr_b.expired()){wk_ptr_b.lock()->show();        // 输出:this is class CB! 调用赋值操作后,wk_ptr_b恢复有效}// 编译错误// 编译必须作用于相同的指针类型之间// wk_ptr_b = wk_ptr_a;// 最后输出的引用计数还是1,说明之前使用weak_ptr类型赋值,不会影响引用计数cout << "ptr_a use count : " << ptr_a.use_count() << endl; // 输出:ptr_a use count : 1cout << "ptr_b use count : " << ptr_b.use_count() << endl; // 输出:ptr_b use count : 1
}

现象分析

引用计数的出现,解决了对象独占的问题,但是也带来了循环引用的困扰,使用weak_ptr可以打破这种循环,当你理不清引用关系的时候,不妨采用文中画图的方式来理一理头绪,或许就会有眼前一亮的感觉。

总结

  1. weak_ptr虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。
  2. weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。
  3. weak_ptr设计之初就是为了服务于shared_ptr的,所以不增加引用计数就是它的核心功能。
  4. 由于不知道什么之后weak_ptr所指向的对象就会被析构掉,所以使用之前请先使用expired函数检测一下。

测试源码

示例传送门:weak_ptr用法

智能指针(三):weak_ptr浅析相关推荐

  1. 深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  2. 【C++11新特性】 C++11智能指针之weak_ptr

    http://blog.csdn.net/xiejingfa/article/details/50772571 原创作品,转载请标明:http://blog.csdn.net/Xiejingfa/ar ...

  3. c++11新特性_【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是 C++11 引入的三种智能指针中的:weak_ptr. 在学习 weak_ptr 之前最好对 shared_ptr 有所了解.如果你还不知道 shared_ptr 是何物,可以 ...

  4. C++智能指针:weak_ptr实现详解

    文章目录 weak_ptr描述 声明 作用 原理实现 函数成员使用 总结 weak_ptr描述 声明 头文件:<memory> 模版类:template <class T> c ...

  5. 深入学习c++--智能指针(三) unique_ptr

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...

  6. 智能指针(三):unique_ptr使用简介

    文章转自:http://blog.csdn.net/weiwenhp/article/details/8708281 版权归原作者! 我们知道auto_ptr通过复制构造或者通过=赋值后,原来的aut ...

  7. Boost智能指针——weak_ptr

    循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象.一个简单的例子如下: #include <string> #include <ios ...

  8. 【Boost】boost库中智能指针——weak_ptr

    循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象.一个简单的例子如下: #include <string> #include <ios ...

  9. C++11中的智能指针unique_ptr、shared_ptr和weak_ptr详解

    目录 1.引言 2.什么是智能指针? 3.在Visual Studio中查看智能指针的源码实现 4.独占式指针unique_ptr 4.1.查看unique_ptr的源码实现片段 4.2.为什么uni ...

  10. C++ malloc、智能指针、类型转换等(三)

    文章目录 malloc.calloc.realloc.alloca 智能指针 shared_ptr weak_ptr unique_ptr auto_ptr 强制类型转换 malloc.calloc. ...

最新文章

  1. win七系统如何卸载MySQL_win7系统卸载SQL2008R2数据库的详细教程
  2. Visual Studio 2013开发 mini-filter driver step by step 内核中使用线程(7)
  3. 安装 | MATLAB2020b软件及安装教程( WIN10 64位)及运行21个图像调试代码
  4. 关于health的原理
  5. 关于OC-省市区习题
  6. 软件构造学习笔记-第四周
  7. 记一次阿里面试题:都有哪些进程间通信方式?麻烦你不要再背了
  8. sql字符串函数_SQL字符串函数概述
  9. Scrapy框架学习(二)
  10. 9.高性能MySQL --- 操作系统和硬件优化
  11. 小程序发布提审被驳回,提示当前提审小程序代码包中地理位置相关接口wx.getLocation暂未开通
  12. rtmp播放器,使用videojs播放,稳定
  13. excel转word后表格超出页面_excel转word后表格显示不全
  14. 电脑如何开启卓越性能模式
  15. WinForm 去掉DataGridView最后一行的空白行,删除空白行
  16. Execution failed for task ':app:processDebugResources'. No slave process to process jobs, aborting
  17. 如何将pdf中一些特定页提取存储在另一个pdf中
  18. JAVA生成纯色背景图-自定义大小-自定义颜色
  19. pycharm python 依赖管理_怎么解决pycharm license Acti的方法_python
  20. CnOpenData地方留言文本数据简介

热门文章

  1. win7打开桌面计算机很慢,鼠标右键刷新桌面很慢怎么办?Win7右键刷新反应特别慢的解决方法...
  2. 在Centos 5.x或6.x上安装RHEL EPEL Repo
  3. 个人关于PAT的认识心得和体会
  4. c语言程序设计大赛 作品,撒花 | C语言程序设计大赛、数据结构大赛圆满成功!...
  5. 45、预制干粉灭火装置的设置要求
  6. 微信获取公众号二维码
  7. sql常用语句(mysql)
  8. 第六章 (数据库) 第3节 数据库高级
  9. removeclass 传入两个类_JS:操作样式表2 :用JS实现添加和删除一个类名的功能(addClass()和removeClass())...
  10. 开发一个App大概要多少钱?