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>头文件中定义的.它使用“资源分配即初始化”技 ...
随机推荐
- 工作中最常用的Excel函数公式大全
电脑那些事儿2016-05-18 22:23:02微软 公式 工作阅读(22574)评论(1) 声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场.举报 Wo ...
- Linux 常见问题解决办法
1.用户切换后显示 -bash-4.1 $ 表示该用户下的配置文件缺失,切换到该用户下(-bash-4.1),使用命令查(pwd)看用户所在位置.如 /home/gpadmin. 使用 cp -a / ...
- latex 竖排子图的生成
latex命令如下: 需要的包为: \usepackage{graphicx} \usepackage{subfigure} \begin{figure*}%加*的作用是跨栏(双栏和单栏latex的区 ...
- iOS 动画组
其实早在一个多月以前就已经实现了动作组播放的功能,不过当时感觉好像没有什么难度并没有放在心上,今天突然要用到动画组,发现已经忘记了,所以又将原来的代码翻出来看了下.最后决定还是写下来,以备不时之需.动 ...
- asp.net用户自定义控件传参
asp.net自定义控件传参的方式有2中: ①字段的方式 在自定义控件的.ascx.cs中定义一个字段,然后在调用页面的page_load方法里面传入参数. 如 在自定义控件中设置字段 publ ...
- C语言课程学习的总结
C语言课程学习的总结 学习C程序这门课一年了,这是我们学的第一门专业课.在大学里,C语言不但是计算机专业的必修课程而且也是非计算机专业学习计算机基础的一门必修课程.所以作为我这个计算机专业的学生来说当 ...
- 05-String动手动脑问题及课后实验性问题总结
一.请运行以下实例代码StringPool.java,查看其输出结果.如何解释这样的输出结果?从中你能总结出什么? (1)在Java中,内容相同的字符常量("Hello")只保存一 ...
- ionic 通过PouchDB + SQLite来实现app的本地存储(Local Storage)
首先声明,本教程参考国外网站(http://gonehybrid.com/how-to-use-pouchdb-sqlite-for-local-storage-in-your-ionic-app/) ...
- 使用 AngularJS & NodeJS 实现基于token 的认证应用(转)
认证是任何 web 应用中不可或缺的一部分.在这个教程中,我们会讨论基于 token 的认证系统以及它和传统的登录系统的不同.这篇教程的末尾,你会看到一个使用 AngularJS 和 NodeJS 构 ...
- MVC5 + EF6 完整入门教程三:EF来了
期待已久的EF终于来了 学完本篇文章,你将会掌握基于EF数据模型的完整开发流程. 本次将会完成EF数据模型的搭建和使用. 基于这个模型,将之前的示例添加数据库查询验证功能. 文章提纲 概述 & ...