智能指针之 weak_ptr
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 )
#include <memory>
#include <iostream>
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}
void method()
{
std::cout << "welcome Test foo.." << std::endl;
}
~foo()
{
std::cout << "foo destruct.." << std::endl;
}
};
int main()
{
// weak_ptr
foo* foo2 = new foo();
// share_ptr 管理对象
std::shared_ptr<foo> shptr_foo2(foo2);
// weak_ptr 弱引用
std::weak_ptr<foo> weak_foo2(shptr_foo2);
// 如果要获取数据指针,需要通过lock接口获取
weak_foo2.lock()->method();
std::shared_ptr<foo> tmp = weak_foo2.lock();
// 我们这边有尝试多次获取所有权(lock),看一下引用计数个数
std::cout << "shptr_foo2 RefCount: " << weak_foo2.lock().use_count() << std::endl;
return 0;
}
执行结果:
```
bash-4.2$ ./share_ptr
foo construct..
welcome Test foo..
shptr_foo2 RefCount: 3
foo destruct..
<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>

<p style="color: #AD5D0F;font-weight: bold;font-size: 20px; font-family: '微软雅黑';">2. weak_ptr 实现和循环引用问题</p>
------
<p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">1. shared_ptr 循环引用问题</p>
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">我们首先看一下循环引用的问题,具体代码如下: </p>
测试类:
include
include
class foo;
class Test
{
public:
Test()
{
std::cout << "construct.." << std::endl;
}
void method()
{
std::cout << "welcome Test.." << std::endl;
}
~Test()
{
std::cout << "destruct.." << std::endl;
}
public:
std::shared_ptr fooptr;
};
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}
void method()
{
std::cout << "welcome Test foo.." << std::endl;
}
~foo()
{
std::cout << "foo destruct.." << std::endl;
}
public:
std::shared_ptr testptr;
};
main函数:
int main()
{
// 循环引用 测试
Test* t2 = new Test();
foo* foo1 = new foo();
std::shared_ptr<Test> shptr_Test(t2);
std::shared_ptr<foo> shptr_foo(foo1);
std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl;
std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl;
shptr_Test->fooptr = shptr_foo;
shptr_foo->testptr = shptr_Test;
std::cout << "shptr_Test RefCount: " << shptr_Test.use_count() << std::endl;
std::cout << "shptr_foo RefCount: " << shptr_foo.use_count() << std::endl;
return 0;
}
测试结果:
bash-4.2$ ./share_ptr
construct..
foo construct..
shptr_Test RefCount: 1
shptr_foo RefCount: 1
shptr_Test RefCount: 2
shptr_foo RefCount: 2
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">从打印结果可以很明显的看出,经过循环引用, 对象引用计数变成了2,并且执行完后,<font color="#ff0000">资源没有释放,没有调用类的destruct(析构)函数</font>。</p>
<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;
}
void method()
{
std::cout << "welcome Test.." << std::endl;
}
~Test()
{
std::cout << "destruct.." << std::endl;
}
public:
std::weak_ptr fooptr;
};
class foo
{
public:
foo()
{
std::cout << "foo construct.." << std::endl;
}
void method()
{
std::cout << "welcome Test foo.." << std::endl;
}
~foo()
{
std::cout << "foo destruct.." << std::endl;
}
public:
std::weak_ptr testptr;
};
<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..
<p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">2. weak_ptr 实现</p>
<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;
template <class S>
friend class shared_ptr;
constexpr weak_ptr() noexcept : m_iWeakRefCount(nullptr), m_ptr(nullptr) { }
weak_ptr( const weak_ptr<T>& rhs ) noexcept : m_iWeakRefCount(rhs.m_iWeakRefCount)
{
m_ptr = rhs.lock().getPointer();
}
weak_ptr( const shared_ptr<T>& rhs ) noexcept
: m_iWeakRefCount(rhs.m_iRefCount), m_ptr(rhs.m_ptr) { }
template <typename S>
weak_ptr & operator=(const shared_ptr<S> & rhs)
{
m_ptr = dynamic_cast<T *>(rhs.m_ptr);
m_iWeakRefCount = rhs.m_iRefCount;
return *this;
}
template <typename S>
weak_ptr & operator=(const weak_ptr<S> & rhs)
{
m_ptr = dynamic_cast<T *>(rhs.m_ptr);
m_iWeakRefCount = rhs.m_iWeakRefCount;
return *this;
}
weak_ptr& operator=( const weak_ptr& rhs ) noexcept
{
m_iWeakRefCount = rhs.m_iWeakRefCount;
m_ptr = rhs.m_ptr;
return *this;
}
weak_ptr& operator=( const shared_ptr<T>& rhs ) noexcept
{
m_iWeakRefCount = rhs.m_iRefCount;
m_ptr = rhs.m_ptr;
return *this;
}
shared_ptr<T> lock() const noexcept
{
shared_ptr<T> tmp;
if(m_iWeakRefCount && *m_iWeakRefCount > 0)
{
tmp.m_iRefCount = m_iWeakRefCount;
tmp.m_ptr = m_ptr;
if(tmp.m_iRefCount)
{
++(*tmp.m_iRefCount);
}
}
return tmp;
}
int use_count()
{
return *m_iWeakRefCount;
}
bool expired() const noexcept
{
return *m_iWeakRefCount == 0;
}
void Reset()
{
m_ptr = NULL;
m_iWeakRefCount = NULL;
}
private:
int * m_iWeakRefCount;
T* m_ptr;
};
<p style="font-size: 15px; text-indent:2em; letter-spacing:1px; font-family: '微软雅黑';">主要注意的是lock函数,如果计数指针为空,那么会返回一个空的shared_ptr,然后就是<font color="#ff0000">不能重载operator*和operator-> 操作符</font>。</p>
主要参考: <a href="https://zh.cppreference.com/w/cpp/memory/weak_ptr" target="_red"><font color=#00ffff size=5>cppreference.com</font></a>
完整实现见:<a href="https://github.com/Yejy813/stl_implement/tree/master/smart_ptr" target="_red"><font color=#00ffff size=5>smart_ptr</font></a>
<p style="font-size: 15px; letter-spacing:1px; font-weight: bold; font-family: '微软雅黑';">3. enable_shared_from_this</p>
<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);
}
~foo()
{
std::cout << "foo destruct .. " << std::endl;
}
};
int main()
{
std::shared_ptr bp1(new foo());
bp1->getptr();
std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
}
<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 ..
<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();
}
~foo()
{
std::cout << "foo destruct .. " << std::endl;
}
};
int main()
{
std::shared_ptr bp1(new foo());
bp1->getptr();
std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
}
<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 ..
<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>
<p style="font-size: 15px;text-indent:60em;letter-spacing:1px; font-family: '微软雅黑';">2018年9月30日00:40:02</p>
智能指针之 weak_ptr的更多相关文章
- [6] 智能指针boost::weak_ptr
[1]boost::weak_ptr简介 boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hp ...
- 深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)
1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他(放弃) 2. shared_ptr: 拥有共享对象所有权语义的智能指针 3. unique_ptr: 拥有独有对象所有权语义的智 ...
- C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>
一.weak_ptr出现的意义 上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_pt ...
- 【C++11新特性】 C++11智能指针之weak_ptr
如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...
- 【STL学习】智能指针之weak_ptr
简介 weak_ptr是shared_ptr的观察者,它不会干扰shared_ptr所共享对象的所有权,当一个weak_ptr所观察的shared_ptr要释放它的资源时,它会把相关的weak_ptr ...
- 智能指针std::weak_ptr
std::weak_ptr 避免shared_ptr内存泄漏的利器.
- C++智能指针--weak_ptr
weak_ptr是对对象的一种弱引用,它不会添加对象的引用计数.weak_ptr和shared_ptr之间能够相互转换.shared_ptr能够直接赋值给week_ptr,week_ptr可通过调用l ...
- Boost智能指针——weak_ptr
循环引用: 引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象.一个简单的例子如下: #include <string>#include <iost ...
- c++智能指针(unique_ptr 、shared_ptr、weak_ptr、auto_ptr)
一.前序 什么是智能指针? ——是一个类,用来存储指针(指向动态分配对象也就是堆中对象的的指针). c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写 ...
随机推荐
- [HNOI2010] 弹飞绵羊 bounce
标签:分块.题解: 200000,而且标号从0开始,很符合分块的条件啊.看看怎么实现. 首先分成√n个区间,然后如果我们对于每一个位置i,求出一个Next[i]和step[i],分别表示跳到的后一个位 ...
- Java 序列化(转)
转自 http://www.infoq.com/cn/articles/cf-java-object-serialization-rmi 对于一个存在于Java虚拟机中的对象来说,其内部的状态只保持在 ...
- bzoj1660:[Usaco2006 Nov]badhair乱头发节
Description 农民John的某 N 头奶牛 (1 <= N <= 80,000) 正在过乱头发节!由于每头牛都 意识到自己凌乱不堪的发型, FJ 希望统计出能够看到其他牛的头发的 ...
- 51Nod 1099 任务执行顺序 (贪心)
#include <iostream> #include <algorithm> using namespace std; +; struct node{ int r, q; ...
- C. Chessboard( Educational Codeforces Round 41 (Rated for Div. 2))
//暴力 #include <iostream> #include <algorithm> #include <string> using namespace st ...
- 证书重复冲突问题:Command /usr/bin/codesign failed with exit code 1
打开钥匙串 查看是否有两个identifier为相同 的证书,显然导证书的时候不知道怎么把证书导进了系统帐号,并且还重复了.把重复的证书删除就行了.
- ES5(基本包装类型)字符串的方法
看一下字符串有哪些常用的方法: 1.concat();将多个文本组合起来,返回新的字符串,就是拼接字符串. 查找位置 2.indexOf();返回要匹配的字符在字符串第一次出现的索引,参数就是匹配的字 ...
- python学习之模块:
每个.py文件就是一个以文件名作为区别的模块,模块化编程便于维护.其它模块要调用某个模块的变量和函数就要用import 模块,然后通过模块.函数.模块.变量来引用. 为防止模块间变量和函数乃至模块名的 ...
- MFC命令行及CCommandLineInfo类
获取命令行的方法: 1.GetCommandLine() 获取输入的所有信息,包括程序所在路径及参数 2.AfxGetApp()->m_lpCmdLine 只包含参数 一般情况下,获取到命令行后 ...
- 关于搭建系统直播和Thinkphp的杂谈(持续更新)
Q:Access denied for user 'root'@'localhost' 错误 A:第一种:配置文件中把数据库的用户名密码再改一遍,把runtime里的文件删除 第二种:修改syste ...