0%

自己实现smart_ptr

2020年3月16日 下午4:41

总结1:

  1. 在多线程下什么场景进行对象操作不安全?
    1. 线程可以释放:不同线程之间,不知道自己所指的内存对应的对象是否还活着,有可能被其中一个线程释放了,那其他线程此时在调用就报错了
    2. 线程不可以释放:要不就是在堆中,所有线程都别释放,这样显然不成
    3. 这两种方法都不好!
  2. 为什么smart_ptr可以实现对多线程下对象生命周期的管理?
    1. 我们从下面的代码中在单线程下的测试输出可以看出,smart_ptr的本质是对象资源的管理,将对象析构的权利给予到第三方的smart_ptr。
    2. 对象内存的资源释放与作用域{},所在的线程都是无关的,至于引用计数值=0相关
      1. 当smart_ptr对象超出自己的作用域{}之后,即使自己被析构了,但是smart_ptr指向的对象引用计数值!=0,那么这个对象还是没有被析构的
      2. 析构所在的线程不一定是资源对象诞生的线程P20
    3. smart_ptr完成的是对资源对象的引用计数,计数的能力是不受线程并发的影响的!所以,smart_ptr可以直接用到多线程场景下。
  3. 为什么对象中的mutex不能解决?
    1. 编写线程安全的类不是难事,使用同步原语(eg:mutex)来保护内部状态,但是对象的生死不能由对象拥有的mutex进行保护。
    2. 析构函数会将对象中的mutex成员变量进行销毁

总结2

  1. smart_ptr是一种类似于解决单点登录的”设计“问题,smart_ptr的本质是对象资源的管理,将对象析构的权利给予到第三方的smart_ptr,这样大家都别抢,我smart_ptr保证对每一位用户负责。
  2. smart_ptr核心目的有2:
    1. 在域外能够自动析构对象,做到RAII
    2. 共享计数:
      1. 也就说这个计数能是每个smart_ptr对象有各自的计数,实现的方式就像是单点登录中redis的作用,在多个tomcat之间共享
      2. 一个smart_ptr加、减,其他的相同指向的smart_ptr都可以看到
  3. 代码实现的关键:
    1. smart_ptr<circle> ptr1(new circle());这里面new circle()产生的对象是分配在堆上的,不受main()函数作用域限制,需要我们手动进行释放
    2. smart_ptr.h中class smart_ptr的实现的关键在于:
      1. 多种构造函数:绿色
      2. 指针常用运算符的重载:红色
      3. shared_count是一个指针类型:黄色

代码:

  • 这份代码包含了shard_ptr 和 unique_ptr
  • main.cpp*
    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
    #include <stdio.h>      // printf/puts
    #include "shape.h" // shape/shape_type/create_shape
    #include "smart_ptr.h" // smart_ptr


    int main()
    {
    // 唯一的一个对象实体new circle()
    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");
    }
    // 测试smart_ptr的强制类型转换
    smart_ptr<circle> ptr3 = dynamic_pointer_cast<circle>(ptr2);
    printf("use count of ptr3 is %ld\n", ptr3.use_count());
    // 移动构造函数调用
    smart_ptr<shape> ptr4 = std::move(ptr2);
    printf("use count of ptr4 is %ld\n", ptr4.use_count());
    printf("use count of ptr2 is now %ld\n", ptr2.use_count());
    // 指向相同对象的smart_ptr有相同的conut
    printf("use count of ptr1 is now %ld\n", ptr1.use_count());
    printf("use count of ptr2 is now %ld\n", ptr2.use_count());// 由于我们使用了移动语义,这里就成0了
    printf("use count of ptr3 is now %ld\n", ptr3.use_count());
    /** 输出
    circle()
    use count of ptr1 is 1
    use count of ptr2 was 0
    use count of ptr2 is now 2
    ptr1 is not empty
    use count of ptr3 is 3
    use count of ptr4 is 3
    use count of ptr2 is now 0
    use count of ptr1 is now 3
    use count of ptr2 is now 0
    use count of ptr3 is now 3
    ~circle()
    * ***/
    }

shape.h

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
#ifndef SHAPE_H
#define SHAPE_H

#include <stdexcept> // std::logic_error
#include <stdio.h> // puts

enum class shape_type {
circle,
triangle,
rectangle,
};

class shape {
public:
virtual ~shape() {}
};

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

class triangle : public shape {
public:
triangle() { puts(“triangle()”); }
~triangle() { puts("~triangle()"); }
};

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

inline shape* create_shape(shape_type type)
{
switch (type) {
case shape_type::circle:
return new circle();
case shape_type::triangle:
return new triangle();
case shape_type::rectangle:
return new rectangle();
}
throw std::logic_error("shape type is invalid");
}

#endif // SHAPE_H

smart_ptr.h

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
#ifndef SMART_PTR_H
#define SMART_PTR_H

#include <atomic> // std::atomic
#include <utility> // std::swap

class shared_count {
public:
shared_count() noexcept
: count_(1)
{
}
void add_count() noexcept
{
count_.fetch_add(1, std::memory_order_relaxed);
}
long reduce_count() noexcept
{
return --count_;
}
long get_count() const noexcept
{
return count_;
}

private:
std::atomic_long count_;
};

template <typename T>
class smart_ptr {
public:
// 没有则报错:fatal error: ‘ptr_’ is a private member of ‘smart_ptr<circle>’
// 错误原因是模板的各个实例间并不天然就有 friend 关系,因而不能互访私有成员
template <typename U>
friend class smart_ptr;
// 在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换
// 1.explicit 关键字只能用于类内部的构造函数声明上。
// 2.explicit 关键字作用于单个参数的构造函数。
// 3.在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换
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) noexcept
{
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 rhs)是会触发一次构造函数的执行的
// 触发的是:smart_ptr(smart_ptr<U>&& other)这个重载的构造函数
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);
}

#endif // SMART_PTR_H