STL中的智能指针(Smart Pointer)及其源码剖析: std::unique_ptr
STL中的智能指针(Smart Pointer)及其源码剖析: std::unique_ptr
- 和
std::auto_ptr
一样,std::unique_ptr
也是一种智能指针,它也是通过指针的方式来管理对象资源,并且在unique_ptr
的生命期结束后释放该资源。 unique_ptr
持有对对象的独有权 —— 两个unique_ptr
不能指向一个对象,不能进行复制操作只能进行移动操作。- 当发生下列情况是,
unique_ptr
会所管理的指针使用其关联的deleter
:
a. 负责管理的unique_ptr
对象被销毁时;
b. 负责管理的unique_ptr
对象通过operator=
或reset
函数赋值给另一个指针。
一. unique_ptr 的使用
1. unique_ptr 的声明
// since C++11
template<class T, class Deleter = std::default_delete<T>> (1)
class unique_ptr;
template<class T, class Deleter> (2)
class unique_ptr<T[], Deleter>;
(1) 管理单个对象泛化的 unique_ptr
模板的声明。
(2) 对 (1) 版本, 针对对象数组进行特化后的版本的声明。
2. unique_ptr 的构造函数
unique_ptr<T>
的构造函数。
constexpr unique_ptr(); (1)
constexpr unique_ptr(nullptr_t);
explicit unique_ptr(pointer p); (2)
unique_ptr(pointer p, (3)
typename conditional<is_reference<Deleter>::value,
Deleter, const Deleter&>::type d);
unique_ptr(pointer p, (4)
typename remove_reference<Deleter>::type&& d);
unique_ptr(unique_ptr&& u); (5)
template<class U, class E> (6)
unique_ptr(unique_ptr<U, E>&& u);
template<class U> (7)
unique_ptr(auto_ptr<U>&& u);
(1) 构造一个没有管理任何资源的 std::unique_ptr
对象。 这里的 nullptr_t
是从 C++11
开始新增的类型, 表示空指针(nullptr
)的类型。
(2) 构造一个管理 p
指向资源的 std::unique_ptr
对象。
(3) 构造一个管理 p
指向资源的 std::unique_ptr
对象, 同时将释放资源的函数设置为 d
。
(4) 构造一个管理 p
指向资源的 std::unique_ptr
对象, 同时将释放资源的函数设置为 d
。[主要针对 (3) 的 d
是右值引用类型时的重载]
conditional: 在编译期,根据条件 B 进行
typedef
,template< bool B, class T, class F > struct conditional;
中, 如果 B 是true
, 则定义为T
类型, 如果 B 是false
, 则定义为F
类型。is_reference:在编译期,判断某个类型是否是引用类型。
remove_reference:在编译期移除某个类型的引用符号。如:
remove_reference<int&>::type
类型就是int
类型。(3)(4)中的
d
的类型推导结果列举如下:
a. 当Deleter
是非引用类型A
时,unique_ptr(pointer p, const A& d); (3) unique_ptr(pointer p, A&& d); (4)
b. 当
Deleter
是左值引用类型A&
时,unique_ptr(pointer p, A& d); (3) unique_ptr(pointer p, A&& d); (4)
c. 当
Deleter
是 常左值引用类型const A&
时,unique_ptr(pointer p, const A& d); (3) unique_ptr(pointer p, const A&& d); (4)
(5) 利用移动语义,解决了 auto_ptr
在构造函数上不足的地方。移动构造函数更能体现 unique_ptr
在赋值时, 对资源的管理权的移交。
(6) 与 (5) 类似。主要针对隐式类型转化的情况。
(7) 实现从 auto_ptr
到 unique_ptr
的构造或赋值。
-
unique_ptr<T[]>
的构造函数。
(略, 具体与unique_ptr<T>
类似,细节处有略微差异) -
例子(取自 cppreference.com)
#include <iostream>
#include <memory>
struct Foo { // object to manage
Foo() { std::cout << "Foo ctor\n"; }
Foo(const Foo&) { std::cout << "Foo copy ctor\n"; }
Foo(Foo&&) { std::cout << "Foo move ctor\n"; }
~Foo() { std::cout << "~Foo dtor\n"; }
};
struct D { // deleter
D() {};
D(const D&) { std::cout << "D copy ctor\n"; }
D(D&) { std::cout << "D non-const copy ctor\n";}
D(D&&) { std::cout << "D move ctor \n"; }
void operator()(Foo* p) const {
std::cout << "D is deleting a Foo\n";
delete p;
};
};
int main()
{
std::cout << "Example constructor(1)...\n";
std::unique_ptr<Foo> up1; // up1 is empty
std::unique_ptr<Foo> up1b(nullptr); // up1b is empty
std::cout << "\nExample constructor(2)...\n";
{
std::unique_ptr<Foo> up2(new Foo); //up2 now owns a Foo
} // Foo deleted
std::cout << "\nExample constructor(3)...\n";
D d;
{ // deleter type is not a reference
std::unique_ptr<Foo, D> up3(new Foo, d); // deleter copied
}
{ // deleter type is a reference
std::unique_ptr<Foo, D&> up3b(new Foo, d); // up3b holds a reference to d
}
std::cout << "\nExample constructor(4)...\n";
{ // deleter is not a reference
std::unique_ptr<Foo, D> up4(new Foo, D()); // deleter moved
}
std::cout << "\nExample constructor(5)...\n";
{
std::unique_ptr<Foo> up5a(new Foo);
std::unique_ptr<Foo> up5b(std::move(up5a)); // ownership transfer
}
std::cout << "\nExample constructor(6)...\n";
{
std::unique_ptr<Foo, D> up6a(new Foo, d); // D is copied
std::unique_ptr<Foo, D> up6b(std::move(up6a)); // D is moved
std::unique_ptr<Foo, D&> up6c(new Foo, d); // D is a reference
std::unique_ptr<Foo, D> up6d(std::move(up6c)); // D is copied
}
std::cout << "\nExample constructor(7)...\n";
{
std::auto_ptr<Foo> up7a(new Foo);
std::unique_ptr<Foo> up7b(std::move(up7a)); // ownership transfer
}
}
执行结果:
3. unique_ptr的析构函数: 销毁管理的对象。
~unique_ptr();
如果 *this
有管理的资源,则用 deleter
销毁该资源。
4. 拷贝赋值函数
unique_ptr<T>
的拷贝赋值函数。
unique_ptr& opertor=(unique_ptr&& r); (1)
template<class U, class E>
unique_ptr& operator=(unique_ptr<U, E>&& r); (2)
unique_ptr& operator=(nullptr_t); (3)
(1) 将 r
管理的资源的控制权移交给 *this
,本身有管理的资源,则先用 Deleter
释放该资源。[这里就是右值引用的好处了,曾记否,只有左值引用时代的 auto_ptr
实现这个操作是多么的复杂。]
(2) 类似于 (1), 不过是针对 可隐式转换为该类型的 r
。
(3) 相当于调用 reset
, 将管理资源的指针设为空。
-
unique_ptr<T[]>
的拷贝赋值函数。
(略, 具体与unique_ptr<T>
类似,细节处有略微差异) -
例子(改自 cppreference.com)
#include <iostream>
#include <memory>
struct Foo {
Foo() { std::cout << "Foo\n"; }
~Foo() { std::cout << "~Foo\n"; }
};
int main()
{
std::unique_ptr<Foo> p1;
{
std::cout << "Creating new Foo...\n";
std::unique_ptr<Foo> p2(new Foo);
// p1 = p2; // Error ! can't copy unique_ptr
//unique_ptr& opertor=(unique_ptr&& r);
p1 = std::move(p2);
std::cout << "About to leave inner block...\n";
// Foo instance will continue to live,
// despite p2 going out of scope
}
// unique_ptr& operator=(nullptr_t);
std::cout << "Creating new Foo...\n";
std::unique_ptr<Foo> p3(new Foo);
std::cout << "Before p3 = nullptr...\n";
p3 = nullptr;
std::cout << "After p3 = nullptr...\n";
std::cout << "About to leave program...\n";
}
执行结果:
Creating new Foo...
Foo
About to leave inner block...
Creating new Foo...
Foo
Before p3 = nullptr...
~Foo
After p3 = nullptr...
About to leave program...
~Foo
6. 其他成员函数(unique_ptr::release, unique_ptr::reset, unique_ptr::swap, unique_ptr::get, unique_ptr::get_deleter, unique_ptr::operator bool, unique_ptr::operator*、unique_ptr::operator->, )
主要针对 unique_ptr<T>
, 特化版本 unique_ptr<T>
与之类似。
pointer release(); (1)
void reset(pointer ptr = pointer()); (2)
void swap(unique_ptr& other); (3)
pointer get() const; (4)
Deleter& get_deleter(); (5)
const Deleter& get_deleter() const; (6)
explicit operator bool() const; (7)
typename std::add_lvalue_reference<T>::type (8)
operator*() const;
pointer operator->() const; (9)
(1) 移交出 *this
管理资源的指针。如果 *this
没有管理资源,则返回 nullptr。
(2) 设置 *this
管理 ptr
指向的资源, 如果 *this
本身有管理的资源,则先用 deleter
释放该资源。
(3) 将 *this
管理的资源和 other
管理的资源进行交换。
(4) 获取 *this
管理资源的指针,如果没有被管理的资源则返回 nullptr
。
(5) 获取 *this
绑定的 deleter
。
(6) 与 (5) 类似。针对常类型。
(7) 隐式转换函数。判断是否 *this
管理有资源,如果是,则返回 true
, 否则返回 false
。
关键字 explicit 用在隐式转换函数前,限制该隐式转换函数,使其不能进行 copy initialization。例如:
struct A { operator bool() const { return true; } }; struct B { explicit operator bool() const { return true; }; int main() { A a1; bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization B b2; if (b2) ; // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization }
(8) 提供类似于指针的接口。获取 *this
所管理的对象的左值引用。如果 *this
没有管理对象,则该未定义该行为。
(9) 提供类似于指针的接口。获取 *this
所管理的对象的指针。如果 *this
没有管理对象,则该未定义该行为。
- 例子
#include <iostream>
#include <memory>
#include <string>
using namespace std;
struct Foo {
Foo(string name = "Foo") : m_name(name)
{ std::cout << m_name << " is creating...\n"; }
~Foo() { std::cout << m_name << " is deleting...\n"; }
string m_name;
};
class FooDeleter{
public:
void Show() const { cout << "I'm FooDeleter..." << endl; }
void operator()(Foo* pf) const{
cout << "FooDeleter is deleting " << pf->m_name << endl;
delete pf;
}
};
int main()
{
{
// for realese()...
cout << "test for release()..." << endl << endl;
unique_ptr<Foo, FooDeleter> f1(new Foo("Foo f1"));
Foo* pf1 = f1.release(); // 将资源的管理权交给 pf1
cout << "before delete pf1..." << endl;
delete pf1;
cout << "after delete pf1..." << endl << endl;
}
cout << "--------------------------------" << endl << endl;
{
// for reset()...
cout << "test for reset()..." << endl << endl;
unique_ptr<Foo, FooDeleter> f2(new Foo("Foo f2"));
Foo* pf2 = new Foo("Foo pf2");
cout << "before f2.reset(pf2)..." << endl;
f2.reset(pf2);
cout << "after f2.reset(pf2)..." << endl << endl;
}
cout << "--------------------------------" << endl << endl;
{
// for swap()...
cout << "test for swap()..." << endl << endl;
unique_ptr<Foo, FooDeleter> f3(new Foo("Foo f3"));
unique_ptr<Foo, FooDeleter> f4(new Foo("Foo f4"), FooDeleter());
f3.swap(f4);
cout << "before f3.reset(nullptr)..." << endl;
f3.reset(nullptr);
cout << "after f3.reset(nullptr)..." << endl << endl;
cout << "before f4.reset(nullptr)..." << endl;
f4.reset(nullptr);
cout << "after f4.reset(nullptr)..." << endl << endl;
}
cout << "--------------------------------" << endl << endl;
{
// for get(), get_deleter(), operator bool(), operator*() and operator->()...
cout << "test for get(), get_deleter(), operator bool()" << endl
<< "operator*() and operator->()..." << endl << endl;
unique_ptr<Foo, FooDeleter> f5(new Foo("Foo f5"));
unique_ptr<Foo, FooDeleter> defaultF;
// for get()...
Foo* pf3 = f5.get();
cout << " f5.get()->m_name = " << pf3->m_name << endl;
// for get_deleter()...
FooDeleter& pd1 = f5.get_deleter();
pd1.Show();
// for operator bool()
if(f5) cout << "f5 == true" << endl;
if(!defaultF) cout << "defaultF == false" << endl;
// for operator*() and operator->()...
cout << "f5->m_name == " << f5->m_name << endl;
cout << "(*f5).m_name == " << (*f5).m_name << endl;
}
cout << "--------------------------------" << endl << endl;
}
运行结果:
test for release()...
Foo f1 is creating...
before delete pf1...
Foo f1 is deleting...
after delete pf1...
--------------------------------
test for reset()...
Foo f2 is creating...
Foo pf2 is creating...
before f2.reset(pf2)...
FooDeleter is deleting Foo f2
Foo f2 is deleting...
after f2.reset(pf2)...
FooDeleter is deleting Foo pf2
Foo pf2 is deleting...
--------------------------------
test for swap()...
Foo f3 is creating...
Foo f4 is creating...
before f3.reset(nullptr)...
FooDeleter is deleting Foo f4
Foo f4 is deleting...
after f3.reset(nullptr)...
before f4.reset(nullptr)...
FooDeleter is deleting Foo f3
Foo f3 is deleting...
after f4.reset(nullptr)...
--------------------------------
test for get(), get_deleter(), operator bool()
operator*() and operator->()...
Foo f5 is creating...
f5.get()->m_name = Foo f5
I'm FooDeleter...
f5 == true
defaultF == false
f5->m_name == Foo f5
(*f5).m_name == Foo f5
FooDeleter is deleting Foo f5
Foo f5 is deleting...
--------------------------------
二. unique_ptr 源码剖析(源码出自 Dev C++)
1. 辅助类 template<typename _Tp> struct default_delete
的源码
default_delete
是 unique_ptr
绑定的默认 deleter。下面解析的是其泛化版本的源码(限于篇幅,针对数组的特化版本就不赘述了)。(代码中保留了 Dev C++ 的注释)
/// Primary template of default_delete, used by unique_ptr
template<typename _Tp>
struct default_delete
{
/// Default constructor
constexpr default_delete() noexcept = default; (1)
/** @brief Converting constructor.
*
* Allows conversion from a deleter for arrays of another type, @p _Up,
* only if @p _Up* is convertible to @p _Tp*.
*/
template<typename _Up, typename = typename (2)
enable_if<is_convertible<_Up*, _Tp*>::value>::type>
default_delete(const default_delete<_Up>&) noexcept { }
/// Calls @c delete @p __ptr
void operator()(_Tp* __ptr) const (3)
{
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;
}
};
(1) deleter 的默认构造函数, 如果 unique_ptr
的构造器没有传入 deleter 对象作为参数,需要在其内部默认构造一个 deleter 对象。
(2) 拷贝构造函数。由于没有加 explicit
限定符, 因此可以用做隐式转换。由于 default_deleter
只是一个 function object
,没有数据成员,因此实际上拷贝操作为空。
(3)该 funtion object
的主体,主要任务就是 delete
掉传入的 __ptr
。
std::is_convertible 判断类型之间是否能成功转换的模板。
/// is_convertible template<typename _From, typename _To> (1) struct is_convertible :public __is_convertible_helper<_From, _To>::type { }; template<typename _From, typename _To, (2) bool = __or_<is_void<_From>, is_function<_To>, is_array<_To>>::value> struct __is_convertible_helper { typedef typename is_void<_To>::type type; }; template<typename _From, typename _To> class __is_convertible_helper<_From, _To, false> (3) { template<typename _To1>static void __test_aux(_To1); (3.1) template<typename _From1, typename _To1, (3.2) typename = decltype(__test_aux<_To1>(std::declval<_From1>()))> static true_type __test(int); template<typename, typename> (3.3) static false_type __test(...); public: typedef decltype(__test<_From, _To>(0)) type; (3.4) };
(1) 判断类型之间是否能成功转换的模板。实际上
is_convertible
只是一个对外的口类, 类型转换的识别主要由其父类__is_convertible_helper
完成。
(2) 限于篇幅,这里就不对__or_
,is_void
和is_array
的源码进行分析了。从顾名思义, 在模板参数列表内判断_From
是不是void
类型,_To
是不是函数类型, 或者_To
是不是数组类型。如果_From
是void
类型,则_To
只能为void
类型才能转换。 如果_To
是函数类型或者数组类型则不能实现转换。(type
类型是true_type
或false_type
类似,用于标识是否能完成转换。)
(3) 是 (2) 的特化版本。当 (2) 的默认模板参数识别出false_type
(该类的对象能隐式转换为false
, 有机会我会细讲其源码。), 就会进入 (3) 的特化版本。 其实 (2) 是针对特殊情况(_From
是void
类型,_To
是函数类型, 或_To
是数组类型)的, 而 (3) 才是针对一般类似是否能实现转换的判断。
利用编译器所谓的 SFINAE(Substitution Failure Is Not An Error) 技术, 实现对类型转换成功与否的判断。SFINAE, 即非模板函数具有最高优先权, 如果不存在匹配的非模板函数的话, 那么最匹配的和最特化的具有最高的优先权。
(3.1) 辅助函数声明。主要是利用静态模板函数声明, 实现在编译期获得其返回值类型(void
的类型) 。decltype(__test_aux<_To1>(std::declval<_From1>()))
就是利用decltype
函数实现获取其返回值类型(void
类型)。当然,这里要求_From1
和_To1
类型一致(模板函数的使用…), 如果不一致,则编译不通过,根据 SFINAE 查找下一个匹配的模板。
(3.2) 当传入第三个模板参数默认为decltype(__test_aux<_To1>(std::declval<_From1>()))
, 当_From1
和_To1
类型一致,则该参数为void
类型,如果不一致,则编译不通过,根据 SFINAE 查找下一个匹配的模板。
(3.3) 任意函数参数的两个模板参数的模板函数。
(3.4) 链接 (3.2) 和 (3.3) 的中枢。将type
定义为__test<_From, _To>(0)
的类型, 根据模板推演原则, 首先选择需要一个函数参数__test
函数的模板函数, 即 (3.2)。但是, (3.2) 能成功推导的前提是 "_From1
和_To1
类型一致"。 如果一致,则type
被定义为true_type
类型((3.2) 的返回值的类型)。 如果不一致, 则根据 SFINAE, 选择下一个较次的匹配项进行模板推演,即(3.3), 则定义type
为false_type
类型((3.3)的返回值类型)。
2. 辅助类 _Pointer
的源码(Dev C++ 实现的成员类, 非标准)
template <typename _Tp, typename _Dp = default_delete<_Tp> > (0)
class unique_ptr;
// use SFINAE to determine whether _Del::pointer exists
class _Pointer
{
template<typename _Up> (1)
static typename _Up::pointer __test(typename _Up::pointer*);
template<typename _Up> (2)
static _Tp* __test(...);
typedef typename remove_reference<_Dp>::type _Del; (3)
public:
typedef decltype(__test<_Del>(0)) type; (4)
};
(0) Dev C++ 中 unique_ptr
的声明。
(1) 如果 _Up::pointer
不存在, 则该声明不会被推导。
(2) 与 (1) 利用 SFINAE
一起判断 _Up::pointer
是否存在。
(3) 将移除引用的 _Dp
类型定义为 _Del
。
(4) 判断 _Del::pointer
是否存在的中枢。将 __test<_Del>(0)
的返回值类型定义为 type
。 根据模板推导原则, 首先选择含有一个函数参数的模板 (1), 如果 _Up::pointer
存在,则推导成功, 将 type
定义为 _Up::pointer
类型((1) 的返回值类型); 如果 _Up::pointer
不存在,根据 SFINAE, 编译器会接着选择较次的匹配, 即(2), 将 type
定义为 _Tp*
((2)的返回值类型)。
题外话: 为什么不直接让 把
type
设为_Tp*
呢?
答: 因为可能管理该资源的不是原始的指针, 而是用户定义的类似于指针的东西。如, 智能指针。
3. unique_ptr
的成员变量和成员类型
private:
typedef std::tuple<typename _Pointer::type, _Dp> __tuple_type; (1)
__tuple_type _M_t;
public:
typedef typename _Pointer::type pointer; (2)
typedef _Tp element_type; (3)
typedef _Dp deleter_type; (4)
(1) 定义 tuple
的二元组类 __tuple_type
, 存放所管理的资源的指针以及 deleter 对象。 并定义其对象 _M_t
。
(2) 所管理资源的指针的类型。
(3) 所管理资源的了类型。
(4) 负责销毁资源的 deleter 的类型。
题外话: 多元组
std::tuple
是用来存放任意数量不同类型的数据的集合。可以通过get<i>(tup)
来获取tup
的第i
个元素。关于它的源码, 希望后续有时间能专门写博客来详细解读。
4. unique_ptr
构造函数的源码
// Constructors.
/// Default constructor, creates a unique_ptr that owns nothing.
constexpr unique_ptr() (1)
: _M_t(){}
/** Takes ownership of a pointer.
*
* @param __p A pointer to an object of @c element_type
*
* The deleter will be value-initialized.
*/
explicit unique_ptr(pointer __p) (2)
: _M_t(__p, deleter_type()) { }
/** 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
*/
unique_ptr(pointer __p, (3)
typename conditional<is_reference<deleter_type>::value,
deleter_type, const deleter_type&>::type __d)
: _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 deleter.
*
* The deleter will be initialized with @p std::move(__d)
*/
unique_ptr(pointer __p, (4)
typename remove_reference<deleter_type>::type&& __d)
: _M_t(std::move(__p), std::move(__d)) { }
/// Creates a unique_ptr that owns nothing.
constexpr unique_ptr(nullptr_t) : unique_ptr() { } (5)
// Move constructors.
/// Move constructor.
unique_ptr(unique_ptr&& __u) (6)
: _M_t(__u.release(),
std::forward<deleter_type>(__u.get_deleter())) { }
/** @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.
*/
template<typename _Up, typename _Ep, (7)
typename = _Require<is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>, __not_<is_array<_Up>>,
typename conditional<is_reference<_Dp>::value, is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u)
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }
#if _GLIBCXX_USE_DEPRECATED
/// Converting constructor from @c auto_ptr
template<typename _Up, (8)
typename = _Require<is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
unique_ptr(auto_ptr<_Up>&& __u);
#endif
(1) 默认构造函数。只是创建 unique_ptr
对象, 并没有管理资源。
(2) 构造管理 __p
指向的资源的构造函数。 并将其 deleter 设为 deleter_type
默认构造的 deleter。
(3) 这个构造函数的声明在 unique_ptr
的使用部分已经介绍过了。构造管理指针 __p
指向的资源, 并且设置其 deleter 为 __d
的构造函数。
(4) 这个构造函数的声明也在 unique_ptr
的使用部分已经介绍过了。 与 (3) 类似,不过是针对 __d
是右值引用的情况。关于 std::move的相关知识,以后有机会细讲。
(5) 构造不管理任何资源的 unique_ptr
对象。
(6) 移动构造函数。与 auto_ptr
类似, unique_ptr
不与他人共同管理资源。实际上只需要实现其移动构造函数即可。std::forward 使其参数按照原来的值类型传递。
(7) 用满足要求的能转换为 *this
管理类型的资源的 __u
, 移动构造或隐式转换为 *this
。
(8) 如果还有 auto_ptr
可用的话, 就加上从 auto_ptr
到 unique_ptr
的移动构造(隐式转换)函数。
5. unique_ptr
析构函数
/// Destructor, invokes the deleter if the stored pointer is not null.
~unique_ptr()
{
auto& __ptr = std::get<0>(_M_t); (1)
if (__ptr != nullptr) (2)
get_deleter()(__ptr);
__ptr = pointer(); (3)
}
(1) _M_t
内依次是 *this
管理的资源和绑定的 deleter。所以 ptr实际上获得的是 *this
管理资源的指针的引用。
(2) 如果 __ptr != nullptr
, 即 *this
有管理的对象, 则用 deleter 将该资源销毁。
(3) 重置管理指针。
6. unique_ptr
移动赋值函数
// Assignment.
/** @brief Move assignment operator.
*
* @param __u The object to transfer ownership from.
*
* Invokes the deleter first if this object owns a pointer.
*/
unique_ptr& operator=(unique_ptr&& __u) (1)
{
reset(__u.release());
get_deleter() = std::forward<deleter_type>(__u.get_deleter());
return *this;
}
/** @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 first if this object owns a pointer.
*/
template<typename _Up, typename _Ep> (2)
typename enable_if< __and_<
is_convertible<typename unique_ptr<_Up, _Ep>::pointer, pointer>,
__not_<is_array<_Up>>
>::value,
unique_ptr&>::type operator=(unique_ptr<_Up, _Ep>&& __u)
{
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) (3)
{
reset();
return *this;
}
(1) 移动赋值函数。相较于 auto_ptr
复杂的实现, 有移动语义的 unique_ptr
的赋值函数更加简洁。 为了防止自我赋值产生奇怪错误, 这里用 __u.release()
函数返回控制资源的指针, 然后 reset
给 *this
。
(2) 针对可转化为 *this
的 unique_ptr
的重载版本, 实现细节与 (1) 类似。
(3) 用 nullptr
赋值给 *this
。 实际上就是将 *this
管理的资源清空。
7. unique_ptr
其他函数
/// Dereference the stored pointer.
typename add_lvalue_reference<element_type>::type operator*() const (1)
{
return *get();
}
/// Return the stored pointer.
pointer operator->() const (2)
{
return get();
}
/// Return the stored pointer.
pointer get() const (3)
{ return std::get<0>(_M_t); }
/// Return a reference to the stored deleter.
deleter_type& get_deleter() (4)
{ return std::get<1>(_M_t); }
/// Return a reference to the stored deleter.
const deleter_type& get_deleter() const (4)
{ return std::get<1>(_M_t); }
/// Return @c true if the stored pointer is not null.
explicit operator bool() const (5)
{ return get() == pointer() ? false : true; }
/// Release ownership of any stored pointer.
pointer release() (6)
{
pointer __p = get();
std::get<0>(_M_t) = pointer();
return __p;
}
/** @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()) (7)
{
using std::swap;
swap(std::get<0>(_M_t), __p);
if (__p != pointer())
get_deleter()(__p);
}
/// Exchange the pointer and deleter with another object.
void swap(unique_ptr& __u) (8)
{
using std::swap;
swap(_M_t, __u._M_t);
}
// Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete; (9)
unique_ptr& operator=(const unique_ptr&) = delete;
(1) 重载 operator*()
。通过 get()
函数获取 *this
管理的资源的引用。
(2) 重载 operator->()
。通过 get()
函数获取 *this
管理的资源的指针。
(3) 从二元组 std::tuple<typename _Pointer::type, _Dp>
中获取 *this
管理的资源的指针。
(4) 从二元组 std::tuple<typename _Pointer::type, _Dp>
中获取 *this
绑定的 deleter 的 (常) 引用。
(5) 隐式转换为布尔类型的函数。前面有解释过 explicit
的作用。用于判断 *this
是否有管理的资源。
(6) 移交出 *this
管理的资源。 先让 __p
保存 *this
管理的资源, 然后让 *this
的 管理指针置零, 最后返回 __p
。
(7) 更改 *this
管理的资源。为了防止 __p == this
时错误的销毁了该资源, 用 swap
函数交换资源, 然后用 deleter 删除交换给原始指针的资源。
(8) 交换两个 unique_ptr
所管理的资源。 实际上就是交换其二元组 std::tuple<typename _Pointer::type, _Dp>
。
(9) 由于 unique_ptr
要求必须单独管理资源, 不能同时管理资源, 因此拷贝赋值函数实际上是没有意义的, 因此需要删除。
三. 总结
unique_ptr
的功能与 auto_ptr
类似, 都是按照 RAII 原则, 实现单独对资源的管理。 相较于 auto_ptr
, unique_ptr
更进一步的是: unique_ptr
有右值引用以及强大的C++新特性的加持, 因此在很多实现上更加的完美; unique_ptr
可以设置销毁资源的 deleter, 这就使得它不仅可以管理内存资源, 也可以管理其它的需要释放的资源(需要设置对应的deleter)。
四. 参考文献
- cppreference.com
- Lippman 等著, 王刚等译《C++ Primer(第五版)》
- Dec C++ 源码
五. 推荐阅读
- 关于 auto_ptr: STL中的智能指针(Smart Pointer)及其源码剖析: std::auto_ptr
- 关于 forward: forward在委托机制中的应用——完美转发