我的编程习惯:RAII

 

关于RAII(Resource Acquisition Is Initialization),以下两点是关键。

  • 获得资源后立刻放进管理对象(managing object)内: 在获得一笔资源后与同一语句内以它初始化某个管理对象。
  • 管理对象运用析构函数确保资源被释放: 无论控制流如何离开程序块,一旦对象被销毁(例如当对象离开作用域),其析构函数自然会被自动调用,于是资源被释放。

 

一,常见用法

RAII最常见的应用就是智能指针。

我们知道对于程序块中动态分配的堆对象,应该保证当控制流离开程序块时被释放。

  1: void func()
  2: {
  3:    Foo* foo = new Foo;
  4:    ... //这里可能抛出异常,造成delete无法调用
  5:    delete foo;
  6: }

如果new和delete之间抛出异常,就会出现资源泄漏。运用智能指针,可以有效的避免资源泄漏。

  1: void func()
  2: {
  3:    std::auto_ptr<Foo> foo(new Foo); //在单独语句内以堆对象初始化智能指针
  4:    ... //这里即使抛出异常,delete也会随着智能指针的析构函数被调用
  5: }

 

二,扩展用法

其实不仅仅是资源的获取和释放,RAII可以广泛用于保持前后状态的一致。

在用GDI+的时候我们知道要先调用初始化函数,用完了要调用关闭函数。于是我们可以创建一个类,吧初始化函数写进类构造函数,关闭函数写进析构函数。

  1: class GdiplusSwitch
  2: {
  3: public:
  4:    GdiplusSwitch()
  5:    {
  6:       Gdiplus::GdiplusStartupInput startupinput;
  7:       Gdiplus::GdiplusStartup(&_token,&startupinput,NULL); //初始化函数
  8:    }
  9: 
 10:    ~GdiplusSwitch()
 11:    {
 12:       Gdiplus::GdiplusShutdown(_token); //关闭函数
 13:    }
 14: 
 15: private:
 16:    ULONG_PTR _token;
 17: };

这样我们就只需要在用GDI+之前实例化一个类的对象。

  1: void func()
  2: {
  3:    GdiplusSwitch gdi;
  4:    ... //继续做事,当对象离开作用域时,关闭函数会随着析构函数被调用
  5: }

当然你也可以结合智能指针一起用。

  1: void func()
  2: {
  3:    std::auto_ptr<GdiplusSwitch> gdi(new GdiplusSwitch);
  4:    ... //当对象离开作用域时,智能指针析构函数调用delete,delete调用类析构函数,类析构函数最终调用关闭函数
  5: }

 

再举个例子,用MFC调用标准打开文件的模式对话框,可能会引起当前工作目录的改变。这就需要先记下当前目录,在模式对话框之后将被改变的目录再改回来。先来定义一个类,

  1: class WorkDirectoryHolder
  2: {
  3: public:
  4:    WorkDirectoryHolder()
  5:    {
  6:       _wgetcwd(m_workpath,_countof(m_workpath)); //记下当前工作目录
  7:    }
  8: 
  9:    ~WorkDirectoryHolder()
 10:    {
 11:       _wchdir(m_workpath); //改变当前工作目录
 12:    }
 13: 
 14: private:
 15:    WorkDirectoryHolder(const WorkDirectoryHolder&){}
 16:    const WorkDirectoryHolder& operator= (const WorkDirectoryHolder&){}
 17: 
 18: private:
 19:    wchar_t m_workpath[256];
 20: };

然后如此运用:

1: void OnBnClickedOpenButton()

  2: {
  3:    std::auto_ptr<WorkDirectoryHolder> wdh(new WorkDirectoryHolder);
  4:    
  5:    CFileDialog dlgFile(TRUE);
  6:    ...
  7:    dlgFile.DoModal();
  8: 
  9:    ... //对象离开作用域时,工作目录将自动改回
 10: }

 

日常生活中类似的情形也很多见,好比说东西从哪里拿的,用完了再放回那里去。养成好的习惯,可以带来很多便利,同样,好的编程习惯,也可以防止一些不必要错误的产生,从而降低维护成本。

 

-----------------------------------------------------------------

最后放张电子名片看看,嘿嘿,大家果断加我:

 

posted @ 2012-07-14 03:45  richfox  阅读(327)  评论(0编辑  收藏  举报