智能指针
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
| template <typename T> class smart_ptr { public: explicit smart_ptr(T *ptr = nullptr) : ptr_(ptr) {} ~smart_ptr() { delete ptr_; } T *get() const { return ptr_; }
T &operator*() const { return *ptr_; } T *operator&() const { return ptr_; } operator bool() const { return 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) { swap(ptr_, rhs.ptr_); }
private: T *ptr_; };
|
存在危险行为
1 2 3 4 5 6 7 8 9
| template <typename T> void print_addr(smart_ptr<T> ptr) { cout << "Real address of shape is " << static_cast<void *>(&*ptr) << endl; } ... smart_ptr<int> ptr(new int(3)); print_addr(ptr); cout << "Now address of shape is "<< static_cast<void *>(&*ptr) << endl;
|
1 2
| Real address of shape is 0xe22130 Now address of shape is 0
|
问题: ptr
变成了空指针,原因: print_addr
为值传递,调用函数 smart_ptr(smart_ptr &other)
,将 ptr
传进函数内部对象并置空,离开作用域内部对象被销毁。
危险行为: auto_ptr
右值引用改进——> unique_ptr
1 2 3 4 5 6 7 8 9 10 11
| smart_ptr(smart_ptr &&other) { ptr_ = other.release(); }
smart_ptr &operator=(smart_ptr rhs) { rhs.swap(*this); return *this; }
|
1 2 3 4 5 6 7
| smart_ptr<int> ptr1{new int(2)}; smart_ptr<int> ptr2{ptr1}; smart_ptr<int> ptr3; ptr3=ptr1; ptr3=move(ptr1); smart_ptr<int> ptr4{move(ptr3)};
|
完善行为:子类指针向基类转换
1 2 3 4 5 6 7 8 9 10 11 12
| template <typename U> smart_ptr(smart_ptr<U> &&other) { ptr_ = other.release(); } ...... smart_ptr<A> ptr1(new A()); smart_ptr<B> ptr2=move(ptr1); ...... smart_ptr<B> ptr1(new B()); smart_ptr<A> ptr2=move(ptr1);
|
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 53 54 55 56
| #include <iostream> using namespace std;
template <typename T> class smart_ptr { public: explicit smart_ptr(T *ptr = nullptr) : ptr_(ptr) {} ~smart_ptr() { delete ptr_; } T *get() const { return ptr_; }
T &operator*() const { return *ptr_; } T *operator&() const { return ptr_; } operator bool() const { return ptr_; }
smart_ptr(const smart_ptr &) = delete; smart_ptr &operator=(const smart_ptr &) = delete;
smart_ptr(smart_ptr &&other) { ptr_ = other.release(); } smart_ptr &operator=(smart_ptr rhs) { rhs.swap(*this); return *this; } template <typename U> smart_ptr(smart_ptr<U> &&other) { ptr_ = other.release(); }
T *release() { T *ptr = ptr_; ptr_ = nullptr; return ptr; } void swap(smart_ptr &rhs) { swap(ptr_, rhs.ptr_); }
private: T *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
| 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_; } } 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; } }
private: T *ptr_; shared_count *shared_count_; };
|
方便创建智能指针的工具函数
make_unique
1 2 3 4 5 6 7 8 9 10
| auto ptr_int = make_unique<int>(42);
struct Point{ int x; int y; }; auto ptr_point = make_unique<Point>(new Point{1,2});
auto ptr_point = make_unique<Point>(1,2);
|
make_shared
其他智能指针特性
- 数组支持
unique_ptr<T[]>
(C++11)
shared_ptr<T[]>
(C++17) // C++17之前管理数组需要自定义删除器来释放内存,因为默认使用 delete
不能正确释放分配的数组,需要在自定义删除器中使用 delete[]
释放数组,C++17以后 shared_ptr<T[]>
默认使用 delete[]
来释放管理的对象
- 自定义删除器
- 定制智能指针离开作用域之后如何删除管理的对象
- 作用:在智能指针释放对象时进行一些特殊操作,比如打印日志、管理内存以外的其他资源比如说:文件句柄、数据库连接
- 使用:可以是函数/类的对象/
lambda
表达式
weak_ptr
- 循环引用问题:如果父节点管理子节点,使用
shared_ptr
指向子节点,此时子节点不可以用 shared_ptr
指向父节点,会形成环状依赖,引用计数不会降到0 。此时子节点可以使用 weak_ptr
指向父节点,这样可以保证引用计数最后会降为0,正常析构。
- 如果可以确定,子节点存在,父节点一定存在的话(不会出现指针悬空),可以考虑使用裸指针,性能更高,使用
weak_ptr
还是有引用计数增减的问题,性能还是有一点影响
智能指针的使用建议
- 在指针具有拥有权的时候应当使用
unique_ptr
和 share_ptr
- 尽量使用
unique_ptr
,一定需要引用计数才用 share_ptr
- 注意对智能指针进行拷贝构造或者赋值会影响对象的生命周期(因为这些操作会改变与智能指针相关的引用计数),进而影响所管理对象的生命周期
- 传参一般仍使用普通指针或引用,除非要传递所有权