C++智能指针使用须知

我在《C++ 智能指针(shared_ptr/weak_ptr)源码分析》已经介绍了智能指针的一些使用注意点,本文在此基础上,基于C++11中的语法特性,介绍一些智能指针的使用须知。

std::unique_ptr

  1. 如果不需要共享,同时需要防止内存泄漏, std::unique_ptr是替代raw pointer(裸指针)的第一选择:它的大小和raw pointer一样,解引用的速度也和raw pointer相当。
  2. 基于std::unique_ptr的语法意义,它可以move,但是不可以copy。
  3. std::unique_ptr可以采用自定义的deleter: 它可以采用stateless lambda表达式,也可以采用函数。stateless lambda的表达式不会增加指针的大小,函数形式的会增加一个函数指针的大小。
  4. 如无十分必要,不要使用数组形式的std::unique_ptr, 最好用std::vector >的形式替代。
  5. 工厂模式一般设计返回类型为std::unique_ptr,用户可以根据使用需要转换为std::shared_ptr

std::shared_ptr

  1. std::shared_ptr的大小至少是raw pointer的两倍,因为其内部包含有一个指向被管理对象(managed object)T的指针以及一个__shared_count对象,__shared_count对象包含一个指向管理对象(manager object)的基类指针:

在这里插入图片描述

  1. 采用std::shared_ptr spw(new Widget)形式创建的智能指针涉及到两次动态内存申请,因而性能上和裸指针形式:Widget* spw= new Widget相比要差些.
  2. 基于C++中的"No naked new"原则,推荐使用auto spw(std::make_shared)或auto spw(std::make_unique)的形式来创建智能指针,避免使用auto pw = new Widget; std::shared_ptr spw(pw)的形式来创建智能指针。
  3. 采用std::make_shared创建智能指针时,只有一次动态内存申请:管理对象和被管理对象在同一内存区域,这可以降低性能开销。但是如果被管理对象占用内存较大,被管理对象部分的内存释放后可能得不到及时的回收(比如管理对象因为其它原因:weak_ptr等的指向而得不到释放),那么整个区域的内存将不会被释放。
  4. 当要在类的内部构造或者使用指向自身的智能指针时,不能直接像std::shared_ptr spw(this)这样构造,因为这会产生另外的管理对象,造成twice delete;解决办法是通过使用CRTP(The Curiously Recurring Template Pattern)的方法:继承std::enable_shared_from_this,在需要的时候通过shared_from_this()方式获取指向自身的智能指针。
  5. std::unique_ptr根据使用需要转换为std::shared_ptr,但是std::shared_ptr不可以转换为std::unique_ptr。

std::weak_ptr

  1. 有时我们可能不想影响某个对象的生命周期,但是有时候又需要知道该对象是否被析构,这个时候就需要使用std::weak_ptr了。std::weak_ptr相对于对象的生命周期来说是一种旁观者的身份。
  2. 可以通过std::shared_ptr构造std::weak_ptr,也可以根据使用需要从std::weak_ptr获得std::shared_ptr。
  3. 通过std::weak_ptr的expired()函数判断对象是否已经被析构;通过lock()函数原子的返回指向该对象的智能指针,如果对象被析构返回null指针。
  4. 在观察者模式中,使用std::weak_ptr可以知道订阅者的是否被析构,如果没有,将消息推送给观察者。

std::shared_ptr的线程安全性

在http://www.boost.org/doc/libs/release/libs/smart_ptr/shared_ptr.htm#ThreadSafety中,指出了shared_ptr的线程安全性:

  • (1)A shared_ptr instance can be “read” (accessed using only const operations) simultaneously by multiple threads.
  • (2)Different shared_ptr instances can be “written to” (accessed using mutable operations such as operator= or reset) simultaneously by multiple threads (even when these instances are copies, and share the same reference count underneath.)
  • (3)Any other simultaneous accesses result in undefined behavior.

对于std::shared_ptr的线程安全性,陈硕做了非常详尽的分析:http://blog.csdn.net/solstice/article/details/8547547,
总结一下:

  1. shared_ptr的赋值涉及到指向被管理对象的指针的复制(a)和管理对象的指针的复制和计数的增减(b);一般的实现步骤a和步骤b各自都是原子的。如果步骤a或者步骤b不是原子的,那么shared_ptr上面(1)或(2)的线程安全性将得不到保障。
  2. 由于a和b合并起来时,涉及到两个方面的操作,如果没有互斥或者锁,合并的操作将不会是原子的,因而不是线程安全的。这就是同一个shared_ptr被不同的线程同时读写时线程不安全的原因。

你可能感兴趣的:(C++)