Understanding C++11: Smart Pointer

程序的内存按类型来划分,分为三种:

  • 静态内存(Static Memory)保存局部static对象、类static数据成员、定义在任何函数之外的变量
  • 栈内存(Stack Memory)保存定义在函数内的非static对象
  • 堆(Heap Memory)用来存储动态分配的对象

如果按照生命周期来看的话,则可以看成两类:

  • 静态内存和栈内存中的对象由编译器控制
  • 堆内存中的对象生存期由程序员控制
The C++ Programming Language

The C++ Programming Language

Raw Pointer

C++通过newdelete来生成和销毁动态内容,并使用原生指针(raw pointer)来访问内存,但其缺陷有:

  • 容易造成资源泄露;
  • 重复销毁会导致未定义行为;
  • 无法识别指针指向的是单一对象,还是数组;

Smart Pointer

C++ 提供了智能指针,它们为模板类,适用于任何类型

C++11里,一共提供了三个类:

  • shared_ptr,允许多个指针指向同一对象
  • unique_ptr,”独占”所指向的对象
  • weak_ptr,弱引用指向shared_ptr所管理的对象

被废弃的auto_ptr

auto_ptr于C++ 98引入,其保存一个指向对象的指针(不能指向动态分配的数组),当auto_ptr对象超出了作用域或另外撤销时,就会回收期所指向的动态分配对象。

auto_ptr对象的复制和赋值是破坏性操作,在拷贝和赋值之后,原本的auto_ptr会交出拥有权,而不是拷贝给新的auto_ptr

因此,auto_ptr不满足STL容器对其元素的要求,绝不要把auto_ptr作为标准容器的元素。同时,也不能作为函数返回值。

C++11发布以后,auto_ptr已经被推荐不再使用了。

取而代之的unique_ptr

unique_ptr主要的用途在于帮助避免资源泄露,比如在对象初始化期间因抛出异常而造成资源泄露,再比如虽然在函数的末尾有进行delete,但是有两类场景容易导致疏漏:

  • 函数中间有return语句,因此跳过了末尾的delete
  • 函数中间有代码抛出了异常,未进行捕获,直接抛出,导致跳过delete

解决上述两类场景问题的法门之一,就是定义一个局部变量,其会在函数结束时被自动销毁,不论是正常结束,还是异常结束。如果这个变量是unique_ptr,则可以在销毁时,执行自定义的逻辑,释放相关资源。

在使用unique_ptr时,需要注意:

  1. 必须直接初始化,
  2. 不一定拥有对象,可以是empty,赋以nullptr,或者调用reset()
  3. 不可以执行copy或assign,可以使用move语义

上述第三条存在一个例外,即return语句不需要std::move,因为编译器会尝试加上。

对于数组类型,unique_ptr提供了额外的支持,提供操作符[],但是其也存在制约,不再提供操作符*->,不接受一个派生类型的array作为初值。

主角的shared_ptr

shared_ptr是最为常用的智能指针,其允许多个指针指向同一个对象,其作用有:

  • 可以赋值、拷贝、比较
  • 可使用操作符*->

对于数组类型,和unique_ptr不同,shared_ptr不提供[]运算符。同时,因为shared_ptr提供的default deleter调用的是delete,而不是delete[],因此无法正确释放数组。不过,对此情况可以有两种途径解决:

  • 可以通过定义自己的deleter,通过传递一个函数或lambda,执行delete[]
  • 使用std::default_delete作为deleter

助攻的weak_ptr

weak_ptr的用途主要是辅助,常用的两个使用场景有:

  • 破解 cyclic reference,两个对象使用shared_ptr互相指向对方
  • reference 的寿命比所指对象的寿命要长

其也存在一些制约,比如:

  • 在default和copy构造函数之外,class weak_ptr只提供“接受一个shared_ptr”的构造函数
  • 不能够使用操作符*->访问所指向的对象

Reference

  1. What is a smart pointer and when should I use one?

  2. Which kind of pointer do I use when?

  3. raw, weakptr, uniqueptr, shared_ptr etc… How to choose them wisely?

  4. C++ 11 Library: Shared Pointers – I

  5. C++ 11 Library: Shared Pointers – II

  6. C++11 Library: Unique Pointers

  7. C++11 Library: Weak Pointers

Leave a comment

Your comment