shared_ptr:资源管理利器
如果你还在使用传统的C++,那么可以肯定堆内存的管理让你头痛过!在传统的C++领域,堆内存管理上我们能借用的现成工具就只有auto_ptr。但是很不幸用auto_ptr管理堆内存简直就是个错误。auto_ptr的问题可以归结为两点:
- 不能配合STL容器一起使用。将auto_ptr置于容器中,就是个编译错误(如果是一个编译错误,你得感谢,还好编译期就发现了)
- 不能管理动态数组。auto_ptr只能管理单个对象指针,如果指针是通过new T[num]的方式生成的,那不好意思了,这个就是个埋得比较深的坑了,哪天踩到了,就只能求老天爷了
- 除了内存资源,其他资源无法自动管理。
既然auto_ptr这么不好,那我们还有其他的选择么?这得感谢boost、感谢tr1库。他们引入了多个新的智能指针。有了tr1库,我们就可以告别传统C++了。
tr1库中主要引入了两个智能指针。一个是shared_ptr;一个是weak_ptr。本文主要介绍下shared_ptr。
什么事shared_ptr呢?这个就不多说了,基本的概念参见这里。大致就是一个基于引用计数的智能指针。
那么shared_ptr到底有什么优势呢?首先,很显然得,它必须得解决auto_ptr不能解决的问题:
- 能够和STL容器配合使用。这个不多说,谁用谁知道。
- 能够动态管理堆内存,包括动态数组。
- 能管理其它类型的资源。
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:资源管理利器的更多相关文章
- Android资源管理利器Resources和AssetManager
前言 : Android工程在运行的时候往往需要引用资源.使用 Resources 来获取 res 目录下的各种与设备相关的资源.而使用 AssetManager 来获取 assets 目录下的资源 ...
- Android实现apk插件方式换肤
换肤思路: 1.什么时候换肤? xml加载前换肤,如果xml加载后换肤,用户将会看见换肤之前的色彩,用户体验不好. 2.皮肤是什么? 皮肤就是apk,是一个资源包,包含了颜色.图片等. 3.什么样的控 ...
- RAII惯用法:C++资源管理的利器(转)
RAII惯用法:C++资源管理的利器 RAII是指C++语言中的一个惯用法(idiom),它是“Resource Acquisition Is Initialization”的首字母缩写.中文可将其翻 ...
- [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git
[.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...
- [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)
[.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...
- [.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上)
[.net 面向对象程序设计进阶] (23) 团队开发利器(二)优秀的版本控制工具SVN(上) 本篇导读: 上篇介绍了常用的代码管理工具VSS,看了一下评论,很多同学深恶痛绝,有的甚至因为公司使用VS ...
- shared_ptr 和 unique_ptr
c++11标准废除乐auto_ptr, C++ 标准库智能指针 使用这些智能指针作为将指针封装为纯旧 C++ 对象 (POCO) 的首选项. unique_ptr 只允许基础指针的一个所有者. 除非你 ...
- Effective C++笔记:资源管理
资源:动态分配的内存.文件描述器.互斥锁.图形界面中的字型与笔刷.数据库连接以及网络sockets等,无论哪一种资源,重要的是,当你不再使用它时,必须将它还给系统. 条款13:以对象管理资源 当我们向 ...
- auto_ptr,shared_ptr 智能指针的使用
Q: 那个auto_ptr是什么东东啊?为什么没有auto_array?A: 哦,auto_ptr是一个很简单的资源封装类,是在<memory>头文件中定义的.它使用“资源分配即初始化”技 ...
随机推荐
- Spring中servletFileUpload完成上传文件以及文本的处理
JSP: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEnco ...
- flask笔记3-模板
flask框架使用jinja2模板引擎.简单的说,模板就是一个纯html文件中夹杂着占位符,在渲染模板时用真实变量值替换占位符,就形成了最终的前台页面. 1.模板存放位置: 默认情况下,flask在根 ...
- 当父级是body时,子元素设置position:absolute;定位是根据body还是html呢?
position:absolute 元素相对最近的 position 为 absolute / relative / fixed 的祖先元素(包含块)定位,如果没有这样的祖先元素,则以初始包含块进行定 ...
- ArcGIS创建tpk切片缓存
一. 背景知识 1. tpk是什么? 从地图或底图生成切片,并将切片进行打包从而创建单个压缩的 .tpk 文件.切片包(.tpk)是在地图或栅格数据集中能作为 Web 切片或 Web 高程图层发布的一 ...
- 将windows上的文件同步到linux上
1.首先下载PSCP.exe,下载地址:http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html 2.将PSCP.exe拷贝到C: ...
- PHP Switch case 条件并用实例
众所周知,Switch循环比if...else...循环效率要好的多,当case有相同代码结构的时候,怎么样来简化代码结构,能让代码更具有通用性呢? 在网上找了一下,好多都是复制粘贴,还有的看起来太复 ...
- C# 中正确实现 IDisposable 接口
作用 此接口的主要用途是释放非托管资源. 当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存. 但无法预测进行垃圾回收的时间. 另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知 ...
- gulp教程之gulp-htmlmin
简介: 使用gulp-htmlmin压缩html,可以压缩页面javascript.css,去除页面空格.注释,删除多余属性等操作. 1.安装nodejs/全局安装gulp/本地安装gulp/创建pa ...
- python【6】-函数式编程
一.高阶函数 map,reduce 1.map() 函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回. def f(x): retur ...
- React Native 获取网络数据
getMoviesFromApiAsync() { return fetch('http://facebook.github.io/react-native/movies.json') .then(( ...