1. weak_ptr 介绍

std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性("弱")引用。在访问所引用的对象指针前必须先转换为 std::shared_ptr。 主要用来表示临时所有权,当某个对象存在时才需要被访问。转换为shared_ptr的过程等于对象的shared_ptr 的引用计数加一,因此如果你使用weak_ptr获得所有权的过程中,原来的shared_ptr被销毁,则该对象的生命期会被延长至这个临时的 std::shared_ptr 被销毁为止。 weak_ptr还可以避免 std::shared_ptr 的循环引用。

std::weak_ptr简单使用:(编译系统:Linux centos 7.0 x86_64 编译器:gcc 4.8.5 )

  1. #include <memory>
  2. #include <iostream>
  3. class foo
  4. {
  5. public:
  6. foo()
  7. {
  8. std::cout << "foo construct.." << std::endl;
  9. }
  10. void method()
  11. {
  12. std::cout << "welcome Test foo.." << std::endl;
  13. }
  14. ~foo()
  15. {
  16. std::cout << "foo destruct.." << std::endl;
  17. }
  18. };
  19. int main()
  20. {
  21. // weak_ptr
  22. foo* foo2 = new foo();
  23. // share_ptr 管理对象
  24. std::shared_ptr<foo> shptr_foo2(foo2);
  25. // weak_ptr 弱引用
  26. std::weak_ptr<foo> weak_foo2(shptr_foo2);
  27. // 如果要获取数据指针,需要通过lock接口获取
  28. weak_foo2.lock()->method();
  29. std::shared_ptr<foo> tmp = weak_foo2.lock();
  30. // 我们这边有尝试多次获取所有权(lock),看一下引用计数个数
  31. std::cout << "shptr_foo2 RefCount: " << weak_foo2.lock().use_count() << std::endl;
  32. return 0;
  33. }

执行结果:

```
bash-4.2$ ./share_ptr
foo construct..
welcome Test foo..
shptr_foo2 RefCount: 3
foo destruct..


  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">我们可以看到,weak_ptr多次通过lock转换成shared_ptr,获得shared_ptr后可以成功调用管理对象的方法,这个过程中<font color="#ff0000">引用计数是在增加的,因此如果原来的shared_ptr销毁是不影响你这个临时对象使用, 资源析构正常</font>。 下面是gnu STL 库中最后调用lock函数会跑到的地方,引用计数 + 1 。(GNU STL 文件:shared_ptr_base.h) </p>
  2. ![](https://img2018.cnblogs.com/blog/1285081/201809/1285081-20180929233137997-1131170390.png)
  3. <p style="color: #AD5D0F;font-weight: bold;font-size: 20px; font-family: '微软雅黑';">2. weak_ptr 实现和循环引用问题</p>
  4. ------
  5. <p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">1. shared_ptr 循环引用问题</p>
  6. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">我们首先看一下循环引用的问题,具体代码如下: </p>
  7. 测试类:

include

include

class foo;

class Test

{

public:

Test()

{

std::cout << "construct.." << std::endl;

}

  1. void method()
  2. {
  3. std::cout << "welcome Test.." << std::endl;
  4. }
  5. ~Test()
  6. {
  7. std::cout << "destruct.." << std::endl;
  8. }

public:

std::shared_ptr fooptr;

};

class foo

{

public:

foo()

{

std::cout << "foo construct.." << std::endl;

}

  1. void method()
  2. {
  3. std::cout << "welcome Test foo.." << std::endl;
  4. }
  5. ~foo()
  6. {
  7. std::cout << "foo destruct.." << std::endl;
  8. }

public:

std::shared_ptr testptr;

};

  1. main函数:

int main()

{

// 循环引用 测试

Test* t2 = new Test();

foo* foo1 = new foo();

  1. std::shared_ptr<Test> shptr_Test(t2);
  2. std::shared_ptr<foo> shptr_foo(foo1);
  3. std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl;
  4. std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl;
  5. shptr_Test->fooptr = shptr_foo;
  6. shptr_foo->testptr = shptr_Test;
  7. std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl;
  8. std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl;
  9. return 0;

}

  1. 测试结果:

bash-4.2$ ./share_ptr

construct..

foo construct..

shptr_Test RefCount: 1

shptr_foo RefCount: 1

shptr_Test RefCount: 2

shptr_foo RefCount: 2

  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">从打印结果可以很明显的看出,经过循环引用, 对象引用计数变成了2,并且执行完后,<font color="#ff0000">资源没有释放,没有调用类的destruct(析构)函数</font></p>
  2. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">将类中的std::shared_ptr<foo> 换成 std::weak_ptr<foo></p>

class foo;

class Test

{

public:

Test()

{

std::cout << "construct.." << std::endl;

}

  1. void method()
  2. {
  3. std::cout << "welcome Test.." << std::endl;
  4. }
  5. ~Test()
  6. {
  7. std::cout << "destruct.." << std::endl;
  8. }

public:

std::weak_ptr fooptr;

};

class foo

{

public:

foo()

{

std::cout << "foo construct.." << std::endl;

}

  1. void method()
  2. {
  3. std::cout << "welcome Test foo.." << std::endl;
  4. }
  5. ~foo()
  6. {
  7. std::cout << "foo destruct.." << std::endl;
  8. }

public:

std::weak_ptr testptr;

};


  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">再看下weak_ptr的执行结果,可以看到<font color="#ff0000">计数正常,资源成功释放</font></p>

bash-4.2$ ./share_ptr

construct..

foo construct..

shptr_Test RefCount: 1

shptr_foo RefCount: 1

shptr_Test RefCount: 1

shptr_foo RefCount: 1

foo destruct..

destruct..

  1. <p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">2. weak_ptr 实现</p>
  2. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">我们这边贴一下weak_ptr类的代码:</p>

template

class weak_ptr

{

public:

template

friend class weak_ptr;

  1. template <class S>
  2. friend class shared_ptr;
  3. constexpr weak_ptr() noexcept : m_iWeakRefCount(nullptr), m_ptr(nullptr) { }
  4. weak_ptr( const weak_ptr<T>& rhs ) noexcept : m_iWeakRefCount(rhs.m_iWeakRefCount)
  5. {
  6. m_ptr = rhs.lock().getPointer();
  7. }
  8. weak_ptr( const shared_ptr<T>& rhs ) noexcept
  9. : m_iWeakRefCount(rhs.m_iRefCount), m_ptr(rhs.m_ptr) { }
  10. template <typename S>
  11. weak_ptr & operator=(const shared_ptr<S> & rhs)
  12. {
  13. m_ptr = dynamic_cast<T *>(rhs.m_ptr);
  14. m_iWeakRefCount = rhs.m_iRefCount;
  15. return *this;
  16. }
  17. template <typename S>
  18. weak_ptr & operator=(const weak_ptr<S> & rhs)
  19. {
  20. m_ptr = dynamic_cast<T *>(rhs.m_ptr);
  21. m_iWeakRefCount = rhs.m_iWeakRefCount;
  22. return *this;
  23. }
  24. weak_ptr& operator=( const weak_ptr& rhs ) noexcept
  25. {
  26. m_iWeakRefCount = rhs.m_iWeakRefCount;
  27. m_ptr = rhs.m_ptr;
  28. return *this;
  29. }
  30. weak_ptr& operator=( const shared_ptr<T>& rhs ) noexcept
  31. {
  32. m_iWeakRefCount = rhs.m_iRefCount;
  33. m_ptr = rhs.m_ptr;
  34. return *this;
  35. }
  36. shared_ptr<T> lock() const noexcept
  37. {
  38. shared_ptr<T> tmp;
  39. if(m_iWeakRefCount && *m_iWeakRefCount > 0)
  40. {
  41. tmp.m_iRefCount = m_iWeakRefCount;
  42. tmp.m_ptr = m_ptr;
  43. if(tmp.m_iRefCount)
  44. {
  45. ++(*tmp.m_iRefCount);
  46. }
  47. }
  48. return tmp;
  49. }
  50. int use_count()
  51. {
  52. return *m_iWeakRefCount;
  53. }
  54. bool expired() const noexcept
  55. {
  56. return *m_iWeakRefCount == 0;
  57. }
  58. void Reset()
  59. {
  60. m_ptr = NULL;
  61. m_iWeakRefCount = NULL;
  62. }

private:

int * m_iWeakRefCount;

  1. T* m_ptr;

};

  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">主要注意的是lock函数,如果计数指针为空,那么会返回一个空的shared_ptr,然后就是<font color="#ff0000">不能重载operator*和operator-> 操作符</font></p>
  2. 主要参考: <a href="https://zh.cppreference.com/w/cpp/memory/weak_ptr" target="_red"><font color=#00ffff size=5>cppreference.com</font></a>
  3. 完整实现见:<a href="https://github.com/Yejy813/stl_implement/tree/master/smart_ptr" target="_red"><font color=#00ffff size=5>smart_ptr</font></a>
  4. <p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">3. enable_shared_from_this</p>
  5. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">这边还有一个点也要介绍一下,那就是enable_shared_from_this,这个主要是为了处理<font color="#ff0000">在shared_ptr管理的对象中要使用该对象的指针所引出的问题</font>。 我们看下下面这个例子: </p>

class foo

{

public:

std::shared_ptr getptr()

{

// 如果类中要返回自己的指针怎么办?

return std::shared_ptr(this);

}

  1. ~foo()
  2. {
  3. std::cout << "foo destruct .. " << std::endl;
  4. }

};

int main()

{

std::shared_ptr bp1(new foo());

bp1->getptr();

std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;

}

  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';"><font color="#ff0000">看下结果,释放两次</font></p>

ash-4.2$ ./share_ptr

foo destruct ..

bp1.use_count() = 1

foo destruct ..

  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">其实我们都不用测试,因为你如果<font color="#ff0000">直接使用该对象的this指针又拷贝给另一个shared_ptr,那不就等于两个没有关系的shared_ptr管理同一个对象了吗? 释放的时候等于会调用两次该对象的析构函数</font>。enable_shared_from_this就是用来解决这个问题的。看下代码:</p>

class foo : public std::enable_shared_from_this

{

public:

std::shared_ptr getptr()

{

return shared_from_this();

}

  1. ~foo()
  2. {
  3. std::cout << "foo destruct .. " << std::endl;
  4. }

};

int main()

{

std::shared_ptr bp1(new foo());

bp1->getptr();

std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;

}

  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">看下结果,成功释放:</p>

bash-4.2$ ./share_ptr

bp1.use_count() = 1

foo destruct ..


  1. <p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';"><font color="#ff0000">总结一下,weak_ptr本质是以一种观察者的形象存在,它可以获取到观察主体的状态,但是无法获取直接获取到观察主体,无法直接对观察主体修改,无法释放观察主体的资源,你只能通过转换成shared_ptr来做一些事情。 和观察者模式很像,订阅,得到观察主体状态,在多线程环境下会比较管用! </font></p>
  2. <p style="font-size: 15px;text-indent:60em;letter-spacing:1px; font-family: '微软雅黑';">2018年9月30日00:40:02</p>

智能指针之 weak_ptr的更多相关文章

  1. [6] 智能指针boost::weak_ptr

    [1]boost::weak_ptr简介 boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hp ...

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

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

  3. C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>

    一.weak_ptr出现的意义 上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_pt ...

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

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

  5. 【STL学习】智能指针之weak_ptr

    简介 weak_ptr是shared_ptr的观察者,它不会干扰shared_ptr所共享对象的所有权,当一个weak_ptr所观察的shared_ptr要释放它的资源时,它会把相关的weak_ptr ...

  6. 智能指针std::weak_ptr

    std::weak_ptr 避免shared_ptr内存泄漏的利器.

  7. C++智能指针--weak_ptr

    weak_ptr是对对象的一种弱引用,它不会添加对象的引用计数.weak_ptr和shared_ptr之间能够相互转换.shared_ptr能够直接赋值给week_ptr,week_ptr可通过调用l ...

  8. Boost智能指针——weak_ptr

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

  9. c++智能指针(unique_ptr 、shared_ptr、weak_ptr、auto_ptr)

    一.前序 什么是智能指针? ——是一个类,用来存储指针(指向动态分配对象也就是堆中对象的的指针). c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写 ...

随机推荐

  1. SpringBoot 2.0 集成 JavaMail ,实现异步发送邮件

    一.JavaMail的核心API 1.API功能图解 2.API说明 (1).Message 类: javax.mail.Message 类是创建和解析邮件的一个抽象类 子类javax.mail.in ...

  2. ionic4+angular7+cordova开发入门

    前言 ionic是一个垮平台开发框架,可通过web技术开发出多平台的应用.但只建议开发简单应用.复杂的应用需要用到许多cordova插件,而cordova插件的更新或者移动平台的更新很可能导致插件的不 ...

  3. 00 | QPS

    每秒查询率 QPS Query Per Second 某个查询服务器 在 规定时间内 处理了多少流量 对应的fetches/sec,即每秒响应请求数,就是最大吞吐量 原理:每天80%的访问集中在20% ...

  4. shell 截取字符串(转)

    linux中对字符串的处理: 1.字符串分割例如  AAAAA-BBBBBB  按-分割去前后两部分 cut : [rich@localhost ~]$ str=AAAAA-BBBBBB[rich@l ...

  5. iOS开发 - 线程与进程的认识与理解

    进程: 进程是指在系统中正在运行的一个应用程序,比如同时打开微信和Xcode,系统会分别启动2个进程; 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内; 线程: 一个进程要想执行任务 ...

  6. mysql整理(个人)

    注意:以下命令都是在Linux系统下执行的: 1.验证mysql是否安装成功: mysqladmin --version 2.连接mysql服务器: mysql -u root -p 之后输入密码 3 ...

  7. Tinghua Data Mining 4

    贝叶斯 决策树 知道三文鱼和金枪鱼颜色 让你去猜 B命中的概率不能直接减去四分之三 因为有可能同时命中 A B 命中不是互斥事件 即便体检报告是阳性,真正得癌症的概率也很小,只有0.21 绝大多数的阳 ...

  8. [WOJ4354] 蜀石经

    题目链接: 点我 题目分析: 大模拟,貌似\(O(n^2)\)也可以卡常过,复杂度正确的做法是用优先队列维护. 代码: #include<bits/stdc++.h> #define N ...

  9. ctypes to load library in c/c++

    cdll.LoadLibrary(...) restype (default is c_int) argtypes (what's the default? c_int?) customized da ...

  10. JTable运行的时候抛出NullPointerException的问题

    在一个需要动态更新JTable的程序中,为了实现动态刷修数据.在主线程之外开了个新线程来进行算法的执行还有数值计算,然后最后调用 jTable.updateUi(); 的方法. 然后图形界面上是一点问 ...