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;}
#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: 3foo destruct..
bash-4.2$ ./share_ptr
foo construct..
welcome Test foo..
shptr_foo2 RefCount: 3
foo destruct..
我们可以看到,weak_ptr多次通过lock转换成shared_ptr,获得shared_ptr后可以成功调用管理对象的方法,这个过程中引用计数是在增加的,因此如果原来的shared_ptr销毁是不影响你这个临时对象使用, 资源析构正常。 下面是gnu STL 库中最后调用lock函数会跑到的地方,引用计数 + 1 。(GNU STL 文件:shared_ptr_base.h)
2. weak_ptr 实现和循环引用问题
1. shared_ptr 循环引用问题
我们首先看一下循环引用的问题,具体代码如下:
测试类:
#include <memory>#include <iostream>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<foo> 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<Test> testptr;};
class foo;
class Test
Test()
std::cout << "construct.." << std::endl;
std::cout << "welcome Test.." << std::endl;
~Test()
std::cout << "destruct.." << std::endl;
std::shared_ptr<foo> fooptr;
std::shared_ptr<Test> 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;}
// 循环引用 测试
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;
测试结果:
bash-4.2$ ./share_ptr construct..foo construct..shptr_Test RefCount: 1shptr_foo RefCount: 1shptr_Test RefCount: 2shptr_foo RefCount: 2
construct..
shptr_Test RefCount: 1
shptr_foo RefCount: 1
shptr_Test RefCount: 2
shptr_foo RefCount: 2
从打印结果可以很明显的看出,经过循环引用, 对象引用计数变成了2,并且执行完后,资源没有释放,没有调用类的destruct(析构)函数。
将类中的std::shared_ptr 换成 std::weak_ptr:
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<foo> 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<Test> testptr;};
std::weak_ptr<foo> fooptr;
std::weak_ptr<Test> testptr;
再看下weak_ptr的执行结果,可以看到计数正常,资源成功释放:
bash-4.2$ ./share_ptr construct..foo construct..shptr_Test RefCount: 1shptr_foo RefCount: 1shptr_Test RefCount: 1shptr_foo RefCount: 1foo destruct..destruct..
destruct..
2. weak_ptr 实现
我们这边贴一下weak_ptr类的代码:
template <class T>class weak_ptr{public: template <class S> 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;};
template <class T>
class weak_ptr
template <class S>
friend class weak_ptr;
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;
weak_ptr & operator=(const weak_ptr<S> & rhs)
m_iWeakRefCount = rhs.m_iWeakRefCount;
weak_ptr& operator=( const weak_ptr& rhs ) noexcept
m_ptr = rhs.m_ptr;
weak_ptr& operator=( const shared_ptr<T>& rhs ) noexcept
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;
主要注意的是lock函数,如果计数指针为空,那么会返回一个空的shared_ptr,然后就是不能重载operator*和operator-> 操作符。
主要参考: cppreference.com 完整实现见:smart_ptr
3. enable_shared_from_this
这边还有一个点也要介绍一下,那就是enable_shared_from_this,这个主要是为了处理在shared_ptr管理的对象中要使用该对象的指针所引出的问题。 我们看下下面这个例子:
class foo{public: std::shared_ptr<foo> getptr() { // 如果类中要返回自己的指针怎么办? return std::shared_ptr<foo>(this); } ~foo() { std::cout << "foo destruct .. " << std::endl; }};int main(){ std::shared_ptr<foo> bp1(new foo()); bp1->getptr(); std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;}
std::shared_ptr<foo> getptr()
// 如果类中要返回自己的指针怎么办?
return std::shared_ptr<foo>(this);
std::cout << "foo destruct .. " << std::endl;
std::shared_ptr<foo> bp1(new foo());
bp1->getptr();
std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;
看下结果,释放两次:
ash-4.2$ ./share_ptr foo destruct .. bp1.use_count() = 1foo destruct ..
ash-4.2$ ./share_ptr
foo destruct ..
bp1.use_count() = 1
其实我们都不用测试,因为你如果直接使用该对象的this指针又拷贝给另一个shared_ptr,那不就等于两个没有关系的shared_ptr管理同一个对象了吗? 释放的时候等于会调用两次该对象的析构函数。enable_shared_from_this就是用来解决这个问题的。看下代码:
class foo : public std::enable_shared_from_this<foo>{public: std::shared_ptr<foo> getptr() { return shared_from_this(); } ~foo() { std::cout << "foo destruct .. " << std::endl; }};int main(){ std::shared_ptr<foo> bp1(new foo()); bp1->getptr(); std::cout << "bp1.use_count() = " << bp1.use_count() << std::endl;}
class foo : public std::enable_shared_from_this<foo>
return shared_from_this();
看下结果,成功释放:
bash-4.2$ ./share_ptr bp1.use_count() = 1foo destruct ..
总结一下,weak_ptr本质是以一种观察者的形象存在,它可以获取到观察主体的状态,但是无法获取直接获取到观察主体,无法直接对观察主体修改,无法释放观察主体的资源,你只能通过转换成shared_ptr来做一些事情。 和观察者模式很像,订阅,得到观察主体状态,在多线程环境下会比较管用!
2018年9月30日00:40:02
本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728