C++ 智能指针总结 根据 cppreference,C++ 提供了这些智能指针,以及和它们有联系的技术(未标注的均为 since c++11)
智能指针:
unique_ptr
shared_ptr
weak_ptr
相关技术:
owner_less
enable_shared_from_this
bad_weak_ptr
default_delete
out_ptr_t(c++23)
inout_ptr_t(c++23)
智能指针 所有智能指针默认非线程安全
通常 unique_ptr 和 raw_ptr 组合使用;shared_ptr 和 weak_ptr 组合使用;
unique_ptr 独占 资源所属权,它其实可以取代 scope pointer,很好的贯彻了 RAII 思想,让空悬指针、野指针、double free、memory leak 等内存问题得到轻松的解决;
shared_ptr 共享 资源所属权,控制了对象的生命周期 ,当没有任何 shared_ptr 指向资源时,资源会被自动释放,这能帮助我们实现 GC,这里的 Garbage 不光指 memory,还包括任何系统资源;在多线程场景下,它能帮我们安全析构对象;
weak_ptr 不会对对象的生命周期产生影响,也不持有资源的所有权 ,但是它能够知道某个对象是否还“活着”;在必要时,它还能提升为 shared_ptr;通常把它和 shared_ptr 组合起来用于双向关联的两个类(见 muduo 阅读笔记);
我根据三个智能指针的功能,写了简单的实现:
unique_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 template <class T >class unique_ptr {public : explicit unique_ptr (T* raw_ptr) : raw_ptr_ (raw_ptr) {} unique_ptr (unique_ptr <T>&& rhs) { raw_ptr_ = rhs.raw_ptr_; rhs.raw_ptr_ = nullptr ; } unique_ptr & operator =(unique_ptr <T>&& rhs) { this ->~unique_ptr (); new (this ) unique_ptr (std ::move(rhs)); return *this ; } ~unique_ptr () { if (raw_ptr_) { delete raw_ptr_; raw_ptr_ = nullptr ; } } void swap (unique_ptr <T>& rhs) { if (this == &rhs) return ; T* tmp = raw_ptr_; raw_ptr_ = rhs.raw_ptr_; rhs.raw_ptr_ = tmp; } void reset (T* raw_ptr = nullptr ) { this ->~unique_ptr (); raw_ptr_ = raw_ptr; } T* release () { T* ret = raw_ptr_; raw_ptr_ = nullptr ; return ret; } T* get () const { return raw_ptr_; } T& operator *() const { return *raw_ptr_; } T* operator ->() const { return raw_ptr_; } private : unique_ptr (const unique_ptr &) = delete ; unique_ptr & operator =(const unique_ptr &) = delete ; T* raw_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 template <class T >class weak_ptr ;template <class T >class shared_ptr {public : shared_ptr () : raw_ptr_(nullptr ), ref_(nullptr ) {} shared_ptr (T* raw_ptr) : raw_ptr_(raw_ptr), ref_(new std ::atomic<uint64_t >(1 )) {} shared_ptr (const shared_ptr <T>& rhs) { if (rhs) { raw_ptr_ = rhs.raw_ptr_; ref_ = rhs.ref_; ref_->fetch_add(1 ); } } shared_ptr <T>& operator =(const shared_ptr <T>& rhs) { if (this == &rhs) return *this ; if (rhs) { this ->~shared_ptr (); raw_ptr_ = rhs.raw_ptr_; ref_ = rhs.ref_; ref_->fetch_add(1 ); } return *this ; } shared_ptr (shared_ptr <T>&& rhs) { } shared_ptr <T>& operator =(shared_ptr <T>&& rhs) { } ~shared_ptr () { if (*this ) { int ref = ref_->fetch_sub(1 ); if (ref == 1 ) { delete raw_ptr_; } raw_ptr_ = nullptr ; ref_ = nullptr ; } } void reset (T* raw_ptr = nullptr ) { if (*this ) { this ->~shared_ptr (); } else { new (this ) shared_ptr <T>(raw_ptr); } } void swap (shared_ptr <T>& lhs, shared_ptr <T>& rhs) { if (&lhs == &rhs) return ; T* tmp1 = lhs.raw_ptr_; std ::atomic<uint64_t >* tmp2 = lhs.ref_; lhs.raw_ptr_ = rhs.raw_ptr_; lhs.ref_ = rhs.ref_; rhs.raw_ptr_ = tmp1; rhs.ref_ = tmp2; } T* get () const { return raw_ptr_; } uint64_t use_count () const { if (!*this ) return 0 ; return *ref_; } bool unique () const { if (!*this ) return false ; return *ref_ == 1 ; } T& operator *() const { return *raw_ptr_; } T* operator ->() const { return raw_ptr_; } operator T*() const { return raw_ptr_; } private : friend class weak_ptr < T>; T* raw_ptr_; std ::atomic<uint64_t >* ref_; }; template <class T , class U >bool operator ==(const shared_ptr <T>& lhs, const shared_ptr <U>& rhs) { return reinterpret_cast <long long >(lhs.get()) == reinterpret_cast <long long >(rhs.get()); }
weak_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 template <class T >class weak_ptr {public : weak_ptr() = default ; weak_ptr(const weak_ptr<T>& wp) { *this = wp; } weak_ptr(const shared_ptr <T>& sp) { *this = sp; } weak_ptr<T>& operator =(const weak_ptr<T>& rhs) { raw_ptr_ = rhs.raw_ptr_; ref_ = rhs.ref_; } weak_ptr<T>& operator =(const shared_ptr <T>& rhs) { raw_ptr_ = rhs.raw_ptr_; ref_ = rhs.ref_; return *this ; } weak_ptr(weak_ptr<T>&& rhs) { *this = std ::move(rhs); } weak_ptr<T>& operator =(weak_ptr<T>&& rhs) { *this = rhs; rhs.raw_ptr_ = nullptr ; rhs.ref_ = nullptr ; } void reset () { raw_ptr_ = nullptr ; ref_ = nullptr ; } void swap (weak_ptr<T>& wp) { swap(raw_ptr_, wp.raw_ptr_); swap(ref_, wp.ref_); } shared_ptr <T> lock () const { shared_ptr <T> ret; if (!expired()) { ret.raw_ptr_ = raw_ptr_; ret.ref_ = ref_; ref_->fetch_add(1 ); } return ret; } bool expired () const { return ref_ == nullptr or *ref_ == 0 ; } private : T* raw_ptr_{0 }; std ::atomic<uint64_t >* ref_{0 }; };
相关技术 owner_less 见 What does std::owner_less do?
enable_shared_from_this 用于获取被 shared_ptr 管理的对象的 shared_ptr;(有点绕。。。)
记住三点:
enable_shared_from_this 使用了 CRTP 技术实现编译期多态;
继承了 enable_shared_from_this 的类,必须是堆对象,而不能是栈对象;
不要在派生类的构造函数里面调用 shared_from_this,因为此时还没有把它交给 shared_ptr 管理;
bad_weak_ptr 这是一个异常类,继承自 std::exception
std::bad_weak_ptr
is the type of the object thrown as exceptions by the constructors of std::shared_ptr that take std::weak_ptr as the argument, when the std::weak_ptr refers to an already deleted object.——cppreference
上面的话的意思就是:std::bad_weak_ptr 是在调用构造函数 shared_ptr(const weak_ptr<T>& rhs)
时,因为 std::weak_ptr 所指的对象已经被删除而抛出的异常对象;
Member Function 中只需要记住一个:std::bad_weak_ptr::what (基类的虚函数);
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <memory> #include <iostream> int main () { std ::shared_ptr <int > p1 (new int (42 )) ; std ::weak_ptr<int > wp (p1) ; p1.reset(); try { std ::shared_ptr <int > p2 (wp) ; } catch (const std ::bad_weak_ptr& e) { std ::cout << e.what() << '\n' ; } }
default_delete 智能指针中的删除器,用于 unique_ptr 和 shared_ptr(只有这两个有 ownership);默认情况下是,std::default_delete<T>()
或 std::default_delete<T[]>()
;前者的默认行为是 delete,后者的默认行为是 delete [];
可以在构造 unique_ptr 的时候指定 default_delete:
1 2 3 4 5 6 7 template < class T , class Deleter = std ::default_delete<T>> class unique_ptr ; std ::unique_ptr <int > up (new int (10 ), std ::default_delete<int >()) ;std ::unique_ptr <int > up (new int [10 ], std ::default_delete<int []>()) ;
可以看到, unique_ptr 模板类的模板参数中就带有 Deleter;
再看下 shared_ptr:
1 2 3 4 5 6 7 8 9 10 11 12 template < class T > class shared_ptr ;template < class Y , class Deleter >shared_ptr ( Y* ptr, Deleter d );std ::shared_ptr <int > sp (new int (10 ), std ::default_delete<int >()) ;std ::shared_ptr <int > sp (new int [10 ], std ::default_delete<int []>()) ;std ::vector <int *> v;for (int n = 0 ; n < 100 ; ++n) v.push_back(new int (n)); std ::for_each(v.begin(), v.end(), std ::default_delete<int >());
shared_ptr 模板类的模板参数中仅有一个资源类型参数,没有 Deleter;它是在构造函数多了个模板参数,它的构造函数是函数模板;
out_ptr_t inout_ptr_t 见 Understanding std::inout_ptr and std::out_ptr in C++23