1.智能指针的由来

C++ 中,动态内存的管理是通过一对运算符来完成的,new 用于申请内存空间,调用对象构造函数初始化对象并返回指向该对象的指针。delete接收一个动态对象的指针,调用对象的析构函数销毁对象,释放与之关联的内存空间。动态内存的管理在实际操作中并非易事,因为确保在正确的时间释放内存是极其困难的,有时往往会忘记释放内存而产生内存泄露;有时在上游指针引用内存的情况下释放了内存,就会产生非法的野指针(悬挂指针)。

为了更容易且更安全的管理动态内存,C++ 推出了智能指针(smart pointer)类型来管理动态对象。智能指针存储指向动态对象的指针,用于动态对象生存周期的控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露[1]^{[1]}[1]。

对动态内存的管理,可以引申为对系统资源的管理,但是 C++ 程序中动态内存只是最常使用的一种资源,其他常见的资源还包括文件描述符(file descriptor)、互斥锁(mutex locks)、图形界面中的字型和笔刷、数据库连接、以及网络sockets等,这些资源事实上都可以使用智能指针来管理。

2.智能指针的基本思想

智能指针的基本思想是以栈对象管理资源[2]^{[2]}[2]。考察如下示例:

void remodel(std::string & str)
{std::string* ps = new std::string(str);...if (weird_thing()){throw exception();}str = *ps; delete ps;return;
}

如果在函数 remodel 中出现异常,语句delete ps没有被执行,那么将会导致 ps 指向的 string 的堆对象残留在内存中,导致内存泄露。如何避免这种问题?有人会说,这还不简单,直接在throw exception();之前加上delete ps;不就行了。是的,你本应如此,问题是很多人都会忘记在适当的地方加上delete语句(连上述代码中最后的那句delete语句也会有很多人忘记吧),如果你要对一个庞大的工程进行review,往往会发现内存泄露时有发生,对于程序而言,这无疑是一场灾难!这时我们会想:当 remodel 这样的函数终止(不管是正常终止,还是由于出现了异常而终止),函数体内的局部变量都将自动从栈内存中删除,因此指针 ps 占据的内存将被释放,如果 ps 指向的内存也被自动释放,那该有多好啊。我们知道析构函数有这个功能。如果 ps 有一个析构函数,该析构函数将在 ps 过期时自动释放它指向的内存。但 ps 的问题在于,它只是一个常规指针,不是有析构凼数的类对象指针。如果 ps 是一个局部的类对象,它指向堆对象,则可以在 ps 生命周期结束时,让它的析构函数释放它指向的堆对象[3]^{[3]}[3]。

通俗来讲, 智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。智能指针的主要作用就是用栈智能指针离开作用域自动销毁时调用析构函数来释放资源。当然,智能指针还不止这些,还包括复制时可以修改源对象等。智能指针根据需求不同,设计也不同(写时复制,赋值即释放对象拥有权限、引用计数、控制权转移等)。

3.智能指针的引用计数

什么是引用计数?
智能指针有时需要将其管理的对象的所有权转移给其它的智能指针,使得多个智能指针管理同一个对象,比如C++ STL中的shared_ptr支持多个智能指针管理同一个对象。这个时候智能指针就需要知道其引用的对象总共有多少个智能指针在引用在它,也就是说智能指针所管理的对象总共有多少个所有者,我们称之为引用计数(Reference Counting),因为智能指针在准备释放所引用的对象时,如果有其他的智能指针同时在引用这个对象时,则不能释放,而只能将引用计数减一。

引用计数的目的?
引用计数,是资源管理的一种技巧和手段,智能指针使用了引用计数,STL中的string也同样使用了引用计数并配合“写时复制”来实现存储空间的优化。总的来说,使用引用计数有如下两个目的:
(1)节省内存,提高程序运行效率。如何很多对象拥有相同的数据实体,存储多个数据实体会造成内存空间浪费,所以最好做法是让多个对象共享同一个数据实体。
(2)记录引用对象的所有者数量,在引用计数为0时,让对象的最后一个拥有者释放对象。

其实,智能指针的引用计数类似于Java的垃圾回收机制:Java的垃圾的判定很简单,如果一个对象没有引用所指,那么该对象为垃圾,系统就可以回收了。

智能指针实现引用计数的策略。
大多数 C++ 类用三种方法之一来管理指针成员:
(1)不管指针成员。复制时只复制指针,不复制指针指向的对象实体。当其中一个指针把其指向的对象的空间释放后,其它指针都成了悬挂指针。这是一种极端做法。
(2)当复制的时候,即复制指针,也复制指针指向的对象。这样可能造成空间的浪费。因为指针指向的对象的复制不一定是必要的。
(3) 第三种就是一种折中的方式。利用一个辅助类来管理指针的复制。原来的类中有一个指针指向辅助类对象,辅助类的数据成员是一个计数器和一个指针(指向原来的对象)。

可见,第三种方法是优先选择的方法,智能指针实现引用计数的策略主要有两种:辅助类与句柄类。使用句柄类尚未研究,本文以辅助类为例,来研究实现智能指针的引用计数。利用辅助类来封装引用计数和指向对象的指针。如此做,智能指针,辅助类对象与被引用对象的关系如下图所示:

辅助类将引用计数与智能指针类指向的对象封装在一起,引用计数记录有多少个智能指针指向同一对象。每次创建智能指针时,初始化智能指针并将引用计数置为1;当智能指针q赋值给另一个智能指针r时,即r=q,拷贝构造函数拷贝智能指针并增加q指向的对象的引用计数,递减r原来指向的对象的引用计数。也就是说对一个智能指针进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数。

4.智能指针的实现模板

智能指针管理对象,本质上是以栈对象来管理堆对象,在《Effective C++》的条款13中称之为资源获取即初始化(RAII,Resource Acquisition Is Initialization),也就是说我们在获得一笔资源后,尽量以独立的一条语句将资源拿来初始化某个资源管理对象。有时候获得的资源被拿来赋值(而非初始化)某个资源管理对象,但不论哪一种做法,获得一笔资源后应该立即放进资源管理对象中。

智能指针就是一种资源管理对象,提供的功能主要有如下几种:
(1)以指针的行为方式访问所管理的对象,需要重载指针->操作符;
(2)解引用(Dereferencing),获取所管理的对象,需要重载解引用*操作符;
(3)智能指针在其声明周期结束时自动销毁其管理的对象;
(4)引用计数、写时复制、赋值即释放对象拥有权限、控制权限转移。

第4条是可选功能,拥有四条中不同的功能对应着不同类型的智能指针,比如C++11在STL中引入的shared_ptr就实现了引用计数的功能,已经被C++11摒弃的auto_ptr[4]^{[4]}[4]实现了赋值即释放对象拥有权限,C++11引入的unique_ptr则实现了控制权限的转移功能。

根据智能指针的功能,通常用类模板实现如下:

template <class T> class SmartPointer
{
private:  T *_ptr;
public:  SmartPointer(T *p) : _ptr(p)  //构造函数  {  }  T& operator *()        //重载*操作符  {  return *_ptr;  }  T* operator ->()       //重载->操作符  {  return _ptr;  }  ~SmartPointer()        //析构函数  {  delete _ptr;  }
};

参考文献

[1]Stanley B.Lippman著,王刚,杨巨峰译.C++ Primer(第五版).2013:400-422
[2]Scott Meyers著,侯捷译.Effective C++中文版(第三版).2011:61-77
[3]C++智能指针简单剖析
[4]shared_ptr基于引用计数智能指针实现

C++ 智能指针简介相关推荐

  1. 【C++ 语言】智能指针 引入 ( 内存泄漏 | 智能指针简介 | 简单示例 )

    文章目录 I . 智能指针 引入 II . 智能指针 简介 III . 智能指针 简单示例 I . 智能指针 引入 1 . 示例前提 : 定义一个 Student 类 , 之后将该类对象作为智能指针指 ...

  2. 【c++基础】第五章 RALL机制与智能指针

    第五章 RALL机制与智能指针 RAII机制 auto_ptr资源所有权转移智能指针 独占智能指针unique_ptr shared_ptr共享智能指针 weak_ptr破环指针 智能指针的问题与解决 ...

  3. C++智能指针:更简单、更高效的内存管理方法

    C++智能指针:从新手到高手的心理密码C++ Smart Pointers: Psychological Passcodes from Beginner to Expert 智能指针简介 (Intro ...

  4. 智能指针(三):unique_ptr使用简介

    文章转自:http://blog.csdn.net/weiwenhp/article/details/8708281 版权归原作者! 我们知道auto_ptr通过复制构造或者通过=赋值后,原来的aut ...

  5. C++ 智能指针详解

    智能指针内容很多,重点是基本用法. #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_fro ...

  6. C++ STL 四种智能指针

    文章目录 0.前言 1.unique_ptr 2.auto_ptr 3.shared_ptr 3.1 shared_ptr 简介 3.2 通过辅助类模拟实现 shared_ptr 4.weak_ptr ...

  7. Boost智能指针——boost::scoped_ptr(使用及原理分析)

    简介 boost::scoped_ptr是一个比较简单的智能指针,它能保证在离开作用域之后它所管理对象能被自动释放.下面这个例子将介绍它的使用: 1 #include <iostream> ...

  8. C++ 以智能指针管理内存资源

    1.简介 C++ 作为一门应用广泛的高级编程语言,却没有像 Java.C# 等语言拥有垃圾回收(Garbage Collection )机制来自动进行内存管理,这也是C++ 一直被诟病的一点.C++ ...

  9. C++异常处理机制由浅入深, 以及函数调用汇编过程底层刨析. C++11智能指针底层模拟实现

    一. 异常 1.1.异常的编程模型和基本使用 咱得用一用, 解释一下上述的模型    double Div(int a, int b) {if (b == 0) throw "Zero Di ...

最新文章

  1. 在 VMware ESXi 5.0 上安装万兆网卡驱动
  2. Jquery一些常见性能的问题
  3. [html]说说页面中字体渲染规则是怎样的?会有哪些因素影响字体的渲染?
  4. pythondjango网页开发_Python-Web开发 Django 简介
  5. java 反编译修改软件名字_反编译APK更改文件的文字(修改应用名称)
  6. A.I. Wiki 人工智能
  7. Python图像旋转任意角度
  8. igm焊接机器人基本操作_焊接机器人技术讲解.ppt
  9. Skyline软件二次开发初级——8如何在WEB页面中的三维地图上管理信息树
  10. linux kernel directory
  11. Android Toast介绍及用法
  12. My Eighty-first Page - 打家劫舍 - By Nicolas
  13. 百度搜索引擎工作原理解读
  14. 三/五/七/九点二次平滑法
  15. Android解析SRT字幕文件
  16. Python学习之文件13
  17. 【SpringBoot项目实战】之Chrome谷歌浏览器全屏
  18. 最新微信8.0.1抢红包神器-亲测2021年2月11日可用-安卓IOS
  19. 多域名SSL证书是什么意思?
  20. # 一个礼拜学习Ios7816协议 第一天

热门文章

  1. 今天是个特殊的日子,养活我的Java爸爸诞生,发送一波福利!
  2. 洛谷——P3909 异或之积
  3. RedHat 7配置keepalived+LVS实现高可用的Web负载均衡
  4. Spring 定时任务
  5. 使用FileReader对象的readAsDataURL方法来读取图像文件
  6. PDI(Kettle)加速插入数据的速度
  7. CentOS6.4配置Hadoop-2.6.0集群配置安装指南
  8. SSH端口转发的理解(精华)
  9. ASP.NET母版页和内容页之间如何互相传值?
  10. short_open_tag 相关