如果你还在使用传统的C++,那么可以肯定堆内存的管理让你头痛过!在传统的C++领域,堆内存管理上我们能借用的现成工具就只有auto_ptr。但是很不幸用auto_ptr管理堆内存简直就是个错误。auto_ptr的问题可以归结为两点:

  1. 不能配合STL容器一起使用。将auto_ptr置于容器中,就是个编译错误(如果是一个编译错误,你得感谢,还好编译期就发现了)
  2. 不能管理动态数组。auto_ptr只能管理单个对象指针,如果指针是通过new T[num]的方式生成的,那不好意思了,这个就是个埋得比较深的坑了,哪天踩到了,就只能求老天爷了
  3. 除了内存资源,其他资源无法自动管理。

既然auto_ptr这么不好,那我们还有其他的选择么?这得感谢boost、感谢tr1库。他们引入了多个新的智能指针。有了tr1库,我们就可以告别传统C++了。

tr1库中主要引入了两个智能指针。一个是shared_ptr;一个是weak_ptr。本文主要介绍下shared_ptr。

什么事shared_ptr呢?这个就不多说了,基本的概念参见这里。大致就是一个基于引用计数的智能指针。

那么shared_ptr到底有什么优势呢?首先,很显然得,它必须得解决auto_ptr不能解决的问题:

  1. 能够和STL容器配合使用。这个不多说,谁用谁知道。
  2. 能够动态管理堆内存,包括动态数组。
  3. 能管理其它类型的资源。

shared_ptr如何管理堆数组?我们先来看一下shared_ptr的一个构造函数:

template<class _Ux, class _Dx>
class shared_ptr(_Ux *_Px, _Dx _Dt);

shared_ptr之所以能管理动态数组的关键就在这个构造函数的第二参数类型_Dx。_Dx类型的对象指定了如何释放_Px指针,_Dt(_Px)。所以,我们可以定义一个仿函数来解决这个问题:

 template <typename T>
struct memory_delete<T[]>
{
void operator ()(T *ptr) const { delete[] ptr; }
};

当然了,如果你使用的编译器版本够高,你可以直接选用default_delete<T[]>来作为_Dx。

到这里,我们也可以发现,_Dx作为一个模板类型,其本质是定义了一个释放器。一个释放器意味着不管_Px是什么指针,只要有对于的释放方式,你都可以用shared_ptr来进行管理。只要将释放的逻辑写成上面的仿函数形式即可(如果编译器支持,你也可以用ambda表示直接表述,或者只用function+bind的形式)。

所以,用malloc分配的内存,我们的释放器实现时,需要调用free。如果是其他第三方类库返回的对象指针,比方说libevent的event_base_new,我们的释放器实现时,需要调用event_base_free。

所以,shared_ptr可以动态管理资源。

关于shared_ptr最基础的部分差不多就介绍完了。下面说一点应用,我们从线程的角度来入手。

线程对于每一个程序员都不陌生。线程在使用上比较让人恼火的一件事情就是对象的跨线程使用。要保证对象的跨线程使用,要么你的对象是一个全局对象;要么你这个对象就是个堆上的对象,通过指针在多个线程中使用。后面这种方法绝对是常用的手段之一。而这种手段恰恰又是特别地恼火。怎么说呢?

在服务端开发中,对象指针的多线程使用最难搞清楚的情况是,我怎么知道我现在用的这个指针所指向的对象还活着?为什么这么说呢。资源有分配就会有释放,当某个线程执行到释放的逻辑时,这个线程根本无法知道它释放完后,其他线程是否持有这个对象指针;同时,持有这个对象指针的线程也没有什么有效的方法能够知道其他某个线程正在是否这个指针指向的对象。

  • 使用if?开玩笑么?if只是判断指针空不空,它哪知道指向的那个内存是否有释放呢?所以,插一句话就是,在raw pointer上应用if根本毫无意义。if测试后发现指针非空,但是程序还是挂了,让人费解。
  • 还有第二种方法么?给指针一个标志位?朦胧感觉,好像可以。如果某个线程把这个指针干掉了,设置下标志位,其他线程使用这个指针时,先判断下这个标志位。

所以,解决这个问题的本质就是,当某个线程把共享指针干掉后,必须能想办法通知到其他使用该对象的线程。

要解决raw pointer的这个问题,我们必须得引入一个间接层,或者说一个代理。这个代理的生命周期必须长于这个raw pointer。多线程访问这个raw pointer,必须通过这个代理来进行。包括释放这个对象也必须通过这个代理来进行。既然如此,那shared_ptr就是我们的理想选择之一了。

又因为shared_ptr在构造的时候就能够指定如果析构这个对象,所以当对象不被任何线程使用时,这个对象就会自动被析构,并且是正确地被析构。你完全不用担心使用的这个对象是否已经被析构,只要有人在用,它就是活着的。这里,唯一需要注意的就是,shared_ptr很可能延长了对象的生命周期。如果这个不是问题,那么这个解决方案就没有问题。

事实上这个释放器的威力还远不止这些。我们知道,如果一个资源是通过某个DLL中的方法生成的,那么这个资源的释放函数必须也要由这个DLL显示提供,并通过该释放函数释放资源。一旦忘了这条准则,当我们的DLL跟新后,就很有可能遇到莫名其妙的运行时问题。而shared_ptr的释放器能很好得帮我们解决这个问,只要将这个shared_ptr对象从DLL中返回出来就可以了,资源的释放在DLL内部构造shared_ptr对象时就指定好。那么就万事OK了。是不是很方便(当然,前提条件还是有的,就是shared_ptr的二进制必须兼容)?

shared_ptr如此强大,那么在使用上还有没有其他要注意的点?

首先,我们要知道,shared_ptr是引用计数型智能指针。引用计数要考虑的一个大问题就是循环引用。简单地描述这个问题就是,你有一个管理类,管理了一波指针,他们会被跨线程使用,所以,你把他们声明为shared_ptr。这些指针对象内部同时也有一个指向管理类对象的指针。因为管理类也会被多线程使用,所以你把这个指针也设计成shared_ptr。OK,你循环引用了,这些对象都不会自动销毁了。要解决这个问题,你需要恰当得使用weak_ptr。怎么用,前面的那个链接已经给出了基本的原则。

shared_ptr:资源管理利器的更多相关文章

  1. Android资源管理利器Resources和AssetManager

    前言  : Android工程在运行的时候往往需要引用资源.使用 Resources 来获取 res 目录下的各种与设备相关的资源.而使用 AssetManager 来获取 assets 目录下的资源 ...

  2. Android实现apk插件方式换肤

    换肤思路: 1.什么时候换肤? xml加载前换肤,如果xml加载后换肤,用户将会看见换肤之前的色彩,用户体验不好. 2.皮肤是什么? 皮肤就是apk,是一个资源包,包含了颜色.图片等. 3.什么样的控 ...

  3. RAII惯用法:C++资源管理的利器(转)

    RAII惯用法:C++资源管理的利器 RAII是指C++语言中的一个惯用法(idiom),它是“Resource Acquisition Is Initialization”的首字母缩写.中文可将其翻 ...

  4. [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git

    [.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...

  5. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

  6. [.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上)

    [.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上) 本篇导读: 上篇介绍了常用的代码管理工具VSS,看了一下评论,很多同学深恶痛绝,有的甚至因为公司使用VS ...

  7. shared_ptr 和 unique_ptr

    c++11标准废除乐auto_ptr, C++ 标准库智能指针 使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项. unique_ptr 只允许基础指针的一个所有者. 除非你 ...

  8. Effective C++笔记:资源管理

    资源:动态分配的内存.文件描述器.互斥锁.图形界面中的字型与笔刷.数据库连接以及网络sockets等,无论哪一种资源,重要的是,当你不再使用它时,必须将它还给系统. 条款13:以对象管理资源 当我们向 ...

  9. auto_ptr,shared_ptr 智能指针的使用

    Q: 那个auto_ptr是什么东东啊?为什么没有auto_array?A: 哦,auto_ptr是一个很简单的资源封装类,是在<memory>头文件中定义的.它使用“资源分配即初始化”技 ...

随机推荐

  1. JPA,EclipseLink 缓存机制学习(一) 树节点搜索问题引发的思考

    最近在项目在使用JPA+EclipseLink 的方式进行开发,其中EclipseLink使用版本为2.5.1.遇到一些缓存方面使用不当造成的问题,从本篇开始逐步学习EclipseLink的缓存机制. ...

  2. Asp.Net_<%%>模式常用语法

    1.//弹出对话框.点击转向指定页面 Response.Write("<script>window.alert('该会员没有提交申请,请重新提交!')</script> ...

  3. 【oracle】 oracle学习笔记1--安装与登录

    由于机器配置原因,加上也是自学,所以就没必要安装专业版的oracle,于是就安装的oracle xe版本 下载地址:http://www.oracle.com/technetwork/database ...

  4. Oracle ORA-12154: TNS: 无法解析指定的连接标识符”错误

    主要原因: 1.监听服务没有起起来.windows平台个一如下操作:开始---程序---管理工具---服务,打开服务面板,启动oraclehome92TNSlistener服务. 2.database ...

  5. 纯CSS tooltip 提示

    一般的tooltip,使用超链接的title,或者是css+javascript生成. 如果页面布局合理,样式结构清晰,可以使用纯CSS的提示. demo如下: a.tooltip { positio ...

  6. mybatis if test 不为空字符串或null

    <if test="type !=null and type !=''"> AND l.type=#{type,jdbcType=INTEGER} </if> ...

  7. 未能加载文件或程序集“Newtonsoft.Json”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配

    引用第三方的 fineui 库依然使用旧版本导致.更换 fineui为新版,或找到源码更改引用 为新版,问题解决.

  8. $(window).height()获取到的高度不对

    关于这个问题,网上有人说这是webkit内核的浏览器导致的,且只有这种浏览器会出现,只需要将html5的声明整成标准的带有<!doctype>的声明的就行了,但是本人亲测仍然是不行的,所用 ...

  9. Rank() 、DENSE_RANK()、NTILE(n)的用法-转

    Rank() over()/DENSE_RANK()  over()的用法 1.Rank() over()/DENSE_RANK()  over() 这两个函数与ROW_NUMBER()函数类似,因为 ...

  10. 14073102(CCDIKRecoil)

    [目标] CCDIKRecoil [思路] 1 CCDIK和Recoil的结合 2 Recoil的回弹机制,逐渐回到原来位置 3 添加一个Recoil基类 [步骤] 1 将\Src\GameFrame ...