当前位置: 首页 > 编程日记 > 正文

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

文章目录

      • weak_ptr描述
        • 声明
        • 作用
        • 原理实现
      • 函数成员使用
      • 总结

weak_ptr描述

声明

头文件:<memory>
模版类:template <class T> class weak_ptr
声明方式:std::weak_ptr<type_id> statement

作用

根据boost库的官方描述,weak_ptr是由shared_ptr管理的一种弱引用对象的模版类。weak_ptr的对象能够使用shared_ptr指针的构造函数转换为一个shared_ptr对象。但是这里shared_ptr的构造函数参数需要包含weak_ptr的lock成员,该成员是weak_ptr用来获取shared_ptr的指针。

这样当shared_ptr在多线程过程中被销毁时shared_ptr::reset,weak_ptr的lock成员仍然能够保留shared_ptr的成员,直到当前shared_ptr正常终止,否则会出现非常危险的内存泄漏。关于lock()成员的作用如下描述:
如下代码

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);// some time laterif(int * r = q.get()) 
{// use *r
}

多线程环境中shared_ptr是可以被多个线程共享,在r被使用之前p对象执行了p.reset(),正如我们上一篇文章中对shared_ptr的描述(C++智能指针:shared_ptr 实现详解),reset成员会重置当前shared_ptr指针的指向。此时,当前线程如果继续使用r指针,势必会产生访问空地址的异常问题

根据以上问题,使用weak_ptr::lock()成员来解决该问题

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);// some time later//使用weak_ptr的lock成员来获取shared_ptr的指针
if(shared_ptr<int> r = q.lock())
{// use *r
}

关于lock()成员简单说明一下,lock成员获取到的shared_ptr p指针创建一个临时对象(我们weak_ptr弱引用的体现),这个临时对象同样指向p,即使p执了reset这样的delete引用的操作,弱引用对象仍然持有改智能指针的地址,直到r指针的生命周期结束才会释放。不得不佩服C++语言设计者的脑洞,为了保持C++对内存操作的自由,即使耗费再大的精力也要实现这一目标,工匠精神才让今天的C++越来越被底层程序员喜欢。

原理实现

源码文件/boost/smart_ptr/weak_ptr.hpp

template<class T> class weak_ptr
{
private:// Borland 5.5.1 specific workaroundstypedef weak_ptr<T> this_type;
  • constructor 构造函数
    //默认构造函数
    weak_ptr() BOOST_NOEXCEPT : px(0), pn() // never throws in 1.30+
    {
    }//拷贝构造函数
    weak_ptr( weak_ptr const & r ) BOOST_NOEXCEPT : px( r.px ), pn( r.pn )
    {
    }
    
  • destructor析构函数,这里weak_ptr使用的是默认析构函数,一般使用expired返回空对象或者user_count()为0的情况则辅助shared_ptr释放引用
  • operator=
    1. weak_ptr & operator=( weak_ptr && r ) BOOST_NOEXCEPT
    {this_type( static_cast< weak_ptr && >( r ) ).swap( *this );return *this;
    }//2.这里会更加安全,使用lock成员获取Y类型的指针
    template<class Y>
    weak_ptr & operator=( weak_ptr<Y> const & r ) BOOST_NOEXCEPT
    {boost::detail::sp_assert_convertible< Y, T >();px = r.lock().get();pn = r.pn;return *this;
    }3.
    template<class Y>
    weak_ptr & operator=( shared_ptr<Y> const & r ) BOOST_NOEXCEPT
    {boost::detail::sp_assert_convertible< Y, T >();px = r.px;pn = r.pn;return *this;
    }
    
  • weak_ptr::swap成员,交换两个weak_ptr所指内容以及地址
     void swap(this_type & other) BOOST_NOEXCEPT
    {//先交换地址,再交换内容std::swap(px, other.px);pn.swap(other.pn);
    }
    
  • weak_ptr::reset成员,·重新指定对象地址和内容,就像是重新使用默认构造函数进行了初始化
    void reset() BOOST_NOEXCEPT // never throws in 1.30+
    {//使用默认构造函数构造的对象和当前对象进行swap操作this_type().swap(*this);
    }
    
  • weak_ptr::use_count成员,获取shared_ptr对象被引用的次数。如果为空,则返回0
    long use_count() const BOOST_NOEXCEPT
    {return pn.use_count();
    }
    
  • weak_ptr::expired成员,当根据use_count==0来返回bool,其返回为true的时候,使用lock获取weak_ptr的指针只能获取到空指针
     bool expired() const BOOST_NOEXCEPT
    {return pn.use_count() == 0;
    }
    
  • weak_ptr::lock成员,会向weak_ptr对象返回一个shared_ptr。正如我们之前在weak_ptr作用中所描述的,防止多线程访问时的shared_ptr内存泄漏。此时weak_ptr对象获取到的指针为临时指针,会指向shared_ptr对象之前所指向的地址。
    shared_ptr<T> lock() const BOOST_NOEXCEPT
    {return shared_ptr<T>( *this, boost::detail::sp_nothrow_tag() );
    }
    

函数成员使用

  • 构造函数
    #include <iostream>
    #include <memory>struct C {int* data;};int main () {std::shared_ptr<int> sp (new int);std::weak_ptr<int> wp1;std::weak_ptr<int> wp2 (wp1);std::weak_ptr<int> wp3 (sp);std::cout << "use_count:\n";//weak_ptr对象如果为经shared_ptr初始化,//它是没有引用计数的,所以这里wp1和wp2引用计数都为0//只有wp3经过了shared_ptr初始化,它的引用计数才为1std::cout << "wp1: " << wp1.use_count() << '\n';std::cout << "wp2: " << wp2.use_count() << '\n';std::cout << "wp3: " << wp3.use_count() << '\n';return 0;
    }
    
    输出如下:
    use_count:
    wp1: 0
    wp2: 0
    wp3: 1
    
  • weak_ptr::operator=赋值运算符
    // weak_ptr::operator= example
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> sp1,sp2;std::weak_ptr<int> wp;// sharing group:// --------------sp1 = std::make_shared<int> (10);    // sp1wp = sp1;                            // sp1, wpsp2 = wp.lock();                     // sp1, wp, sp2sp1.reset();                         //      wp, sp2//通过lock保留的临时指针,重新获取到了shared_ptr共享的地址sp1 = wp.lock();                     // sp1, wp, sp2std::cout << "*sp1: " << *sp1 << '\n';std::cout << "*sp2: " << *sp2 << '\n';return 0;
    }
    
    输出如下
    *sp1: 10
    *sp2: 10
    
  • std::weak_ptr::swap交换指针地址以及对应的内容
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> sp1 (new int(10));std::shared_ptr<int> sp2 (new int(20));std::weak_ptr<int> wp1(sp1);std::weak_ptr<int> wp2(sp2);std::cout << "wp1 -> " << *wp1.lock() << " " << wp1.lock() << '\n';std::cout << "wp2 -> " << *wp2.lock() << " " << wp2.lock() << '\n';//这里的swap仅仅是交换wp2的各自的指向地址//并不会直接导致对应智能指针原始指针的地址交换//根据输出,所以很明显,交换完成之后weak_ptr对象指向发生了变化,但是并未导致share_ptr指针的指向变化wp1.swap(wp2);std::cout << "sp1 -> " << *sp1 << " " << sp1 << '\n';std::cout << "sp2 -> " << *sp2 << " " << sp2 << '\n';std::cout << "wp1 -> " << *wp1.lock() << " " << wp1.lock() << '\n';std::cout << "wp2 -> " << *wp2.lock() << " " << wp2.lock() << '\n';return 0;
    }
    
    输出如下:
    wp1 -> 10 0x11daf90
    wp2 -> 20 0x11dafd0
    sp1 -> 10 0x11daf90
    sp2 -> 20 0x11dafd0
    wp1 -> 20 0x11dafd0
    wp2 -> 10 0x11daf90
    
  • std::weak_ptr::reset成员,执行之后weak_ptr对象就像是重新执行了默认构造函数,又变成了一个空的对象
    // weak_ptr::reset example
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> sp (new int(10));std::weak_ptr<int> wp(sp);std::cout << "1. wp " << (wp.expired()?"is":"is not") << " expired\n";std::cout << "4. wp " << wp.use_count() << " *wp " << *wp.lock() << '\n';wp.reset();std::cout << "2. wp " << (wp.expired()?"is":"is not") << " expired\n";std::cout << "3. sp " << sp.use_count() << " *sp " << *sp << '\n';return 0;
    }
    
    输出如下
    1. wp is not expired
    2. wp 2 *wp 10
    3. wp is expired
    4. sp 1 *sp 10
    

总结

shared_ptr和weak_ptr主要区别如下

  1. shared_ptr对象能够初始化实际指向一个地址内容而weak_ptr对象没办法直接初始化一个具体地址,它的对象需要由shared_ptr去初始化
  2. weak_ptr不会影响shared_ptr的引用计数,因为它是一个弱引用,只是一个临时引用指向shared_ptr。即使用shared_ptr对象初始化weak_ptr不会导致shared_ptr引用计数增加。依此特性可以解决shared_ptr的循环引用问题。
  3. weak_ptr没有解引用*和获取指针->运算符,它只能通过lock成员函数去获取对应的shared_ptr智能指针对象,从而获取对应的地址和内容。

参考文档:
http://www.cplusplus.com/reference/memory/weak_ptr/
https://www.boost.org/doc/libs/1_66_0/libs/smart_ptr/doc/html/smart_ptr.html#weak_ptr

相关文章:

在PHP中使用全局变量的几种方法

简介即使开发一个新的大型PHP程序&#xff0c;你也不可避免的要使用到全局数据&#xff0c;因为有些数据是需要用到你的代码的不同部分的。一些常见的全局数据有&#xff1a;程序设定类、数据库连接类、用户资料等等。有很多方法能够使这些数据成为全局数据&#xff0c;其中最常…

python处在哪个阶段_python 基础复习

1、简述cpu、内存、硬盘的作用cpu(1)cpu&#xff1a;处理逻辑运算、算术运算(2)cpu&#xff1a;接受指令传给电脑硬件&#xff0c;让其运行内存&#xff1a;(1)内存&#xff1a;从硬盘中读取数据&#xff0c;供其cpu调取指令运行&#xff0c;短暂的存贮数据&#xff1b;运行速度…

android用户界面之WebView教程实例汇总

一、WebView教程1.Android---UI篇---WebView&#xff08;网络视图&#xff09;http://www.apkbus.com/android-14259-1-1.html2.webview学习记录http://www.apkbus.com/android-44567-1-1.html3.Android中使用WebView, WebChromeClient和WebViewClient加载网页http://www.apkbu…

java下输出中文的一点研究

网上或者大部分书上都说Java中输出中文使用FileReader类就可以了&#xff0c;但是当你读取一个中文文档时&#xff0c;你会发现&#xff0c;除了乱码&#xff0c;还是乱码。究其原因&#xff0c;这其实是文件流读取时使用的编码方式和文件本身编码方式不同,造成读取出来文件乱码…

C++智能指针:unique_ptr详解

文章目录unique_ptr描述声明作用函数指针描述总结unique_ptr描述 声明 头文件&#xff1a;<memory> 模版类&#xff1a; 默认类型template <class T, class D default_delete<T>> class unique_ptr数组类型template <class T, class D> class uniq…

川崎机器人示教盒维修_专业维修丹阳市KUKA库卡KRC2库卡C4主板维修{苏州罗韦维修}...

发那科机器人故障分析 发那科伺服放大器上LED指示灯故障维修大全_发那科机器人维修&#xff0c;FANUC机器人保养&#xff0c;伺服电机示教器减速器维修&#xff0c;驱动器维修&#xff0c;苏州发那科机器人维修&#xff0c;本文主要介绍了发那科伺服放大器上因故障而出现的各种…

js 文本反向排列显示

一次面试遇到这样的题目 反向输出“how are you” 解决方法 <script language"JavaScript">var message1"how are you";var message2"";for (countmessage1.length; count > 0; count--)message2message1.substring(count,count-1);doc…

2012年12月4期手机网页开发

最近主要做手机上页面的开发&#xff0c;主要框架是&#xff0c;手机安装客户端&#xff0c;加载主站手机应用页面&#xff0c;手机客户端配合主站功能实现本地扫描或重力感应的效果。 针对安卓系统&#xff0c;在开发和调试时发现如下问题&#xff1a;1路径错误&#…

zabbix4.0构建实录

【Nginx】 #wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo [rootcentos ~]# yum -y install zlib pcre pcre-devel openssl openssl-devel[rootcentos ~]# useradd -s /sbin/nologin nginx [rootzabbix-server ~]# yum install -y nginx 【M…

ceph-kvstore-tool 工具使用详解

文章目录简介使用总结简介 ceph-kvstore-tool工具是用来获取存放在leveldb或者rocksdb数据库中的键值元数据。并且该工具能够对kvstore中的数据进行配置&#xff0c;就像是对离线后的osd操作osd map一样 使用该工具&#xff0c;需要安装ceph-test-12.2.1.06-0.el7.centos.x86_…

springboot 订单重复提交_Spring Boot (一) 校验表单重复提交

一、前言在某些情况下&#xff0c;由于网速慢&#xff0c;用户操作有误(连续点击两下提交按钮)&#xff0c;页面卡顿等原因&#xff0c;可能会出现表单数据重复提交造成数据库保存多条重复数据。存在如上问题可以交给前端解决&#xff0c;判断多长时间内不能再次点击保存按钮&a…

智能会议白板系统每日开发记录

智能会议白板系统&#xff0c;在开发过程中&#xff0c;整个项目期限内&#xff0c;每月&#xff0c;每周&#xff0c;每天要做的事情&#xff0c;作为组长的记录&#xff0c;多有不足之处&#xff0c;望指点。 转载于:https://www.cnblogs.com/mayijun/p/3458039.html

java.lang.OutOfMemoryError: PermGen space及其解决方法

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域OutOfMemoryError: PermGen space从表面上看就是内存益出&#xff0c;解决方法也一定是加大内存。说说为什么会内存益出&#xff1a;这一部分用于存放Class和Meta的信息,Class在被 Load的时候被放入Per…

ceph-dencoder工具使用详解

文章目录简介使用decode命令用法encode简介 ceph-dencoder工具是一个序列化编码、解码并且打印ceph数据结构的工具。它主要用来调试和测试ceph不同版本之间的兼容性问题 该工具是由 ceph-common-12.2.1.06-0.el7.centos.x86_64 rpm包生成 本文章是根据ceph-12.2.1版本来描述改…

EBS fnd_global.apps_initialize

原型&#xff1a;fnd_global.apps_initialize(user_ID, Responsibility_id,Responsibility_application_id);作用&#xff1a;在数据库的会话中设置全局变量&#xff0c;和用户概要信息。参数获得&#xff1a;参数一&#xff0c;用户ID select user_idfrom fnd_userwhere user_…

js判断鼠标靠近屏幕最侧面的监听_threejs按鼠标位置缩放场景

threejs的orbitcontrol&#xff0c;默认的缩放模式为整体以target为中心进行缩放。有时候&#xff0c;我们想让场景按照鼠标位置进行缩放&#xff0c;体验起来就和地图的缩放一样&#xff0c;最直观的感觉就是整个场景会越来越靠近鼠标点的位置&#xff0c;而不是整体的缩放大小…

hibernate中多对多分解成一对多,

1&#xff0c;参考&#xff1a;http://blog.csdn.net/yaerfeng/article/details/6969632

C++ 函数参数 值传递与引用传递

以前我们在C语言中函数参数传递过程中&#xff0c;如果我们想要让当A函数作用域中的变量经过B函数处理之后的数值仍然在A函数中生效&#xff0c;这个时候函数参数的传递时需要引用方式去传递&#xff0c;方式如下&#xff1a; #include <stdio.h> //函数参数为指针&…

SharePoint 2013 图文开发系列之代码定义列表

在SharePoint的开发中&#xff0c;用Visual Studio自定义列表是经常会用到的&#xff0c;因为很多时候&#xff0c;我们并不会手动创建列表&#xff0c;而手动创建列表在测试服务器和正式机之间同步字段&#xff0c;也很麻烦&#xff0c;所以我们经常用代码来定义列表或者文档库…

arduino下载库出错_【arduino】DIY音乐播放器,arduino播放wav音乐,TRMpcm库测试及使用...

微信关注 “DLGG创客DIY”设为“星标”&#xff0c;重磅干货&#xff0c;第一时间送达。arduino特点库超多&#xff0c;想必大家都领教了&#xff0c;今天来分享一下之前玩过的TRMpcm库。这个库是干嘛用的&#xff1f;简单粗暴用arduino(这里特指arduino官方那几个板子uno、nan…

vim替换技巧4

、 转自&#xff1a;http://www.confay.com/2008/03/vim4.html [技巧一] 第一个是在VIM邮件列表中看到的&#xff0c;给出了一个如何统计文章字数的方法。 统计一个完整文件的字数&#xff0c;可以使用Unix下的wc工具&#xff0c;它能够统计一个文件的行数、单词数和字符数。 如…

spark1.x和2.xIterable和iterator兼容问题

1. spark 1.x 升级到spark 2.x 对于普通的spark来说,变动不大 : 1 举一个最简单的实例:spark1.x public static JavaRDD<String> workJob(JavaRDD<String> spark1Rdd) {JavaPairRDD<String, Integer> testRdd spark1Rdd.flatMapToPair(new PairFlatMapFunct…

C++ 拷贝构造函数和重载赋值运算符的区别

文章目录拷贝构造函数重载赋值运算符赋值运算符和拷贝构造函数最大区别是赋值运算符没有新的对象生成&#xff0c;而拷贝构造函数会生成新的对象。 为了更加形象 准确得描述 赋值运算符和拷贝构造函数得区别&#xff0c;将详细通过代码展示两者之间得差异。 拷贝构造函数 首先…

单元格内多个姓名拆分成一列_EXCEL拆分单元格中的姓名,这都不叫事儿

作者&#xff1a;祝洪忠 转自&#xff1a;Excel之家ExcelHome小伙伴们好啊&#xff0c;今天老祝和大家来分享一个数据整理的技巧。下面的表格形式&#xff0c;想必大家不会陌生吧&#xff1a;在这个表格内&#xff0c;同一个部门的人员名单都挤到一个单元格内。现在问题来了&am…

3.1 A Historical Perspective 历史观点

1.从1978年的8086到现在的2008年core i7 ,从29K个晶体管到781M个晶体管&#xff0c;地址线&#xff08;也叫地址位长&#xff08;bit long))8086只有20个地址线&#xff0c;1982年&#xff0c;MS-windows 使用80286平台开发了自己的windows。直到1985年&#xff0c;i386正式扩展…

idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml,

第一种错误 &#xff1a;idea中 maven打包时时报错User setting file does not exist C:\Users\lenevo\.m2\setting.xml&#xff0c; 解决方案如下&#xff1a;将maven的安装目录\conf目录下的setting.xml拷贝到C:\Users\lenevo\.m2目录下即可。 第二种错误&#xff1a; This a…

关于部署osd过程中:Device is in use by a device-mapper mapping问题解决

ceph环境&#xff1a;12.2.1 使用古老的ceph-disk工具部署osd,仅仅prepare过程中就出现如上所示问题 Device is in use by a device-mapper mapping md127 解决方法如下&#xff1a; 由于device-mapper为系统自己的磁盘映射器&#xff0c;此时检查系统是否有逻辑卷 pvs lvs vg…

spyder一打开就卡了_欧姆龙plc 用 SD 卡上传/下载程序

以Nx102为例&#xff0c;NJ类似。使用 SD 卡将 Sysmac Studio 编写的程序传入 NX1P2 内&#xff1b; 使用 SD 卡对NX1P2 的程序进行备份&#xff0c; 查看备份的程序&#xff0c; 并把备份的程序传入另一台对应型号的 NX1P2 内。一、 使用 SD 卡将 Sysmac Studio 写的程序传入 …

Unity3D 中 2D_Toolkit插件下载 和 导入方法

Unity3D 中 2D_Toolkit插件下载 和 导入方法 1.你把下载来的包放到 安装目录&#xff1a;Editor\Standard Packages里面。 2.然后按ctrl9&#xff0c;进入asset store&#xff0c;等页面加载。 3.页面加载成功后&#xff0c;如果有账号&#xff0c;就登录&#xff0c;没有账号先…

Emacs 使用YASnippet

<?xml version"1.0" encoding"utf-8"?> Emacs 使用YASnippetUP | HOME Emacs 使用YASnippet Table of Contents 1 安装YASnippent2 安装 org-mode字典3 org-mode中使用教程4 YASnippet增加模板1 安装YASnippent $ cd ~/.emacs.d/plugins $ git c…