测试代码
#include <stdio.h>
#include <memory>
class CTest {
public:
CTest() { printf("%s()\n", __func__); }
~CTest() { printf("%s()\n", __func__); }
};
int main(int argc, char **argv)
{
std::shared_ptr<void> ptr(new CTest);
return 0;
}
测试结果
通过测试可见,正常调用析构函数了。我们可以思考一个问题,就是模板参数是void,但是问什么可以调用到CTest的析构呢。
我们以以下代码为例子
#include <iostream>
#include <memory>
class CTest {
public:
CTest() { printf("%s()\n", __func__); }
~CTest() { printf("%s()\n", __func__); }
};
int main(int argc, char **argv)
{
void *ptr = new CTest;
delete ptr;
return 0;
}
测试结果
从测试结果看,delete
虽然释放了CTest
的内存,但是未调用~CTest
,所以shared_ptr
可以正常调用到析构一定是shared_ptr
做了某些操作。
注意:以下shared_ptr均为linux下源代码
// shared_ptr继承自__shared_ptr,所以直接看__shared_ptr代码
template<typename _Tp>
class shared_ptr : public __shared_ptr<_Tp> {}
// _Lock_policy无关紧要,是防止冲突手段,包括原子,锁和单线程(即无锁无原子操作),默认是原子
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr : public __shared_ptr_access<_Tp, _Lp> {}
// 接下来看__shared_ptr构造函数
// 常用的就下面几个函数
constexpr __shared_ptr() noexcept // 无参数的默认构造
: _M_ptr(0), _M_refcount()
{ }
template<typename _Yp, typename = _SafeConv<_Yp>>
explicit __shared_ptr(_Yp* __p) // 传入指针的有参构造
: _M_ptr(__p), _M_refcount(__p, typename is_array<_Tp>::type())
{
static_assert( !is_void<_Yp>::value, "incomplete type" );
static_assert( sizeof(_Yp) > 0, "incomplete type" );
// 以上可以确定_Yp类型不是void
_M_enable_shared_from_this_with(__p);
}
template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
__shared_ptr(_Yp* __p, _Deleter __d) // 可以传入删除器的有参构造
: _M_ptr(__p), _M_refcount(__p, std::move(__d))
{
static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
"deleter expression d(p) is well-formed");
_M_enable_shared_from_this_with(__p);
}
// 从以上来看,构造函数多了一个模板参数_Yp,这个就是真正的指针类型
// _M_refcount是__shared_count<_Lp>类型(_Lp是_Lock_policy),由它进行内存管理
// 而此时传进去的指针类型也是_Yp*,所以在__shared_ptr析构时,其实__shared_count进行的内存释放,故可以正确的调用对应类型的析构
将__shared_count
整理出来
template <_Lock_policy _Lp>
class __shared_count
{
public:
constexpr __shared_count() noexcept : _M_pi(0)
{
}
template <typename _Ptr>
explicit __shared_count(_Ptr __p) : _M_pi(0)
{
__try
{
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
}
__catch(...)
{
delete __p;
__throw_exception_again;
}
}
template <typename _Ptr>
__shared_count(_Ptr __p, /* is_array = */ false_type)
: __shared_count(__p)
{
}
template <typename _Ptr>
__shared_count(_Ptr __p, /* is_array = */ true_type)
: __shared_count(__p, __sp_array_delete{}, allocator<void>())
{
}
template <typename _Ptr, typename _Deleter>
__shared_count(_Ptr __p, _Deleter __d)
: __shared_count(__p, std::move(__d), allocator<void>())
{
}
template <typename _Ptr, typename _Deleter, typename _Alloc>
__shared_count(_Ptr __p, _Deleter __d, _Alloc __a) : _M_pi(0)
{
typedef _Sp_counted_deleter<_Ptr, _Deleter, _Alloc, _Lp> _Sp_cd_type;
__try
{
typename _Sp_cd_type::__allocator_type __a2(__a);
auto __guard = std::__allocate_guarded(__a2);
_Sp_cd_type *__mem = __guard.get();
::new (__mem) _Sp_cd_type(__p, std::move(__d), std::move(__a));
_M_pi = __mem;
__guard = nullptr;
}
__catch(...)
{
__d(__p); // Call _Deleter on __p.
__throw_exception_again;
}
}
template <typename _Tp, typename _Alloc, typename... _Args>
__shared_count(_Sp_make_shared_tag, _Tp *, const _Alloc &__a,
_Args &&...__args)
: _M_pi(0)
{
typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
typename _Sp_cp_type::__allocator_type __a2(__a);
auto __guard = std::__allocate_guarded(__a2);
_Sp_cp_type *__mem = __guard.get();
::new (__mem) _Sp_cp_type(std::move(__a),
std::forward<_Args>(__args)...);
_M_pi = __mem;
__guard = nullptr;
}
// Special case for unique_ptr<_Tp,_Del> to provide the strong guarantee.
template <typename _Tp, typename _Del>
explicit __shared_count(std::unique_ptr<_Tp, _Del> &&__r) : _M_pi(0)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2415. Inconsistency between unique_ptr and shared_ptr
if (__r.get() == nullptr)
return;
using _Ptr = typename unique_ptr<_Tp, _Del>::pointer;
using _Del2 = typename conditional<is_reference<_Del>::value,
reference_wrapper<typename remove_reference<_Del>::type>,
_Del>::type;
using _Sp_cd_type = _Sp_counted_deleter<_Ptr, _Del2, allocator<void>, _Lp>;
using _Alloc = allocator<_Sp_cd_type>;
using _Alloc_traits = allocator_traits<_Alloc>;
_Alloc __a;
_Sp_cd_type *__mem = _Alloc_traits::allocate(__a, 1);
_Alloc_traits::construct(__a, __mem, __r.release(),
__r.get_deleter()); // non-throwing
_M_pi = __mem;
}
// Throw bad_weak_ptr when __r._M_get_use_count() == 0.
explicit __shared_count(const __weak_count<_Lp> &__r);
// Does not throw if __r._M_get_use_count() == 0, caller must check.
explicit __shared_count(const __weak_count<_Lp> &__r, std::nothrow_t);
~__shared_count() noexcept
{
if (_M_pi != nullptr)
_M_pi->_M_release();
}
__shared_count(const __shared_count &__r) noexcept
: _M_pi(__r._M_pi)
{
if (_M_pi != 0)
_M_pi->_M_add_ref_copy();
}
__shared_count &operator=(const __shared_count &__r) noexcept
{
_Sp_counted_base<_Lp> *__tmp = __r._M_pi;
if (__tmp != _M_pi)
{
if (__tmp != 0)
__tmp->_M_add_ref_copy();
if (_M_pi != 0)
_M_pi->_M_release();
_M_pi = __tmp;
}
return *this;
}
void _M_swap(__shared_count &__r) noexcept
{
_Sp_counted_base<_Lp> *__tmp = __r._M_pi;
__r._M_pi = _M_pi;
_M_pi = __tmp;
}
long _M_get_use_count() const noexcept
{
return _M_pi != 0 ? _M_pi->_M_get_use_count() : 0;
}
bool _M_unique() const noexcept
{
return this->_M_get_use_count() == 1;
}
void *_M_get_deleter(const std::type_info &__ti) const noexcept
{
return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr;
}
bool _M_less(const __shared_count &__rhs) const noexcept
{
return std::less<_Sp_counted_base<_Lp> *>()(this->_M_pi, __rhs._M_pi);
}
bool _M_less(const __weak_count<_Lp> &__rhs) const noexcept
{
return std::less<_Sp_counted_base<_Lp> *>()(this->_M_pi, __rhs._M_pi);
}
// Friend function injected into enclosing namespace and found by ADL
friend inline bool operator==(const __shared_count &__a, const __shared_count &__b) noexcept
{
return __a._M_pi == __b._M_pi;
}
private:
friend class __weak_count<_Lp>;
_Sp_counted_base<_Lp> *_M_pi;
};