文章目录

  • 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

C++智能指针:weak_ptr实现详解相关推荐

  1. C++智能指针: shared_ptr 实现详解

    文章目录 shared_ptr描述 声明 作用 原理实现 函数使用 关于shared_ptr循环引用问题 shared_ptr描述 声明 shared_ptr属于C++11特性中新加的一种智能指针,它 ...

  2. C++智能指针:unique_ptr详解

    文章目录 unique_ptr描述 声明 作用 函数指针描述 总结 unique_ptr描述 声明 头文件:<memory> 模版类: 默认类型template <class T, ...

  3. 【C++进阶】智能指针(万字详解)

  4. 数学建模——智能优化之遗传算法详解Python代码

    数学建模--智能优化之遗传算法详解Python代码 import numpy as np import matplotlib.pyplot as plt from matplotlib import ...

  5. this指针的用法详解

    C++中this指针的用法详解 2010-11-12 20:40:45 分类: C/C++ this指针的用处: 一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果.th ...

  6. c语言二级指针有什么作用,C语言中二级指针的实例详解

    C语言中二级指针的实例详解 C语言中二级指针的实例详解 用图说明 示例代码: #include int main(int argc, const char * argv[]) { // int a = ...

  7. 3.堆栈指针寄存器 SP 详解

    堆栈指针寄存器 SP 详解 堆栈是一种具有"后进先出"(LIFO---Last In First Out)特殊访问属性的存储结构.堆栈一般使用RAM 物理资源作为存储体,再加上LI ...

  8. 【C语言】小妹不懂指针和数组的关系?那就安排指针数组关系详解

    目录 前言 一.什么是数组 二.什么是指针 三.指针变量的大小 四.数组和指针的关系 五.指针变量的自增自减运算 六.两个参数确定一个数组 七.字符型指针和字符型数组 总结 写在最后 前言 前段时间整 ...

  9. C语言(函数指针数组)详解

    要了解函数指针数组,可以从三个角度来分析.所谓函数指针数组,从字面意思上来解析,函数指针数组的组成有三个点,函数,指针,数组.首先我们知道,函数指针数组,是一个数组,数组的每个元素是函数指针,也就是一 ...

最新文章

  1. 初级篇第六期:学习UITableView
  2. 使用JavaScript实现一个简单的编译器
  3. Java8新特性学习记录
  4. WordPress设计bug+WooCommerce漏洞导致网站存在被劫持风险
  5. rsync+inotify实现实时同步案例--转
  6. boost::graph模块实现DFS parenthesis的测试程序
  7. Mini 容器学习笔记6——组件的获取(应用)
  8. 压缩可以卸载吗_番禺街坊注意!微信发送高清大文件不压缩,网友:QQ可以卸载了?...
  9. 零基础学Java的朋友值得一看
  10. JS获取登录者IP和登录城市
  11. 关于ics lab8 performance中的smooth
  12. c#和c++互操作(平台调用相关)
  13. 线段树模板--单点更新+区间求和
  14. 双重差分模型能做固定效应吗_互助问答第213期:模型中的固定效应问题
  15. 一个有趣手绘风格的Python绘图库使用
  16. ctf-希尔伯特曲线隐写
  17. pdf文档怎么转换成word格式,pdf转word的方法
  18. SAP补提折旧-折旧的增加/减少
  19. 移动魔百和M302A-ZN-S905L2_蓝牙语音正常_线刷免费固件包
  20. [半监督学习] Tri-Training: Exploiting Unlabeled Data Using Three Classifiers

热门文章

  1. 【PL/SQL】--导出oracle单表数据--drp204
  2. [PHP]php基础练习题学习随笔
  3. linux进程间通信-XSI IPC
  4. python文本处理实例_Python 文件处理的简单示例
  5. 电脑护眼模式_看绿色护眼不管用!别再相信这些护眼方法了!想护眼记住这4点!...
  6. 如何判断第一位是1_如何快速判断1瓶红酒的价格,防止被坑?
  7. php mysql安装位置_安装php时,--with-mysql指定的是哪个路径呢?
  8. 福师2021计算机应用基础,2021福师《计算机应用基础》在线作业二【满分答案】...
  9. linux c 函数专挑,Linux C wait函数
  10. php和架构,结构和架构的区别是什么?