这篇博客是对unique_ptr源代码的分析,本文使用的编译器是MinGW
本篇文章不保证能够说明清楚unique_ptr实现的所有细节以及原因,但会尽可能的做到这样
需要读者有TMP的基础,例如熟悉std::enable_if, std::remove_referene等等的元函数
本篇文章不是一个unique_ptr的使用教程,但是会提及到一些使用中的细节
unique_ptr是比rew-pointer更为好的选择,标准库实现unique_ptr使接口尽可能的接近原生指针。不同的编译器对其实现有所不同。
gcc编译器对 unique_ptr的实现,大概分为以下几部分
C++支持OOP,这就涉及到的多态,本章的主题是指针,如果想放心的对于unique_ptr的使用就像使用内置指针一样(某些部分),需要做类型安全的约束。包括但不限于
为什么需要额外的类型安全,而不是靠编译器的检擦?
因为C++是一个弱类型语言,就是说在不同的类型之间可以实现隐式的类型转换——这种转换有时候很便利。但有更多的时候会产生副作用。例如精度丢失,数据溢出,甚至是内存泄漏和未定义行为。
在C中T[]
和T*
可以看作是等价的类型,但是在C++的模板中,请将T[]
和T*
看作不同类型
例如std::is_array的实现
template
struct is_array
: public false_type { };
template
struct is_array<_Tp[_Size]>
: public true_type { };
template
struct is_array<_Tp[]>
: public true_type { };
类似的如果想用unique_ptr存放数组unique_ptr
是正确的,而unique_ptr
是不正确的
gcc对于unique_ptr的实现放在了unique_ptr.h中
本篇文章将按照源文件从上至下的顺序来剖析unique_ptr的实现
//基本的默认删除器实现
/// Primary template of default_delete, used by unique_ptr for single objects
template struct default_delete {
/// Default constructor
constexpr default_delete() noexcept = default;
/** @brief Converting constructor.
*
* Allows conversion from a deleter for objects of another type, `_Up`,
* only if `_Up*` is convertible to `_Tp*`.
*/
// 正如注释所说,如果底层指针允许准换,则允许删除器转换
// 空实现目的是做类型约束
// 使用_Requires检查_Up * 能否转换为_TP *
// 例如能用基类指针来析构一个派生类对像,却不能用派生类指针析构基类对象(先通过语法再说)
template >>
default_delete(const default_delete<_Up> &) noexcept {}
//重载的调用运算符,接受一个_Tp指针,对其进行delete
/// Calls `delete __ptr`
void operator()(_Tp *__ptr) const {
//如果是一个非完整类型,例如只声明没有定义,会断言失败
static_assert(!is_void<_Tp>::value,
"can't delete pointer to incomplete type");
static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type");
delete __ptr;
}
};
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 740 - omit specialization for array objects with a compile time length
/// Specialization of default_delete for arrays, used by `unique_ptr`
template struct default_delete<_Tp[]> {
public:
/// Default constructor
constexpr default_delete() noexcept = default;
/** @brief Converting constructor.
*
* Allows conversion from a deleter for arrays of another type, such as
* a const-qualified version of `_Tp`.
*
* Conversions from types derived from `_Tp` are not allowed because
* it is undefined to `delete[]` an array of derived types through a
* pointer to the base type.
*/
//允许删除其转换为另一种类型的数组,例如转换为_TP的const限定版本
//但是不允许从子类转换为父类,不要用多态的方式处理数组,通过基类指针删除由派生类的数组是未定义的
//所以禁止这种转换
//具体的信息请参考《More Effective C++》条款5
template >>
default_delete(const default_delete<_Up[]> &) noexcept {}
/// Calls `delete[] __ptr`
template
typename enable_if::value>::type //注意这里的类型是数组的指针
//虽然允许派生类指针隐式的转换为基类指针,为了检查这种转换,我们要写成这样
//例如可以从non-const 转换为const,但不能从derived ** 转换为 base **
operator()(_Up *__ptr) const {
static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type");
//调用delete[]
delete[] __ptr;
}
};
__uniq_ptr_impl是实现部分,把这个类读懂,就读懂了unique_ptr的一半。unique_ptr的核心实现,全部在这个类中。没有复杂的逻辑,但是做到类型安全的实现方法,还是比较难的部分。__uniq_ptr_impl中没有复杂的关于类型安全的约束。比较复杂的约束在uniqe_ptr
和unique_ptr
中
// Manages the pointer and deleter of a unique_ptr
//_TP是指向的类型, _DP是删除器的类型
template class __uniq_ptr_impl {
//这里做了一个type_traits
//_Up实际上是_Tp, _Ep实际上是_Dp
//指向堆区对象指针的的类型为_Up *
template struct _Ptr {
using type = _Up *;
};
//如果删除器中声明了删除的指针类型,有优先考虑这个指针类型
//如果没有,类型替换失败,转而匹配上面的_Ptr
//这样做的原因,想想一种情况
//class B; class D : public B;
//删除器中接受B*而实际存储的对象为D类型对象,在删除器中定义了pointer为B*
template
struct _Ptr<_Up, _Ep,
__void_t::type::pointer>> {
//这里假定了给定的_Ep有type类型,并且type下有pointer类型
using type = typename remove_reference<_Ep>::type::pointer;
};
public:
//这里做了一个约束条件
//只是简单的别名定义,std::enable_if::没有取type
//用到的地方再unique_ptr中
//这个模板别名目的是为了约束_Dp也就是删除器,不能是一个指针,并且要求具有默认构造函数
using _DeleterConstraint = enable_if<
__and_<__not_>, is_default_constructible<_Dp>>::value>;
//pointer为_Tp, _Ep经过 _Ptr类型萃取后的的别名
//::type萃取出来一个指针类型
//我们把pointer 这个类型直接当作raw-pointer类型
using pointer = typename _Ptr<_Tp, _Dp>::type;
//正如断言所言,unique_ptr的删除其必须是一个函数对象或者一个左值引用
//如果是一个右值引用, !value == false,断言失败
static_assert(!is_rvalue_reference<_Dp>::value,
"unique_ptr's deleter type must be a function object type"
" or an lvalue reference type");
__uniq_ptr_impl() = default; // =defalut是调用tuple的默认构造,tuple中的默认构造会为每个对象给定一个初值
//例如int等类型会被初始化为0,而指针类型会被初始化为nullptr
__uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
//通过裸指针+删除器的方式构造
//删除器使用万能引用+转发的形式传递
template
__uniq_ptr_impl(pointer __p, _Del &&__d)
: _M_t(__p, std::forward<_Del>(__d)) {}
//移动构造
//注意如果类型没有移动构造函数情况就是拷贝构造
__uniq_ptr_impl(__uniq_ptr_impl &&__u) noexcept
//将u下的_M_t直接移动给自己
: _M_t(std::move(__u._M_t)) {
__u._M_ptr() = nullptr; //滞空u下的_M_ptr
}
//移动赋值运算符
__uniq_ptr_impl &operator=(__uniq_ptr_impl &&__u) noexcept {
reset(__u.release());
_M_deleter() = std::forward<_Dp>(__u._M_deleter()); //将删除器转发过去 为什么用转发??
return *this;
}
//_M_ptr获得指向堆区内存的指针
pointer &_M_ptr() { return std::get<0>(_M_t); }
//对于const的版本,我们按值返回即可,因为按值返回pointer的耗费较小
pointer _M_ptr() const { return std::get<0>(_M_t); }
//_M_ptr获得删除器
_Dp &_M_deleter() { return std::get<1>(_M_t); }
//对于删除其,使用const &方式返回,因为删除器是可能是函数对象——这种情值返回可能耗费较大
//并且删除器有可能不支持拷贝操作
const _Dp &_M_deleter() const { return std::get<1>(_M_t); }
//reset调用删除器析构raw-pointer指向的空间,并重新赋值为__p
//假定不会抛出异常 使用noexcept
void reset(pointer __p) noexcept {
const pointer __old_p = _M_ptr();
_M_ptr() = __p;
//不为nullptr, 进行析构操作
if (__old_p)
_M_deleter()(__old_p);
}
//并不析构指针指向的内存,而是返回指针
//并且将指向堆区部分的指针赋值为nullptr
pointer release() noexcept {
pointer __p = _M_ptr();
_M_ptr() = nullptr;
return __p;
}
//对于两个unique_ptr的swap,只需要交换其指针部分和删除器部分即可
void swap(__uniq_ptr_impl &__rhs) noexcept {
//这里的using std::swap请参考《Effective C++》条款25
using std::swap;
swap(this->_M_ptr(), __rhs._M_ptr());
swap(this->_M_deleter(), __rhs._M_deleter());
}
private:
//使用一个tuple来存储具体指向对象的指针和删除器,便于实现,上面默认构造函数中有解释
//并且,tuple具有空间压缩的功能(模板递归继承)
//这种方式为什么能够压缩空间,是另一个问题了,参考《深度探索C++对象模型》
tuple _M_t;
};
我们可以测试一下。正如我们所说在默认构造一个智能指针的情况下,不允许是一个函数指针,
这是由于内置指针在初始化的时候,也就是删除器初始化的时候,是一个nullptr。所以,在默认构造的情况下,不允许删除器是一个指针类型
如果把指针类型的删除器(函数指针)当作类型参数,编译器会说我找不到默认的构造函数,这是因为unique_ptr的默认构造函数进行了如此的约束,类型替换失败,于是找不到默认构造函数。如果析构器是函数对象或者lambda,就不会有这样的情况
namespace jan {
template
using _DeleterConstraint = enable_if<
__and_<__not_>, is_default_constructible<_Dp>>::value>;
}
void fun() { }
struct Foo {
Foo() = delete;
};
int main() {
typename jan::_DeleterConstraint::type; //error
typename jan::_DeleterConstraint::type; //ok
typename jan::_DeleterConstraint::type; //error
return 0;
}
从源代码中可以看出,如果删除器中定义了一个pointer型别,_Ptr会有限萃取出删除器::pointer作为unique_ptr的raw-pointer类型。我们想象这样一种情况。
class A { };
class B : public A { };
class C : public B { };
struct BDel {
using pointer = B*;
void operator()(pointer p) {
delete p;
}
};
struct ADel{
// using pointer = B*;
void operator()(A* p) {
//do something
//但是和BDel做的事情不一样
delete p;
}
};
int main() {
unique_ptr p (new C, BDel{});
return 0;
}
指定Bel为删除器类型,就表明,我们要处理的类型/要删除的类型,是B类型或者B的派生类。记者OOP中的一条规则吗?面向接口编程,而不是面向实例编程。这这个情况中,尽管是个C对象,但是仍当作B类型处理。如果没有_Ptr萃取,unique_ptr::pointer就是C*,无法应用于B对象。
就是说尽管我们这样写unique_ptr
,仍是可以应用于B对象,就像这样unique_ptr
是有一些莫名其妙!!!
在__uniq_ptr_impl和unique_ptr,又加上了一个中间层,目的是根据删除器是否有移动构造或者移动赋值属性,
部分特例化,来匹配不同的情况
// Defines move construction + assignment as either defaulted or deleted.
template ::value,
bool = is_move_assignable<_Dp>::value>
struct __uniq_ptr_data : __uniq_ptr_impl<_Tp, _Dp> {
//uisng 使用__uniq_ptr_impl的构造函数,下同
using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
__uniq_ptr_data(__uniq_ptr_data &&) = default;
__uniq_ptr_data &operator=(__uniq_ptr_data &&) = default;
};
template
struct __uniq_ptr_data<_Tp, _Dp, true, false> : __uniq_ptr_impl<_Tp, _Dp> {
using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
__uniq_ptr_data(__uniq_ptr_data &&) = default;
__uniq_ptr_data &operator=(__uniq_ptr_data &&) = delete;
};
template
struct __uniq_ptr_data<_Tp, _Dp, false, true> : __uniq_ptr_impl<_Tp, _Dp> {
using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
__uniq_ptr_data(__uniq_ptr_data &&) = delete;
__uniq_ptr_data &operator=(__uniq_ptr_data &&) = default;
};
template
struct __uniq_ptr_data<_Tp, _Dp, false, false> : __uniq_ptr_impl<_Tp, _Dp> {
using __uniq_ptr_impl<_Tp, _Dp>::__uniq_ptr_impl;
__uniq_ptr_data(__uniq_ptr_data &&) = delete;
__uniq_ptr_data &operator=(__uniq_ptr_data &&) = delete;
};
/// 20.7.1.2 unique_ptr for single objects.
template > class unique_ptr {
template
//如果不符合 __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint中的约束条件
//enable_if不会取到type这个类型,类型替换失败
using _DeleterConstraint =
typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
//内涵一个_uniq_ptr_data来实现unique_ptr
__uniq_ptr_data<_Tp, _Dp> _M_t;
public:
//简单的型别别名
using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
using element_type = _Tp; //被指向元素类型
using deleter_type = _Dp; //删除器类型
private:
// helper template for detecting a safe conversion from another unique_ptr
//帮助模板推到是否是安全的转换从另一个unique_ptr
template
using __safe_conversion_up =
//先推导两个unique_ptr的内部指针转换是否安全,再看_Up是否是一个数组(数组可以退化为指针,这样的转换是不安全的)
//约束_Up不能是一个数组
__and_::pointer, pointer>,
__not_>>;
public:
//关于unique_ptr的方法功能,可以看见已经有很好的说明了,所以这里我不再说明
// Constructors.
/// Default constructor, creates a unique_ptr that owns nothing.
//模板约束再这里使用,因为这里的删除器约束条件是impl中的约束条件::type
//相当于对std::enable_if取type
//如果删除器是一个指针类型,或者没有默认构造函数,会编译失败
template >
constexpr unique_ptr() noexcept : _M_t() {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an object of @c element_type
*
* The deleter will be value-initialized.
*/
//同上
template >
explicit unique_ptr(pointer __p) noexcept : _M_t(__p) {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an object of @c element_type
* @param __d A reference to a deleter.
*
* The deleter will be initialized with @p __d
*/
//这里做的删除器类型约束为:需要能拷贝构造
template >>
unique_ptr(pointer __p, const deleter_type &__d) noexcept
: _M_t(__p, __d) {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an object of @c element_type
* @param __d An rvalue reference to a (non-reference) deleter.
*
* The deleter will be initialized with @p std::move(__d)
*/
//这里做的删除器类型约束为:需要能拷贝构造
template >>
unique_ptr(
pointer __p,
//约束为不能是一个左值引用
__enable_if_t::value, _Del &&> __d) noexcept
: _M_t(__p, std::move(__d)) {}
//删除器类型是左值引用,并且对于传入的删除器是一个右值,是不允许的所以 =delete
template ::type>
unique_ptr(pointer,
__enable_if_t::value, _DelUnref &&>) =
delete;
//对于nullptr,也就是std::nullptr_t类型的构造函数例如unique_ptr p(nullptr);
/// Creates a unique_ptr that owns nothing.
template >
constexpr unique_ptr(nullptr_t) noexcept : _M_t() {}
/// Move constructor.
//仅仅是=default
unique_ptr(unique_ptr &&) = default;
/** @brief Converting constructor from another type
*
* Requires that the pointer owned by @p __u is convertible to the
* type of pointer owned by this object, @p __u does not own an array,
* and @p __u has a compatible deleter type.
*/
//正如注释所言,从一个unique_ptr构造一个unique_ptr,要求拥有的指针能够转换并且不是数组类型
//并且要求__u的删除器能够兼容this的删除器
template <
typename _Up, typename _Ep,
typename = _Require<
__safe_conversion_up<_Up, _Ep>, //看看内部指针能不能安全转换
typename conditional::value,
is_same<_Ep, _Dp>, //看看删除器是不是一个引用类型,是,需要相同类型
is_convertible<_Ep, _Dp>>::type>> //不是需要_Ep(__u的删除器类型)能够转换为_Dp
unique_ptr(unique_ptr<_Up, _Ep> &&__u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}
#if _GLIBCXX_USE_DEPRECATED
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/// Converting constructor from @c auto_ptr
//因为auto_ptr是废弃的特性,所以这个函数不说明了,有兴趣的话读者可以自己分析一下
template ,
is_same<_Dp, default_delete<_Tp>>>>
unique_ptr(auto_ptr<_Up> &&__u) noexcept;
#pragma GCC diagnostic pop
#endif
/// Destructor, invokes the deleter if the stored pointer is not null.
~unique_ptr() noexcept {
//如果删除器不是可调用的,会断言失败
//__is_invocable具体的实现,这里不说明
static_assert(__is_invocable::value,
"unique_ptr's deleter must be invocable with a pointer");
auto &__ptr = _M_t._M_ptr(); //先缓存指针
if (__ptr != nullptr) //如果不是空,使用删除器删除
get_deleter()(std::move(__ptr));
__ptr = pointer();
}
// Assignment.
/** @brief Move assignment operator.
*
* Invokes the deleter if this object owns a pointer.
*/
//同样的,简单是使用default
unique_ptr &operator=(unique_ptr &&) = default;
/** @brief Assignment from another type.
*
* @param __u The object to transfer ownership from, which owns a
* convertible pointer to a non-array object.
*
* Invokes the deleter if this object owns a pointer.
*/
//涉及到类型的转换,这个问题上面已经看到过了
//可以类似的如法炮制
template
typename enable_if<__and_<__safe_conversion_up<_Up, _Ep>,
is_assignable>::value,
unique_ptr &>::type
operator=(unique_ptr<_Up, _Ep> &&__u) noexcept {
reset(__u.release());
get_deleter() = std::forward<_Ep>(__u.get_deleter());
return *this;
}
/// Reset the %unique_ptr to empty, invoking the deleter if necessary.
//对于nullptr_t的赋值操作
unique_ptr &operator=(nullptr_t) noexcept {
reset();
return *this;
}
// Observers.
/// Dereference the stored pointer.
//对element_type添加一个左值引用
//在type_traits中定义的add_lvalue_reference的实现细节,在这里并不想多说
//我可以负责任的告诉你,add_lvalue_reference会对T, T&&变为T &,对T & 不变
typename add_lvalue_reference::type operator*() const {
__glibcxx_assert(get() != pointer());
return *get();
}
/// Return the stored pointer.
//->运算符,不用多说,看一下就懂
pointer operator->() const noexcept {
_GLIBCXX_DEBUG_PEDASSERT(get() != pointer());
return get();
}
/// Return the stored pointer.
pointer get() const noexcept { return _M_t._M_ptr(); }
/// Return a reference to the stored deleter.
deleter_type &get_deleter() noexcept { return _M_t._M_deleter(); }
/// Return a reference to the stored deleter.
const deleter_type &get_deleter() const noexcept {
return _M_t._M_deleter();
}
//隐式转换为bool,可以用在这种场景,例如
//unique_ptr a;
//if(a) {...}
/// Return @c true if the stored pointer is not null.
explicit operator bool() const noexcept {
return get() == pointer() ? false : true;
}
// Modifiers.
/// Release ownership of any stored pointer.
pointer release() noexcept { return _M_t.release(); }
/** @brief Replace the stored pointer.
*
* @param __p The new pointer to store.
*
* The deleter will be invoked if a pointer is already owned.
*/
void reset(pointer __p = pointer()) noexcept {
static_assert(__is_invocable::value,
"unique_ptr's deleter must be invocable with a pointer");
_M_t.reset(std::move(__p));
}
/// Exchange the pointer and deleter with another object.
void swap(unique_ptr &__u) noexcept {
//断言检查删除器是否右可交换的能力
static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
//调用_M_t的swap,实际上是__uniq_ptr_impl中的swap
_M_t.swap(__u._M_t);
}
// Disable copy from lvalue.
//拷贝构造和赋值运算符是删除的
unique_ptr(const unique_ptr &) = delete;
unique_ptr &operator=(const unique_ptr &) = delete;
};
对于非数组版本的unique_ptr我们已经分析完成了,下面是对于数组版本的特例化
相对于非数组版本,数组版本主要修改了
/// 20.7.1.3 unique_ptr for array objects with a runtime length
// [unique.ptr.runtime]
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 740 - omit specialization for array objects with a compile time length
// 对编译时期能确定长度的数组特例化
// 对于已经说过的问题,就不再写注释了,请读者自己注意
template class unique_ptr<_Tp[], _Dp> {
template
using _DeleterConstraint =
typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
__uniq_ptr_data<_Tp, _Dp> _M_t;
//就像remove_cv_t
template using __remove_cv = typename remove_cv<_Up>::type;
// like is_base_of<_Tp, _Up> but false if unqualified types are the same
// 从实现中和注释中就可以看出,需要判断Up是否是Tp的派生类
template
using __is_derived_Tp =
__and_,
__not_, __remove_cv<_Up>>>>;
public:
using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer;
using element_type = _Tp;
using deleter_type = _Dp;
// helper template for detecting a safe conversion from another
// unique_ptr
//辅助模板来帮助实现从另一个unique_ptr转换是否安全
//安全转换的条件是:
//是一个数组
//poniter和element_type*是同一类型
//另一个unique_ptr::pointer和::element_type*是同一个类型
//另一个unique_ptr::element_type(*)[]可以转换为当前的unique_ptr::element_type(*)[]
//至于为什么要这样设计,还是和用多态方式处理数组有关,下面会说到
template ,
typename _UP_pointer = typename _UPtr::pointer,
typename _UP_element_type = typename _UPtr::element_type>
using __safe_conversion_up =
__and_, is_same,
is_same<_UP_pointer, _UP_element_type *>,
is_convertible<_UP_element_type (*)[], element_type (*)[]>>;
// helper template for detecting a safe conversion from a raw pointer
// 辅助模板从裸指针转换为当前的unique_ptr::pointer
//这段代码实在是太难以阅读了,为了方便,我们进行一些格式上的改变
// template
// using __safe_conversion_raw = __and_<
// __or_<__or_, is_same<_Up, nullptr_t>>,
// __and_, is_same,
// is_convertible::type (*)[],
// element_type (*)[]>>>>;
//满足转换规则只需要满足其中以下条件之一(Up是一个raw-pointer)
//- Up和pointer是同类型或者Up和nullptr_t是同类型
//- Up是一个指针并且pointer和element_type*是同一类型并且Up移除指针后的类型的数组指针可以转换为element_type的数组指针
template
using _safe_conversion_raw = __and_<
__or_<
__or_, is_same >,
__and_<
is_pointer, is_same,
is_convertible::type (*)[], element_type (*)[]>
>
>
>;
// Constructors.
/// Default constructor, creates a unique_ptr that owns nothing.
template >
constexpr unique_ptr() noexcept : _M_t() {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an array of a type safely convertible
* to an array of @c element_type
*
* The deleter will be value-initialized.
*/
template <
typename _Up, typename _Vp = _Dp, typename = _DeleterConstraint<_Vp>,
typename =
typename enable_if<__safe_conversion_raw<_Up>::value, bool>::type> //做了一个类型约束
explicit unique_ptr(_Up __p) noexcept : _M_t(__p) {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an array of a type safely convertible
* to an array of @c element_type
* @param __d A reference to a deleter.
*
* The deleter will be initialized with @p __d
*/
//从一个raw-pointer和一个删除器构造一个uniuqe_ptr
//很明显的需要raw-pointer可以安全的转换为unique_ptr并且删除器是可以拷贝狗仔的
template , //只是类型约束和非数组版的不一样
is_copy_constructible<_Del>>>
unique_ptr(_Up __p, const deleter_type &__d) noexcept : _M_t(__p, __d) {}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an array of a type safely convertible
* to an array of @c element_type
* @param __d A reference to a deleter.
*
* The deleter will be initialized with @p std::move(__d)
*/
template ,
is_move_constructible<_Del>>>
unique_ptr(
_Up __p,
__enable_if_t::value, _Del &&> __d) noexcept
: _M_t(std::move(__p), std::move(__d)) {}
template ::type,
typename = _Require<__safe_conversion_raw<_Up>>>
unique_ptr(_Up,
__enable_if_t::value, _DelUnref &&>) =
delete;
/// Move constructor.
unique_ptr(unique_ptr &&) = default;
/// Creates a unique_ptr that owns nothing.
template >
constexpr unique_ptr(nullptr_t) noexcept : _M_t() {}
template <
typename _Up, typename _Ep,
typename = _Require<
__safe_conversion_up<_Up, _Ep>,
typename conditional::value, is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep> &&__u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}
/// Destructor, invokes the deleter if the stored pointer is not null.
~unique_ptr() {
auto &__ptr = _M_t._M_ptr();
if (__ptr != nullptr)
get_deleter()(__ptr);
__ptr = pointer();
}
// Assignment.
/** @brief Move assignment operator.
*
* Invokes the deleter if this object owns a pointer.
*/
unique_ptr &operator=(unique_ptr &&) = default;
/** @brief Assignment from another type.
*
* @param __u The object to transfer ownership from, which owns a
* convertible pointer to an array object.
*
* Invokes the deleter if this object owns a pointer.
*/
template
typename enable_if<__and_<__safe_conversion_up<_Up, _Ep>,
is_assignable>::value,
unique_ptr &>::type
operator=(unique_ptr<_Up, _Ep> &&__u) noexcept {
reset(__u.release());
get_deleter() = std::forward<_Ep>(__u.get_deleter());
return *this;
}
/// Reset the %unique_ptr to empty, invoking the deleter if necessary.
unique_ptr &operator=(nullptr_t) noexcept {
reset();
return *this;
}
// Observers.
// 重载的[],实现也是非常简单
/// Access an element of owned array.
typename std::add_lvalue_reference::type
operator[](size_t __i) const {
__glibcxx_assert(get() != pointer());
return get()[__i];
}
/// Return the stored pointer.
pointer get() const noexcept { return _M_t._M_ptr(); }
/// Return a reference to the stored deleter.
deleter_type &get_deleter() noexcept { return _M_t._M_deleter(); }
/// Return a reference to the stored deleter.
const deleter_type &get_deleter() const noexcept {
return _M_t._M_deleter();
}
/// Return @c true if the stored pointer is not null.
explicit operator bool() const noexcept {
return get() == pointer() ? false : true;
}
// Modifiers.
/// Release ownership of any stored pointer.
pointer release() noexcept { return _M_t.release(); }
/** @brief Replace the stored pointer.
*
* @param __p The new pointer to store.
*
* The deleter will be invoked if a pointer is already owned.
*/
template <
typename _Up,
typename = _Require<__or_<
is_same<_Up, pointer>,
__and_, is_pointer<_Up>,
is_convertible::type (*)[],
element_type (*)[]>>>>>
void reset(_Up __p) noexcept {
_M_t.reset(std::move(__p));
}
void reset(nullptr_t = nullptr) noexcept { reset(pointer()); }
/// Exchange the pointer and deleter with another object.
void swap(unique_ptr &__u) noexcept {
static_assert(__is_swappable<_Dp>::value, "deleter must be swappable");
_M_t.swap(__u._M_t);
}
// Disable copy from lvalue.
unique_ptr(const unique_ptr &) = delete;
unique_ptr &operator=(const unique_ptr &) = delete;
};
令人头大的部分终于过去了,占据了本文章绝大篇幅的那些部分。接下来放松一下,看一看std::make_unique
的实现吧
在这里仅仅分析14的部分(make_unique是14增添的),对于14以上的代码,读者有兴趣可以自行了解
做的功能就是类型萃取,用作于make_unique的返回值,这样做的目的也是为了实现类型安全
类型参数应为T的地方写成T[]会导致类型替换失败,反之亦然
template
struct _MakeUniq
{ typedef unique_ptr<_Tp> __single_object; };
template
struct _MakeUniq<_Tp[]>
{ typedef unique_ptr<_Tp[]> __array; };
template
struct _MakeUniq<_Tp[_Bound]>
{ struct __invalid_type { }; };
make_unique的实现部分
/// std::make_unique for single objects
// 非数组版本
template
inline typename _MakeUniq<_Tp>::__single_object
make_unique(_Args&&... __args) //&& ... +forward 的形式转发参数
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
/// std::make_unique for arrays of unknown bound
//返回一个unique_ptr指向一个长度为__num的数组
//关于remove_extent_t,请参考https://www.apiref.com/cpp-zh/cpp/types/remove_extent.html
template
inline typename _MakeUniq<_Tp>::__array
make_unique(size_t __num)
{ return unique_ptr<_Tp>(new remove_extent_t<_Tp>[__num]()); }
//不能从一个已知界限的数组返回一个unique_ptr
/// Disable std::make_unique for arrays of known bound
template
typename _MakeUniq<_Tp>::__invalid_type
make_unique(_Args&&...) = delete;
unique_ptr的源码分析,就告一段落了。还有很多很多细节和实现手法没有剖析,剖析了的细节和实现手法,一些也没有说明为什么要这样做。
如果日后发现的错误,会在这里更新勘误表。
如果你有任何的问题,可以通过邮箱,QQ来联系到我。