如果你还在使用传统的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. shell中三种引号的用法

    1.单引号 所见即所得 例如:var=123 var2='${var}123' echo var2 var2结果为${var}123 2.双引号 输出引号中的内容,若存在命令.变量等,会先执行命令解析 ...

  2. 跨站请求伪造 CSRF / XSRF<二:应用>

    防御的方法主要有两种<java示例> 1.检查Referer字段 HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址.在处理敏感数据请求时,通常来说,Referer字 ...

  3. iOS 开发之使用safari对webview进行调试

    转自:http://www.tuicool.com/articles/ZBFnUbz 使用safari对webview进行调试 时间 2016-02-25 14:35:20  陈斌彬的技术博客 原文  ...

  4. Linux-ubuntu指令使用积累(长期更新)

    alias cat cd cp ls mkdir mv rm sudo tar chmod       1. sudo 系统管理指令.放在其它指令之前使用,允许普通用户在root权限下执行部分或者全部 ...

  5. 在控制台启动服务器时出现:对于服务器soa1_wls, 与计算机oim1相关联的节点管理器无法访问。

    问题:在控制台启动服务器时出现:对于服务器soa1_wls, 与计算机oim1相关联的节点管理器无法访问.原因:nodemanager没有启起来解决方法: 一.对于managedServer于admi ...

  6. 转:详解Eclipse断点

    详解Eclipse断点(原) 详解Eclipse断点 大家肯定都用过Eclipse的调试的功能,在调试的过程中自然也无法避免要使用断点(breakpoint),但不知是否对Eclipse中各类断点都有 ...

  7. js邮箱自动补全

    邮箱自动补全js和jQuery html: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &q ...

  8. HTML5 十大新特性(九)——Web Storage

    H5的webStorage技术一共提供了两个对象:window.sessionStorage和window.localStorage. 一.window.sessionStorage--会话级存储 存 ...

  9. oracle笔记一

    一.Oracle简介 1.Oracle属于关系型数据库,是一款可以在Client/Server模式下运行的RDBMS产品.2009年,Oracle公司收购SUN. 2.Oracle是对象关系型数据库管 ...

  10. Tomcat APR & Linux Optimization

    一.简介 APR(Apache portable Run-time libraries)模式:简单理解,就是从操作系统级别解决异步IO问题,大幅度的提高服务器的处理和响应性能, 也是Tomcat运行高 ...