C++智能指针的一种实现(转载)

C++智能指针的一种实现(转载)

智能指针其实就是 RAII 资源管理功能的自然展现

零、一个资源管理的简单包装类

使用智能指针,可以简化资源的管理,从根本上消除资源(包括内存)泄漏的可能性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class shape_wrapper 
{
public:
explicit shape_wrapper(shape* ptr = nullptr)
: ptr_(ptr) {}

~shape_wrapper()
{
delete ptr_;
}

shape* get() const { return ptr_; }

private:
shape* ptr_;
};

这个类可以完成智能指针的最基本的功能:对超出作用域的对象进行释放。但这个包装类有以下局限

  1. 这个类只适用于 shape 类
  2. 该类对象的行为不够像指针
  3. 拷贝该类对象会引发程序行为异常

一、模板化和易用性

要让这个类能够包装任意类型的指针,我们需要把它变成一个类模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename T>
class smart_ptr {
public:
explicit smart_ptr(T* ptr = nullptr)
: ptr_(ptr) {}

~smart_ptr()
{
delete ptr_;
}

T* get() const { return ptr_; }

private:
T* ptr_;
};

和 shape_wrapper 比较一下,我们就是在开头增加模板声明 template ,然后把代码中的 shape 替换成模板参数 T 而已。这个模板使用也很简单,把原来的 shape_wrapper 改成 smart_ptr< shape > 就行。

目前这个 smart_ptr 的行为还是和指针有点差异的:

  • 它不能用 * 运算符解引用
  • 它不能用 -> 运算符指向对象成员
  • 它不能像指针一样用在布尔表达式里

这些问题也相当容易解决,加几个 operator重载运算符 成员函数就可以:

1
2
3
4
5
6
7
8
template <typename T>
class smart_ptr {
public:

T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
}

二、拷贝构造和赋值

拷贝构造和赋值,我们暂且简称为拷贝,这是个比较复杂的问题了。关键还不是实现问题,而是我们该如何定义其行为。假设有下面的代码:

1
2
smart_ptr<shape> ptr1{create_shape(shape_type::circle)};
smart_ptr<shape> ptr2{ptr1};

对于第二行,究竟应当让编译时发生错误,还是可以有一个更合理的行为?我们来逐一检查一下各种可能性。

最简单的情况显然是禁止拷贝。我们可以使用下面的代码:

1
2
3
4
5
6
7
8
9
template <typename T>
class smart_ptr {

smart_ptr(const smart_ptr&) // 禁用拷贝构造
= delete;
smart_ptr& operator=(const smart_ptr&) // 禁用赋值构造
= delete;

};

禁用这两个函数非常简单,但却解决了一种可能出错的情况。否则,smart_ptr ptr2{ptr1}; 在编译时不会出错,但在运行时却会有未定义行为——由于会对同一内存释放两次,通常情况下会导致程序崩溃。

我们是不是可以考虑在拷贝智能指针时把对象拷贝一份?不行,通常人们不会这么用,因为使用智能指针的目的就是要减少对象的拷贝啊。何况,虽然我们的指针类型是 shape,但实际指向的却应该是 circle 或 triangle 之类的对象。在 C++ 里没有像 Java 的 clone 方法这样的约定;一般而言,并没有通用的方法可以通过基类的指针来构造出一个子类的对象来。

我们要么试试在拷贝时转移指针的所有权?大致实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
template <typename T>
class smart_ptr {

smart_ptr(smart_ptr& other)
{
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr& rhs)
{
smart_ptr(rhs).swap(*this);
return *this;
}

T* release()
{
T* ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(smart_ptr& rhs)
{
using std::swap;
swap(ptr_, rhs.ptr_);
}

};

在拷贝构造函数中,通过调用 other 的 release 方法来释放它对指针的所有权。在赋值函数中,则通过拷贝构造产生一个临时对象并调用 swap 来交换对指针的所有权。实现上是不复杂的。

如果你学到的赋值函数还有一个类似于 if (this != &rhs) 的判断的话,那种用法更啰嗦,而且异常安全性不够好——如果在赋值过程中发生异常的话,this 对象的内容可能已经被部分破坏了,对象不再处于一个完整的状态。

上面代码里的这种惯用法(见参考资料 [1])则保证了强异常安全性:赋值分为拷贝构造和交换两步,异常只可能在第一步发生;而第一步如果发生异常的话,this 对象完全不受任何影响。无论拷贝构造成功与否,结果只有赋值成功和赋值没有效果两种状态,而不会发生因为赋值破坏了当前对象这种场景。

如果你觉得这个实现还不错的话,那恭喜你,你达到了 C++ 委员会在 1998 年时的水平:上面给出的语义本质上就是 C++98 的 auto_ptr 的定义。如果你觉得这个实现很别扭的话,也恭喜你,因为 C++ 委员会也是这么觉得的:auto_ptr 在 C++17 时已经被正式从 C++ 标准里删除了。

上面实现的最大问题是,它的行为会让程序员非常容易犯错。一不小心把它传递给另外一个 smart_ptr,你就不再拥有这个对象了……

三、移动指针?

先简单看一下 smart_ptr 可以如何使用“移动”来改善其行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
class smart_ptr {

smart_ptr(smart_ptr&& other)
{
ptr_ = other.release();
}
smart_ptr& operator=(smart_ptr rhs)
{
rhs.swap(*this);
return *this;
}

};

看到修改的地方了吗?改了两个地方:

  • 把拷贝构造函数中的参数类型 smart_ptr& 改成了 smart_ptr&&;现在它成了移动构造函数。
  • 把赋值函数中的参数类型 smart_ptr& 改成了 smart_ptr,在构造参数时直接生成新的智能指针,从而不再需要在函数体中构造临时对象。现在赋值函数的行为是移动还是拷贝,完全依赖于构造参数时走的是移动构造还是拷贝构造。

根据 C++ 的规则,如果我提供了移动构造函数而没有手动提供拷贝构造函数,那后者自动被禁用(记住,C++ 里那些复杂的规则也是为方便编程而设立的)。于是,我们自然地得到了以下结果:

1
2
3
4
5
6
smart_ptr<shape> ptr1{create_shape(shape_type::circle)};
smart_ptr<shape> ptr2{ptr1}; // 编译出错
smart_ptr<shape> ptr3;
ptr3 = ptr1; // 编译出错
ptr3 = std::move(ptr1); // OK,可以
smart_ptr<shape> ptr4{std::move(ptr3)}; // OK,可以

这也是 C++11 的 unique_ptr 的基本行为。

四、子类指针向基类指针的转换

不知道你注意到没有,一个 circle* 是可以隐式转换成 shape* 的,但上面的 smart_ptr< circle > 却无法自动转换成 smart_ptr< shape >。这个行为显然还是不够“自然”。

不过,只需要额外加一点模板代码,就能实现这一行为。在我们目前给出的实现里,只需要增加一个构造函数即可——这也算是我们让赋值函数利用构造函数的好处了。

1
2
3
4
5
template <typename U>
smart_ptr(smart_ptr<U>&& other)
{
ptr_ = other.release();
}

这样,我们自然而然利用了指针的转换特性:现在 smart_ptr< circle > 可以移动给 smart_ptr< shape >,但不能移动给 smart_ptr< triangle >。不正确的转换会在代码编译时直接报错。

需要注意,上面这个构造函数不被编译器看作移动构造函数,因而不能自动触发删除拷贝构造函数的行为。如果我们想消除代码重复、删除移动构造函数的话,就需要把拷贝构造函数标记成 = delete 了。不过,更通用的方式仍然是同时定义标准的拷贝 / 移动构造函数和所需的模板构造函数。下面的引用计数智能指针里我们就需要这么做。

至于非隐式的转换,因为本来就是要写特殊的转换函数的,我们留到这一讲的最后再讨论。

五、引用计数

unique_ptr 算是一种较为安全的智能指针了。但是,一个对象只能被单个 unique_ptr 所拥有,这显然不能满足所有使用场合的需求。一种常见的情况是,多个智能指针同时拥有一个对象;当它们全部都失效时,这个对象也同时会被删除。这也就是 shared_ptr 了。

unique_ptr 和 shared_ptr 的主要区别如下图所示:

多个不同的 shared_ptr 不仅可以共享一个对象,在共享同一对象时也需要同时共享同一个计数。当最后一个指向对象(和共享计数)的 shared_ptr 析构时,它需要删除对象和共享计数。我们下面就来实现一下。

我们先来写出共享计数的接口:

1
2
3
4
5
6
7
class shared_count {
public:
shared_count();
void add_count();
long reduce_count();
long get_count() const;
};

这个 shared_count 类除构造函数之外有三个方法:一个增加计数,一个减少计数,一个获取计数。注意上面的接口增加计数不需要返回计数值;但减少计数时需要返回计数值,以供调用者判断是否它已经是最后一个指向共享计数的 shared_ptr 了。由于真正多线程安全的版本需要用到我们目前还没学到的知识,我们目前先实现一个简单化的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class shared_count {
public:
shared_count() : count_(1) {}
void add_count()
{
++count_;
}
long reduce_count()
{
return --count_;
}
long get_count() const
{
return count_;
}

private:
long count_;
};

现在我们可以实现我们的引用计数智能指针了。首先是构造函数、析构函数和私有成员变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
template <typename T>
class smart_ptr {
public:
explicit smart_ptr(T* ptr = nullptr)
: ptr_(ptr)
{
if (ptr) {
shared_count_ =
new shared_count();
}
}
~smart_ptr()
{
if (ptr_ &&
!shared_count_
->reduce_count()) {
delete ptr_;
delete shared_count_;
}
}

private:
T* ptr_;
shared_count* shared_count_;
};

构造函数跟之前的主要不同点是会构造一个 shared_count 出来。析构函数在看到 ptr_ 非空时(此时根据代码逻辑,shared_count 也必然非空),需要对引用数减一,并在引用数降到零时彻底删除对象和共享计数。原理就是这样,不复杂。

当然,我们还有些细节要处理。为了方便实现赋值(及其他一些惯用法),我们需要一个新的 swap 成员函数:

1
2
3
4
5
6
7
void swap(smart_ptr& rhs)
{
using std::swap;
swap(ptr_, rhs.ptr_);
swap(shared_count_,
rhs.shared_count_);
}

赋值函数可以跟前面一样,保持不变,但拷贝构造和移动构造函数是需要更新一下的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
smart_ptr(const smart_ptr& other)
{
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}
template <typename U>
smart_ptr(const smart_ptr<U>& other)
{
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}
template <typename U>
smart_ptr(smart_ptr<U>&& other)
{
ptr_ = other.ptr_;
if (ptr_) {
shared_count_ =
other.shared_count_;
other.ptr_ = nullptr;
}
}

除复制指针之外,对于拷贝构造的情况,我们需要在指针非空时把引用数加一,并复制共享计数的指针。对于移动构造的情况,我们不需要调整引用数,直接把 other.ptr_ 置为空,认为 other 不再指向该共享对象即可。

不过,上面的代码有个问题:它不能正确编译。编译器会报错,像:

1
fatal error: ‘ptr_’ is a private member of ‘smart_ptr’

错误原因是模板的各个实例间并不天然就有 friend 关系,因而不能互访私有成员 ptr_ 和 shared_count_。我们需要在 smart_ptr 的定义中显式声明:

1
2
template <typename U>
friend class smart_ptr;

此外,我们之前的实现(类似于单一所有权的 unique_ptr )中用 release 来手工释放所有权。在目前的引用计数实现中,它就不太合适了,应当删除。但我们要加一个对调试非常有用的函数,返回引用计数值。定义如下:

1
2
3
4
5
6
7
8
9
long use_count() const
{
if (ptr_) {
return shared_count_
->get_count();
} else {
return 0;
}
}

这就差不多是一个比较完整的引用计数智能指针的实现了。我们可以用下面的代码来验证一下它的功能正常:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class shape {
public:
virtual ~shape() {}
};

class circle : public shape {
public:
~circle() { puts("~circle()"); }
};

int main()
{
smart_ptr<circle> ptr1(new circle());
printf("use count of ptr1 is %ld\n",
ptr1.use_count());
smart_ptr<shape> ptr2;
printf("use count of ptr2 was %ld\n",
ptr2.use_count());
ptr2 = ptr1;
printf("use count of ptr2 is now %ld\n",
ptr2.use_count());
if (ptr1) {
puts("ptr1 is not empty");
}
}

这段代码的运行结果是:

use count of ptr1 is 1
use count of ptr2 was 0
use count of ptr2 is now 2
ptr1 is not empty
~circle()

六、指针类型转换

对应于 C++ 里的不同的类型强制转换:

  • static_cast
  • reinterpret_cast
  • const_cast
  • dynamic_cast

智能指针需要实现类似的函数模板。实现本身并不复杂,但为了实现这些转换,我们需要添加构造函数,允许在对智能指针内部的指针对象赋值时,使用一个现有的智能指针的共享计数。如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
template <typename U>
smart_ptr(const smart_ptr<U>& other,
T* ptr)
{
ptr_ = ptr;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}

这样我们就可以实现转换所需的函数模板了。下面实现一个 dynamic_pointer_cast 来示例一下:

1
2
3
4
5
6
7
8
template <typename T, typename U>
smart_ptr<T> dynamic_pointer_cast(
const smart_ptr<U>& other)
{
T* ptr =
dynamic_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}

在前面的验证代码后面我们可以加上:

1
2
3
4
smart_ptr<circle> ptr3 =
dynamic_pointer_cast<circle>(ptr2);
printf("use count of ptr3 is %ld\n",
ptr3.use_count());

编译会正常通过,同时能在输出里看到下面的结果:

use count of ptr3 is 3

最后,对象仍然能够被正确删除。这说明我们的实现是正确的。

七、代码列表

为了方便你参考,下面我给出了一个完整的 smart_ptr 代码列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#include <utility>  // std::swap

class shared_count {
public:
shared_count() noexcept
: count_(1) {}
void add_count() noexcept
{
++count_;
}
long reduce_count() noexcept
{
return --count_;
}
long get_count() const noexcept
{
return count_;
}

private:
long count_;
};

template <typename T>
class smart_ptr {
public:
template <typename U>
friend class smart_ptr;

explicit smart_ptr(T* ptr = nullptr)
: ptr_(ptr)
{
if (ptr) {
shared_count_ =
new shared_count();
}
}
~smart_ptr()
{
if (ptr_ &&
!shared_count_
->reduce_count()) {
delete ptr_;
delete shared_count_;
}
}

smart_ptr(const smart_ptr& other)
{
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}
template <typename U>
smart_ptr(const smart_ptr<U>& other) noexcept
{
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
template <typename U>
smart_ptr(smart_ptr<U>&& other) noexcept
{
ptr_ = other.ptr_;
if (ptr_) {
shared_count_ =
other.shared_count_;
other.ptr_ = nullptr;
}
}
template <typename U>
smart_ptr(const smart_ptr<U>& other,
T* ptr) noexcept
{
ptr_ = ptr;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}
smart_ptr&
operator=(smart_ptr rhs) noexcept
{
rhs.swap(*this);
return *this;
}

T* get() const noexcept
{
return ptr_;
}
long use_count() const noexcept
{
if (ptr_) {
return shared_count_
->get_count();
} else {
return 0;
}
}
void swap(smart_ptr& rhs) noexcept
{
using std::swap;
swap(ptr_, rhs.ptr_);
swap(shared_count_,
rhs.shared_count_);
}

T& operator*() const noexcept
{
return *ptr_;
}
T* operator->() const noexcept
{
return ptr_;
}
operator bool() const noexcept
{
return ptr_;
}

private:
T* ptr_;
shared_count* shared_count_;
};

template <typename T>
void swap(smart_ptr<T>& lhs,
smart_ptr<T>& rhs) noexcept
{
lhs.swap(rhs);
}

template <typename T, typename U>
smart_ptr<T> static_pointer_cast(
const smart_ptr<U>& other) noexcept
{
T* ptr = static_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}

template <typename T, typename U>
smart_ptr<T> reinterpret_pointer_cast(
const smart_ptr<U>& other) noexcept
{
T* ptr = reinterpret_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}

template <typename T, typename U>
smart_ptr<T> const_pointer_cast(
const smart_ptr<U>& other) noexcept
{
T* ptr = const_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}

template <typename T, typename U>
smart_ptr<T> dynamic_pointer_cast(
const smart_ptr<U>& other) noexcept
{
T* ptr = dynamic_cast<T*>(other.get());
return smart_ptr<T>(other, ptr);
}

如果你足够细心的话,你会发现我在代码里加了不少 noexcept。这对这个智能指针在它的目标场景能正确使用是十分必要的。

八、内容小结

这一讲我们从 shape_wrapper 出发,实现了一个基本完整的带引用计数的智能指针。这个智能指针跟标准的 shared_ptr 比,还缺了一些东西(见参考资料 [2]),但日常用到的智能指针功能已经包含在内。现在,你应当已经对智能指针有一个较为深入的理解了。

九、拓展延伸

shared_ptr() 一种简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
typename<T>
class shared_ptr
{
// 指针成员
T* _ptr = nullptr;

// 用指针,一般是 new 出来的话,这样在进程的堆区,那么这样其实也就实现了计数共享
uint32_t* _count = 0;

// 拷贝构造
shared_ptr(const shared_ptr& other)
{
_ptr = other->_ptr;
count = other->_count;
++(*_count);
}

shared_ptr(T* ptr)
{
_ptr = ptr;
if(ptr)
{
_count = new uint32_t();
_count = 0;
}
}

// 赋值构造
shared_ptr operator = (const shared_ptr& other)
{
if(_ptr == this)
{
// 如果是自赋值,则不处理
}
else
{
// 要变更管理的指针对象了
// 如果当前不为空,说明有管理的指针
if(this != nullptr)
{
--(*_count);
if(*_count == 0)
{
delete _ptr;
delete _count;
}
}
// 否则未初始化的,则进行赋值
else
{
_ptr = other->_ptr;
_count = other->_count;
++(*_count);
}
}
}

// 析构函数
~shared_ptr()
{
if(_ptr)
{
// 共享技术是正值
if(*_count > 0)
{
--(*_count);
}
// 计数减少为0了
else
{
delete _ptr;
delete _count;
}
}
}
};

typename<T>
shared_ptr<T> make_shared()
{
return ret(new T());
}

weak_ptr() 和 shared_ptr() 另一种简单实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// 指针表
template<typename T>
class ptr_table
{
protected:
static std::unordered_map<T*, size_t> table_;
};

// 初始化
template<typename T>
std::unordered_map<T*, size_t> ptr_table<T>::table_;

// shared_ptr 实现
template<typename T>
class shared_ptr : public ptr_table<T> {
private:
T* ptr_;
using ptr_table<T>::table_;
public:
explicit shared_ptr( T* ptr = nullptr ) noexcept : ptr_(ptr) { table_[ptr] = 1u; }

void reset( T* ptr = nullptr ) noexcept {
if(--table_[ptr_] == 0u){
table_.erase(ptr_);
delete ptr_;
}
ptr_ = ptr;
table_[ptr] = 1u;
}

~shared_ptr() noexcept {
if(--table_[ptr_] == 0u){
table_.erase(ptr_);
delete ptr_;
}
}

shared_ptr<T>& operator=(shared_ptr<T> const& other) noexcept {
if(--table_[ptr_] == 0u){
table_.erase(ptr_);
delete ptr_;
}
ptr_ = other.ptr_;
++table_[ptr_];
return *this;
}

shared_ptr<T>& operator=(shared_ptr<T>&& other) noexcept {
std::swap(ptr_, other.ptr_);
return *this;
}

operator bool() const noexcept { return ptr_ == nullptr; }

T* get() const noexcept { return ptr_; }

T operator*() const noexcept { return *ptr_; }

T* operator->() const noexcept { return ptr_; }

size_t use_count() const noexcept { return table_[ptr_]; }

bool unique() const noexcept { return table_[ptr_] == 1u; }

void swap(shared_ptr<T>& other) noexcept {
std::swap(ptr_, other.ptr_);
}

};

// weak_ptr 实现
template <typename T>
class weak_ptr : public ptr_table<T> {
private:
T* ptr_;
using ptr_table<T>::table_;
public:
explicit weak_ptr( shared_ptr<T> const& other = shared_ptr<T>() ) noexcept : ptr_(other.get()) {}

void reset( shared_ptr<T> const& other = shared_ptr<T>() ) noexcept { ptr_ = other.get(); }

~weak_ptr() noexcept = default;

weak_ptr& operator=(weak_ptr<T> const& other) noexcept {
ptr_ = other.ptr_;
return *this;
}

weak_ptr& operator=(shared_ptr<T> const& other) noexcept {
ptr_ = other.get();
return *this;
}

weak_ptr& operator=(weak_ptr<T>&& other) noexcept {
std::swap(ptr_,other.ptr_);
return *this;
}

size_t use_count() const noexcept { return table_[ptr_]; }

bool expired() const noexcept { return table_.find(ptr_) == table_.end(); }

shared_ptr<T> lock() const noexcept {
return shared_ptr<T>(ptr_);
}

void swap(weak_ptr<T>& other) noexcept {
std::swap(ptr_,other.ptr_);
}

};

一种 shared_ptr 的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#pragma once

#include <functional>

// 模仿shared_ptr实现一个智能指针
template <typename T>
class smart_ptr
{
public:
smart_ptr();
explicit smart_ptr(T*);
smart_ptr(const smart_ptr&);
smart_ptr(T*, std::function<void(T*)>);
smart_ptr& operator=(const smart_ptr&);
T& operator*() const;
T* operator->() const;

~smart_ptr();
// 向bool的类型转换
explicit operator bool() const;

bool unique();
void reset();
void reset(T*);
void reset(T*, std::function<void(T*)>);
T* release();

T* get() const;

private:
// 默认的deleter
static std::function<void(T*)> default_del;

private:
unsigned* m_p_use_count = nullptr;
T* m_pobject = nullptr;
std::function<void(T*)> m_del = default_del;
};

template <typename T>
std::function<void(T*)> smart_ptr<T>::default_del = [](T*p) {delete p; p = nullptr; };


template <typename T, typename... Args>
smart_ptr<T> make_smart(Args&&... args)
{
smart_ptr<T> sp(new T(std::forward<Args>(args)...));
return sp;
}


template <typename T>
smart_ptr<T>::smart_ptr()
:m_pobject(nullptr), m_p_use_count(new unsigned(1))
{
}


template <typename T>
smart_ptr<T>::smart_ptr(T *p)
:m_pobject(p), m_p_use_count(new unsigned(1))
{
}


template <typename T>
smart_ptr<T>::smart_ptr(T *p, std::function<void(T*)> del)
:m_pobject(p), m_p_use_count(new unsigned(1)), m_del(del)
{
}


template <typename T>
smart_ptr<T>::smart_ptr(const smart_ptr& rhs)
:m_pobject(rhs.m_pobject), m_p_use_count(rhs.m_p_use_count), m_del(rhs.m_del)
{
(*m_p_use_count)++;
}


template <typename T>
smart_ptr<T>& smart_ptr<T>::operator =(const smart_ptr &rhs)
{
// 使用rhs的deleter
m_del = rhs.m_del;
// 递增右侧运算对象的引用计数
++(*rhs.m_p_use_count);
// 递减本对象的引用计数
if (--(*m_p_use_count) == 0)
{
// 如果管理的对象没有其他用户了,则释放对象分配的成员
m_del(m_pobject);

delete m_p_use_count;
}

m_p_use_count = rhs.m_p_use_count;
m_pobject = rhs.m_pobject;

return *this; // 返回本对象
}


template <typename T>
T& smart_ptr<T>::operator*() const
{
return *m_pobject;
}


template <typename T>
T* smart_ptr<T>::operator->() const
{
return &this->operator*();
}


template <typename T>
smart_ptr<T>::~smart_ptr()
{
if (--(*m_p_use_count) == 0)
{
m_del(m_pobject);
m_pobject = nullptr;

delete m_p_use_count;
m_p_use_count = nullptr;
}
}


template <typename T>
bool smart_ptr<T>::unique()
{
return *m_p_use_count == 1;
}


template <typename T>
void smart_ptr<T>::reset()
{
(*m_p_use_count)--;

if (*m_p_use_count == 0)
{
m_del(m_pobject);
}

m_pobject = nullptr;
*m_p_use_count = 1;
m_del = default_del;
}


template <typename T>
void smart_ptr<T>::reset(T* p)
{
(*m_p_use_count)--;

if (*m_p_use_count == 0)
{
m_del(m_pobject);
}

m_pobject = p;
*m_p_use_count = 1;
m_del = default_del;
}


template <typename T>
void smart_ptr<T>::reset(T *p, std::function<void(T*)> del)
{
reset(p);
m_del = del;
}


template <typename T>
T* smart_ptr<T>::release()
{
(*m_p_use_count)--;

if (*m_p_use_count == 0)
{
*m_p_use_count = 1;
}

auto p = m_pobject;
m_pobject = nullptr;

return p;
}


template <typename T>
T* smart_ptr<T>::get() const
{
return m_pobject;
}


template <typename T>
smart_ptr<T>::operator bool() const
{
return m_pobject != nullptr;
}

unique_ptr() 一种简单实现

1、几个基本成员函数的作用

1
2
3
4
u.reset();          // 释放 u 指向的对象
u.reset(q); // 如果提供了内置指针 q,就令 u 指向这个对象
u.reset(nullptr); // 将 u 置为空
u.release(); // u 放弃对指针的控制权,返回指针,并将 u 置为空

2、一些规则

  1. 某个时刻只能有一个 unique_ptr 指向一个给定的对象。当它销毁时,它所指向的对象也会被销毁。
  2. 初始化 unique_ptr 只能采用直接初始化的方式(explicit 关键字)
  3. 不支持复制构造与赋值操作
  4. 在创建或者是 reset 一个具有删除器的 unique_ptr 时,必须提供删除器
  5. 不支持拷贝与赋值的规则有一个例外,那就是我们可以拷贝或者赋值一个将要被销毁的 unique_ptr(右值引用)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /* 从函数返回一个unique_ptr */
    unique_ptr<int> clone(int p ){
    return unique_ptr<int>(new int(p));
    }

    /* 返回一个局部对象的拷贝 */
    unique_ptr<int> clone(int p ){
    unique_ptr<int> ret(new int (p));
    return ret;
    }

3、unique_ptr 使用场景

1、为动态申请的资源提供异常安全保证
2、返回函数内动态申请资源的所有权
3、在容器中保存指针
4、管理动态数组
5、作为 auto_ptr 的替代品

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
             /*unique_ptr.h 文件 */
#ifndef _UNIQUE_PTR_H
#define __UNIQUE_H
class Delete {
public:
template<typename T>
void operator()(T *p) const {
delete p;
}
};
template<typename T,typename D = Delete >
class unique_ptr {
public:
explicit unique_ptr(T *pp = nullptr ,const D &dd= D() )
:un_ptr(pp),del(dd)
{
}
~unique_ptr() {
del(un_ptr);
}
/* 不支持拷贝与赋值 */
unique_ptr(const unique_ptr&) = delete ;
unique_ptr& operator=(const unique_ptr& ) = delete ;

/*可以拷贝或者赋值一个将要被销毁的 unique_ptr(右值引用)*/
unique_ptr( unique_ptr&& right_value):
un_ptr(right_value.un_ptr),del(std::move(right_value.del)) {
right_value.un_ptr = nullptr ;
}
unique_ptr& operator=( unique_ptr&& right_value ) noexcept {
if(this != &right_value ){
std::cout << "operator && right_value " << std::endl ;
del(*this);
un_ptr = right_value.un_ptr;
del = std::move(right_value.del);
right_value.un_ptr = nullptr ;
}
return *this ;
}
//u.release() u 放弃对指针的控制权,返回指针,并将 u 置为空
T* release(){
T *tmp = un_ptr ;
un_ptr = nullptr ;
return tmp ;
}
/*
u.reset() 释放u指向的对象
u.reset(q) 如果提供了内置指针q,就令u指向这个对象
u.reset(nullptr) 将 u 置为空
*/
void reset(){ del(un_ptr); }
void reset(T* q ){
if( un_ptr ){
del(un_ptr) ;
un_ptr = q ;
}
else
un_ptr = nullptr ;
}
void swap(unique_ptr &other ) noexcept {
using std::swap ;
swap( un_ptr,other.un_ptr );
swap(del,other.del) ;
}
T* get() { return un_ptr ; }
D& get_deleter(){ return del ; }
T& operator*() { return *un_ptr ; }
T* operator->() { return un_ptr ; }
private:
T *un_ptr = nullptr ;
D del ;
};
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
            /*    main.cpp 文件  */
#include <iostream>
#include <string>
//#include"shared_ptr.h"
#include "unique_ptr.h"
#include"DebugDelete.h"
#include <assert.h>
struct Foo {
Foo() { std::cout << "Foo()\n"; }
~Foo() { std::cout << "~Foo()\n"; }
Foo(const Foo&) { std::cout << "Foo copy ctor\n"; }
Foo(Foo&&) { std::cout << "Foo move ctor\n"; }
};
struct Fooo {
Fooo(int n = 0) noexcept : bar(n) { std::cout << "Fooo: constructor, bar = " << bar << '\n'; }
~Fooo() { std::cout << "Fooo: destructor, bar = " << bar << '\n'; }
int GetBar() const noexcept { return bar; }
private:
int bar;
};
struct D {
void bar() { std::cout << "Call deleter D::bar()...\n"; }
void operator()(Foo* p) const
{
std::cout << "Call delete from function object...\n";
delete p;
}
};
using namespace std ;
int main()
{
unique_ptr<string> p1(new string("Shengxi-Liu"));
cout << *p1 << endl ;
{
std::cout << "======================\nunique_ptr constructor:\n";
unique_ptr<Foo> up1;
unique_ptr<Foo> up1b(nullptr);
unique_ptr<Foo> up2(new Foo);

DebugDelete d;
unique_ptr<Foo, DebugDelete> up3(new Foo, d);
unique_ptr<Foo, DebugDelete&> up3b(new Foo, d);
unique_ptr<Foo, DebugDelete> up4(new Foo, DebugDelete());
unique_ptr<Foo> up5b(std::move(up2));
unique_ptr<Foo, DebugDelete> up6b(std::move(up3));

unique_ptr<Foo> up7 = std::move(up5b);
Foo* fp = up7.release();
assert(up7.get() == nullptr);
delete fp;

up6b.reset(new Foo());
up6b.reset(nullptr);

unique_ptr<Fooo> up71(new Fooo(1));
unique_ptr<Fooo> up72(new Fooo(2));

up71.swap(up72);

std::cout << "up71->val:" << up71->GetBar() << std::endl;
std::cout << "up72->val:" << (up72.get())->GetBar() << std::endl;

unique_ptr<Foo, D> up8(new Foo(), D());
D& del = up8.get_deleter();
del.bar();
}
}

参考资料

[1] Stack Overflow, GManNickG’s answer to “What is the copy-and-swap idiom?”. https://stackoverflow.com/a/3279550/816999
[2] cppreference.com, “std::shared_ptr”. https://en.cppreference.com/w/cpp/memory/shared_ptr

原文链接:
自己动手,实现C++的智能指针
C++ 之实现自己的 unique_ptr

评论

:D 一言句子获取中...

加载中,最新评论有1分钟缓存...