Loading... C++语言中并没有自动回收内存的机制,每次手动分配 (new) 的内存一定要手动删除 (delete)。一旦因为各种情况导致忘记删除(内存泄漏),或者意外被删除(野指针),都会导致问题,非常的不方便。 而C++11的 **智能指针** 则是可以有效地缓解这一问题。智能指针是为了解决动态内存分配时带来的内存泄漏以及多次释放同一块内存空间而提出的。C++11 中封装在了 `<memory>` 头文件中。 智能指针是一个类,用来存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。 ## shared_ptr 资源可以被多个指针共享,使用**计数机制**表明资源被几个指针共享。通过 `use_count()` 查看资源的所有者的个数,可以通过 `unique_ptr`、`weak_ptr` 来构造,调用 `release()` 释放资源的所有权,计数减一,当计数减为 0 时,会自动释放内存空间,从而避免了内存泄漏。 以下是一个简易的实现(并非原版): ```cpp #include <iostream> #include <memory> template <typename T> class SmartPtr { private : T *_ptr; size_t *_count; public: SmartPtr(T *ptr = nullptr) : _ptr(ptr) { if (_ptr) { _count = new size_t(1); } else { _count = new size_t(0); } } ~SmartPtr() { (*this->_count)--; if (*this->_count == 0) { delete this->_ptr; delete this->_count; } } SmartPtr(const SmartPtr &ptr) // 拷贝构造:计数 +1 { if (this != &ptr) { this->_ptr = ptr._ptr; this->_count = ptr._count; (*this->_count)++; } } SmartPtr &operator=(const SmartPtr &ptr) // 赋值运算符重载 { if (this->_ptr == ptr._ptr) { return *this; } if (this->_ptr) // 将当前的 ptr 指向的原来的空间的计数 -1 { (*this->_count)--; if (this->_count == 0) { delete this->_ptr; delete this->_count; } } this->_ptr = ptr._ptr; this->_count = ptr._count; (*this->_count)++; // 此时 ptr 指向了新赋值的空间,该空间的计数 +1 return *this; } T &operator*() { assert(this->_ptr == nullptr); return *(this->_ptr); } T *operator->() { assert(this->_ptr == nullptr); return this->_ptr; } size_t use_count() { return *this->count; } }; ``` **双重管理的危险** 在使用 share_ptr 时,**应该避免出现可能导致两个指针组管理相同对象的情况**。例如,来看以下代码: ```cpp T * rPtr = new T(); shared_ptr<T> sPtr1(rPtr); shared_ptr<T> sPtr2(rPtr); ``` 这两个共享指针指向的是相同的对象。如果第一个`sPtr1`的引用计数降至 0 并删除该对象,那么这将导致其他组的指针变成悬挂指针。为了避免出现这种问题,**给定的裸指针应该最多只能初始化一个共享指针**,其他的shared_ptr都应当用第一个shared_ptr初始化。 ## unique_ptr 独享所有权的智能指针,资源只能被一个指针占有,该指针**不能拷贝构造和赋值**。但**可以进行移动构造和移动赋值构造**(调用 `move()` 函数,在[右值引用的移动语义](https://www.irimsky.top/archives/296/)中提过),即一个 unique_ptr 对象赋值给另一个 unique_ptr 对象,可以通过该方法进行赋值。 它是用于**取代C++98的auto_ptr**的。auto_ptr 同样是一个独占对象所有权的智能指针,但是在c++98的时候还没有移动语义的支持,因此对于auto_ptr的控制权转移的实现采用的是拷贝语义,转移指针资源的所有权的同时将原指针置为NULL,这跟通常理解的copy行为是不一致的(不会修改原数据),而这样的行为在有些场合下不是我们希望看到的。 这样带来的一些问题是拷贝构造函数和赋值操作重载函数不够完美,举个例子:把auto_ptr作为函数参数,传进去的时候控制权转移,转移到函数参数,当函数返回的时候并没有一个控制权移交回来的过程,函数调用之后原先的auto_ptr反而已经失效了。 在c++11当中有了移动语义,使用`move()`把unique_ptr传入函数,这样你就知道原先的unique_ptr已经失效了(不过对于move之后使用原来的内容是未定义行为,并非抛出异常 ## weak_ptr 弱指针(weak_ptr): 指向share_ptr指向的对象。weak_ptr 是⼀种弱引⽤,它是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期;也就是说,弱引⽤不会引起引⽤计数增加,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得其所观察的shared_ptr。 weak_ptr 没有重载 * 运算符和 -> 运算符,所以不能够对资源进⾏操作,它的唯⼀作⽤就是⽤于**检查 std::shared_ptr 的状态**,比如有多少指向相同的 shared_ptr 指针、shared_ptr 指针指向的堆内存是否已经被释放等等。其`use_count()`方法返回的是指向目标对象的shared_ptr的个数,其 `expired() `⽅法能在资源未被释放时,会返回 false,否则返回 true 。 weak_ptr 能够解决由shared_ptr带来的**循环引⽤**问题。  ## 参考 [C++智能指针详解](https://www.cnblogs.com/greatverve/p/smart-ptr.html) [c++ 智能指针用法详解](https://www.cnblogs.com/TenosDoIt/p/3456704.html) [C语言中文网](http://c.biancheng.net/view/7898.html) 最后修改:2022 年 01 月 01 日 12 : 06 PM © 允许规范转载