C++中的资源管理

来源:互联网 发布:golang 1.8 新特性 编辑:程序博客网 时间:2024/05/16 13:44

探讨下C++中的资源管理,谈到资源管理,就不得不谈异常安全,正是因为有了异常,才使得资源管理变得更加重要。C++11提供了一套非常好的编程Idom来处理这个问题,C++11的新特*使得这些Idom变得更加易用。

计算中的资源是个非常广泛的概念,内存、锁、文件、Socket等等都是资源,C++中可以通过统一的方式管理这些资源,即RAII。其基本思路非常简单,用类来封装资源,在类的构造函数中获取资源,在类的析构函数中释放资源。使用的时候,把这个类在栈上面实例化出一个对象,当这个对象超出作用域时,这个对象的析构函数会被调用,从而释放资源。正是这个简单的方式,构成了C++资源管理的基础,并且这样的方式是异常安全的,因为:1、如果在对象构造之前发生异常,则资源还没申请,不会有问题;2、如果在类的构造函数中发生异常,C++编译器保证资源不会发生资源泄漏3、在对象构造好之后发生了异常,stack unwinding 的过程中,C++标准要求编译器保证当前栈上面成功构造的对象的析构函数一定会得到调用,内存一定得到释放。

智能指针就是RAII的实现范例,专门用来管理内存,C++11中有三个智能指针:unique_ptrshared_ptrweak_ptrauto_ptr已经是过时的了,它的功能被unique_ptr取代了,后者可以用于STL容器。

对于其它资源,需要用户自己去封装,同样的资源只要封装一次,以后使用起来就方便了。如果嫌每个资源都要用类包装起来麻烦,可以利用ScopeGuard来处理,这个设施由Andrei Alexandrescu发明,刘未鹏在C++11(及现代C++风格)和快速迭代式开发中做了详细介绍。当然了,ScopeGuardC++11(得益于std::functionlambda表达式)下,才达到了非常易用的地步。我这边贴一下SocpeGuard的代*,有兴趣的可以参考刘未鹏的文章。


class ScopeGuard
{
public:
explicit ScopeGuard(std::function<void()> onExitScope)
: onExitScope_(onExitScope), dismissed_(false)
{ }

~ScopeGuard()
{
if(!dismissed_)
{
onExitScope_();
}
}

void Dismiss()
{
dismissed_ = true;
}

private:
std::function<void()> onExitScope_;
bool dismissed_;

private: // noncopyable
ScopeGuard(ScopeGuard const&);
ScopeGuard& operator=(ScopeGuard const&);
};
使用起来很简单,在以C的方式申请一个资源的时候:

HANDLE h = CreateFile(...);
ScopeGuard onExit([&] { CloseHandle(h); });
这样申请资源和释放资源永远写在一起,不会忘记,而且保证资源永远不会泄漏。

说到资源泄漏,当然想到内存泄漏了,Visual C++中可以这样的方式来检测内存泄漏:

cpp文件开始处,添加这么一段代*


#ifdef _DEBUG
#define DEBUG_CLIENTBLOCK new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_CLIENTBLOCK
#endif // _DEBUG
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif // _DEBUG
在某个函数中,添加这么一个函数调用:

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);这样一来,在程序在Debug模式运行结束后,如果有内存泄漏的话,会打印出类似如下的检测信息。

Detected memory leaks!
Dumping objects ->
{197} normal block at 0x00FBCE98, 44 bytes long.
Data: < > 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Object dump complete.
谈了C++资源管理的好处,来吐槽下JavaJava是个喜欢反复的语言,一次编译,到处运行(Write once,run anywhere) ”程序员再也不用担心内存泄漏了纯粹的面向对象语言,诸如此类。其它先不说,就谈谈它的资源管理。Java中对资源的管理分为两种,一是内存,通过gc管理;二是其它资源,通过trycatchfinally来管理。第一个问题,不统一,Java里面不统一的地方还有很多,比如基础数据类型不能作为模板参数、明明是纯粹的面向对象语言却还有基本数据类型等等。第二个问题,trycatchfinally语法太丑了,而且最不爽的是在finally里面还要在try一下,太蛋疼了!第三个问题,内存被JVM管理了,导致gc时机不确定,严重影响Java的普及范围(在一个大型游戏中,正打boss呢,突然gc了,然后,然后就没有然后了)。我承认,Java是个很好用的语言,能很大程度提高程序员的开发效率,特别是JavaEE体系,的确很成熟。我自己也写过不少Java*,最方便的地方是出错了以后,异常栈能够快速帮助我定位到bug。但是Java因为屏蔽了太多底层信息,导致它培养了一大批不太合格的程序员。这里不是说Java程序员不优秀,只是如果一直用Java,不去了解C/C++的话,真的很容易退化。

 

原创粉丝点击