std::weak_ptr
std::weak_ptr是一种弱引用,它不能单独使用,设计之初是为了配合std::shared_ptr,解决后者设计上存在的问题。
-
使用注意:
- 不能直接指向原始指针:
std::weak_ptr<int> wp (new int);
- 只能指向
std::shared_ptr
对象或者std::weak_ptr
对象 - 不增加引用计数
- 可以用
expired()
来检测指向的std::shared_ptr
管理的对象是否被析构了。 - 不能直接使用
std::shared_ptr
管理的对象,如果要使用需要调用lock()
。如果底层的对象还没被析构,那么就会返回一个std::shared_ptr
指针,指向该对象,否则返回nullptr
。
- 不能直接指向原始指针:
-
构造函数
constexpr weak_ptr() noexcept; weak_ptr( const weak_ptr& r ) noexcept; weak_ptr( weak_ptr&& r ) noexcept; template< typename Y > weak_ptr( const weak_ptr<Y>& r ) noexcept; template< typename Y > weak_ptr( weak_ptr<Y>&& r ) noexcept; template< typename Y > weak_ptr( const std::shared_ptr<Y>& r ) noexcept;
从构造函数可见,
std::weak_ptr
只能接受std::weak_ptr
和std::shared_ptr
类型,而不能std::weak_ptr<T> wp (new T);
。移动语义下的构造函数,构造完成
r
将会变成nullptr
,不可用。std::weak_ptr
的正确使用场景是那些资源如果可能就使用,如果不可使用则不用的场景,它不参与资源的生命周期管理。例如,网络分层结构中,Session 对象(会话对象)利用 Connection 对象(连接对象)提供的服务工作,但是 Session 对象不管理 Connection 对象的生命周期,Session 管理 Connection 的生命周期是不合理的,因为网络底层出错会导致 Connection 对象被销毁,此时 Session 对象如果强行持有 Connection 对象与事实矛盾。 -
std::weak_ptr
主要有两个用途:
它只能配合std::shared_ptr
使用,不能单独使用。
- 防止
std::shared_ptr
循环引用
如果两个std::shared_ptr
相互引用,那么就会形成一个环,引用计数无法变成0,也会导致内存泄漏。
class Foo : public std::enable_shared_from_this<Foo> {
public:
Foo(){ std::cout<<"ctor\n"; }
~Foo(){ std::cout<<"dtor\n"; }
void self()
{
fptr_ = shared_from_this();
}
private:
std::shared_ptr<Foo> fptr_; // 改 fptr_ 为 std::weak_ptr 类型即可
};
int main() {
{
std::shared_ptr<Foo> fptr = std::make_shared<Foo>();
fptr->self();
}
return 0;
}
-
std::enable_shared_from_this<T>::shared_from_this
这是个侵入式设计。为的解决传入this
导致对象被析构两次的问题。
什么情况下需要使用shared_from_this()
??? 用于返回当前对象*this
的std::shared_ptr
类型指针时:class Foo : public enable_shared_from_this<Foo>{ public: Foo(){ std::cout<<"Foo ctor.\n"; } ~Foo(){ std::cout<<"Foo dtor.\n"; } std::shared_ptr<Foo> getSelf(){ return shared_from_this(); } }; int main() { Foo* foo = new Foo; std::shared_ptr<Foo> sp1(foo); std::shared_ptr<Foo> sp2 = sp1->getSelf(); // 为了对 foo对象进行共享 std::cout<<std::boolalpha; std::cout<<(sp2.get()== foo)<<std::endl; std::cout<<sp1.use_count()<<std::endl; }
函数原型
template<typename _Tp> class enable_shared_from_this { protected: ... public: shared_ptr<_Tp> shared_from_this() { return shared_ptr<_Tp>(this->_M_weak_this); } shared_ptr<const _Tp> shared_from_this() const { return shared_ptr<const _Tp>(this->_M_weak_this); } private: ... mutable weak_ptr<_Tp> _M_weak_this; }
enable_shared_from_this
的子类需要返回自身的std::shared_ptr
指针,那么就需要继承这个类。 -
成员变量为什么是
weak_ptr
类型
因为如果是std::shared_ptr
类型,那么就永远无法析构对象自身。这个
_M_weak_this
不是这个类中初始化,而是在shared_ptr
中初始化,初始化的值就是this
。因此如果智能指针类型是std::shared_ptr
,那么这个类对象一旦创建,引用计数就是1,那么永远也无法析构。 -
为什么不直接传回
this
std::shared_ptr
的引用计数增加是需要用operator=
实现的。class Foo {/** ... */}; int main() { Foo* foo = new Foo; std::shared_ptr<Foo> sp1(foo); std::shared_ptr<Foo> sp2(foo); std::cout<<sp1.use_count()<<std::endl; // 输出是1 }
也就是说,尽管
sp1
和sp2
都指向了foo
,但是却不共享计数,当析构的时候就会被析构两次,产生未定义行为。
std::weak_ptr
可以接受std::shared_ptr
参数来构造自己,std::shared_ptr
也具有接受std::weak_ptr
参数来构造自己。
智能指针问题
-
share_ptr
与unique_ptr
区别主要在于前者采用引用技术实现对象共享,
redis
里的对象管理也是采用这个。而后者只能独占,不能赋值/复制,只能移动,因为其拷贝构造函数和赋值函数被禁用了。在
std::unique_ptr
内部:// Disable copy from lvalue. unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete;
参考
shared_ptr
指向一个动态数组需要注意什么?
析构器需要设置为 delete[]
。而uniqued_ptr
的的默认析构器模板类std::default_delete
,能自动设别new int
和new int[]
因为不用担心。
template< class Y > explicit shared_ptr( Y* ptr );
template< class Y, class Deleter > shared_ptr( Y* ptr, Deleter d )
上面一个,默认的析构器是delete ptr
。当 Y* ptr = new Y[x]
,析构器也需要重置。
#include <memory>
#include <vector>
#include <algorithm>
int main() {
// {
// std::shared_ptr<int> shared_bad(new int[10]);
// } // 析构函数调用 delete ,未定义行为
{
std::shared_ptr<int> shared_good(new int[10], std::default_delete<int[]>());
} // 析构函数调用 delete[] , ok
{
std::unique_ptr<int> ptr(new int(5));
} // unique_ptr<int> 使用 default_delete<int>
{
std::unique_ptr<int[]> ptr(new int[10]);
} // unique_ptr<int[]> 使用 default_delete<int[]>
}
std::shared_ptr
线程安全 ?
std::shared_ptr
的引用计数本身是安全且无锁的,但对象的读写则不是。也就是说std::shared_ptr
对象的创建析构是线程安全的,但是多线程读写std::shared_ptr
对象不是线程安全的。std::shared_ptr
内存是由于两个组成部分: 指向管理对象的指针 和 引用计数器。在读/写时,是直接对两个变量操作,不可能是原子类型的。因为 std::shared_ptr
有两个数据成员,读写操作不能原子化.使得多线程读写同一个 std::shared_ptr 对象需要加锁.
std::weak_ptr
的实现原理
std::weak_ptr
是为了解决 std::shared_ptr
循环引用而生,构造 std::weak_ptr
对象只能通过 std::shared_ptr
来构造,但是std::weak_ptr
对象的生命周期对相应的 std::shared_ptr
的引用计数不产生影响,即不增加或者减少引用计数。
std::weak_ptr
的引用计数部分也是有锁操作,因此 std::weak_ptr
对象生命周期的构造与销毁都是线程安全的。
// 基类
template<typename _Tp, _Lock_policy _Lp>
class __weak_ptr
{
template<typename _Yp, typename _Res = void>
using _Compatible = typename enable_if<__sp_compatible_with<_Yp*, _Tp*>::value, _Res>::type;
// Constraint for assignment from shared_ptr and weak_ptr:
template<typename _Yp>
using _Assignable = _Compatible<_Yp, __weak_ptr&>;
public:
using element_type = typename remove_extent<_Tp>::type;
constexpr __weak_ptr() noexcept
: _M_ptr(nullptr), _M_refcount()
{ }
__weak_ptr(const __weak_ptr&) noexcept = default;
~__weak_ptr() = default;
// The "obvious" converting constructor implementation:
//
// template<typename _Tp1>
// __weak_ptr(const __weak_ptr<_Tp1, _Lp>& __r)
// : _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount) // never throws
// { }
//
// has a serious problem.
//
// __r._M_ptr may already have been invalidated. The _M_ptr(__r._M_ptr)
// conversion may require access to *__r._M_ptr (virtual inheritance).
//
// It is not possible to avoid spurious access violations since
// in multithreaded programs __r._M_ptr may be invalidated at any point.
template<typename _Yp, typename = _Compatible<_Yp>>
__weak_ptr(const __weak_ptr<_Yp, _Lp>& __r) noexcept
: _M_refcount(__r._M_refcount)
{ _M_ptr = __r.lock().get(); }
template<typename _Yp, typename = _Compatible<_Yp>>
__weak_ptr(const __shared_ptr<_Yp, _Lp>& __r) noexcept
: _M_ptr(__r._M_ptr), _M_refcount(__r._M_refcount)
{ }
__weak_ptr(__weak_ptr&& __r) noexcept
: _M_ptr(__r._M_ptr), _M_refcount(std::move(__r._M_refcount))
{ __r._M_ptr = nullptr; }
template<typename _Yp, typename = _Compatible<_Yp>>
__weak_ptr(__weak_ptr<_Yp, _Lp>&& __r) noexcept
: _M_ptr(__r.lock().get()), _M_refcount(std::move(__r._M_refcount))
{ __r._M_ptr = nullptr; }
__weak_ptr&
operator=(const __weak_ptr& __r) noexcept = default;
template<typename _Yp>
_Assignable<_Yp>
operator=(const __weak_ptr<_Yp, _Lp>& __r) noexcept
{
_M_ptr = __r.lock().get();
_M_refcount = __r._M_refcount;
return *this;
}
template<typename _Yp>
_Assignable<_Yp>
operator=(const __shared_ptr<_Yp, _Lp>& __r) noexcept
{
_M_ptr = __r._M_ptr;
_M_refcount = __r._M_refcount;
return *this;
}
__weak_ptr&
operator=(__weak_ptr&& __r) noexcept
{
_M_ptr = __r._M_ptr;
_M_refcount = std::move(__r._M_refcount);
__r._M_ptr = nullptr;
return *this;
}
template<typename _Yp>
_Assignable<_Yp>
operator=(__weak_ptr<_Yp, _Lp>&& __r) noexcept
{
_M_ptr = __r.lock().get();
_M_refcount = std::move(__r._M_refcount);
__r._M_ptr = nullptr;
return *this;
}
__shared_ptr<_Tp, _Lp> lock() const noexcept
{ return __shared_ptr<element_type, _Lp>(*this, std::nothrow); }
long use_count() const noexcept
{ return _M_refcount._M_get_use_count(); }
bool expired() const noexcept
{ return _M_refcount._M_get_use_count() == 0; }
template<typename _Tp1>
bool owner_before(const __shared_ptr<_Tp1, _Lp>& __rhs) const noexcept
{ return _M_refcount._M_less(__rhs._M_refcount); }
template<typename _Tp1>
bool owner_before(const __weak_ptr<_Tp1, _Lp>& __rhs) const noexcept
{ return _M_refcount._M_less(__rhs._M_refcount); }
void reset() noexcept
{ __weak_ptr().swap(*this); }
void swap(__weak_ptr& __s) noexcept
{
std::swap(_M_ptr, __s._M_ptr);
_M_refcount._M_swap(__s._M_refcount);
}
private:
// Used by __enable_shared_from_this.
void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept
{
if (use_count() == 0)
{
_M_ptr = __ptr;
_M_refcount = __refcount;
}
}
template<typename _Tp1, _Lock_policy _Lp1> friend class __shared_ptr;
template<typename _Tp1, _Lock_policy _Lp1> friend class __weak_ptr;
friend class __enable_shared_from_this<_Tp, _Lp>;
friend class enable_shared_from_this<_Tp>;
element_type* _M_ptr; // Contained pointer.
__weak_count<_Lp> _M_refcount; // Reference counter.
};
/**
* @brief A smart pointer with weak semantics.
*
* With forwarding constructors and assignment operators.
*/
template<typename _Tp>
class weak_ptr : public __weak_ptr<_Tp>
{
template<typename _Arg>
using _Constructible = typename enable_if<is_constructible<__weak_ptr<_Tp>, _Arg>::value>::type;
template<typename _Arg>
using _Assignable = typename enable_if<is_assignable<__weak_ptr<_Tp>&, _Arg>::value, weak_ptr&>::type;
public:
constexpr weak_ptr() noexcept = default;
template<typename _Yp,
typename = _Constructible<const shared_ptr<_Yp>&>>
weak_ptr(const shared_ptr<_Yp>& __r) noexcept
: __weak_ptr<_Tp>(__r) { }
weak_ptr(const weak_ptr&) noexcept = default;
template<typename _Yp, typename = _Constructible<const weak_ptr<_Yp>&>>
weak_ptr(const weak_ptr<_Yp>& __r) noexcept
: __weak_ptr<_Tp>(__r) { }
weak_ptr(weak_ptr&&) noexcept = default;
template<typename _Yp, typename = _Constructible<weak_ptr<_Yp>>>
weak_ptr(weak_ptr<_Yp>&& __r) noexcept
: __weak_ptr<_Tp>(std::move(__r)) { }
weak_ptr&
operator=(const weak_ptr& __r) noexcept = default;
template<typename _Yp>
_Assignable<const weak_ptr<_Yp>&>
operator=(const weak_ptr<_Yp>& __r) noexcept
{
this->__weak_ptr<_Tp>::operator=(__r);
return *this;
}
template<typename _Yp>
_Assignable<const shared_ptr<_Yp>&>
operator=(const shared_ptr<_Yp>& __r) noexcept
{
this->__weak_ptr<_Tp>::operator=(__r);
return *this;
}
weak_ptr&
operator=(weak_ptr&& __r) noexcept = default;
template<typename _Yp>
_Assignable<weak_ptr<_Yp>>
operator=(weak_ptr<_Yp>&& __r) noexcept
{
this->__weak_ptr<_Tp>::operator=(std::move(__r));
return *this;
}
shared_ptr<_Tp> lock() const noexcept
{ return shared_ptr<_Tp>(*this, std::nothrow); }
};