2.2. RAII

智能指针的原理基于一个常见的习语叫做 RAII :资源申请即初始化。 智能指针只是这个习语的其中一例——当然是相当重要的一例。 智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放了出来。 这包括程序因为异常而中断,原本用于释放内存的代码被跳过的场景。 用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。 因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。

无论何时,一定得有第二条指令来释放之前另一条指令所分配的资源时,RAII 都是适用的。 许多的 C++ 应用程序都需要动态管理内存,因而智能指针是一种很重要的 RAII 类型。 不过 RAII 本身是适用于许多其它场景的。

  1. #include <windows.h>
  2.  
  3. class windows_handle
  4. {
  5. public:
  6. windows_handle(HANDLE h)
  7. : handle_(h)
  8. {
  9. }
  10.  
  11. ~windows_handle()
  12. {
  13. CloseHandle(handle_);
  14. }
  15.  
  16. HANDLE handle() const
  17. {
  18. return handle_;
  19. }
  20.  
  21. private:
  22. HANDLE handle_;
  23. };
  24.  
  25. int main()
  26. {
  27. windows_handle h(OpenProcess(PROCESS_SET_INFORMATION, FALSE, GetCurrentProcessId()));
  28. SetPriorityClass(h.handle(), HIGH_PRIORITY_CLASS);
  29. }

上面的例子中定义了一个名为 windows_handle 的类,它的析构函数调用了 CloseHandle() 函数。 这是一个 Windows API 函数,因而这个程序只能在 Windows 上运行。 在 Windows 上,许多资源在使用之前都要求打开。 这暗示着一旦资源不再使用之后就应该关闭。 windows_handle 类的机制能确保这一点。

windows_handle 类的实例以一个句柄来初始化。 Windows 使用句柄来唯一的标识资源。 比如说,OpenProcess() 函数返回一个 HANDLE 类型的句柄,通过该句柄可以访问当前系统中的进程。 在示例代码中,访问的是进程自己——换句话说就是应用程序本身。

我们通过这个返回的句柄提升了进程的优先级,这样它就能从调度器那里获得更多的 CPU 时间。 这里只是用于演示目的,并没什么实际的效应。 重要的一点是:通过 OpenProcess() 打开的资源不需要显示的调用 CloseHandle() 来关闭。 当然,应用程序终止时资源也会随之关闭。 然而,在更加复杂的应用程序里, windowshandle 类确保当一个资源不再使用时就能正确的关闭。 某个资源一旦离开了它的作用域——上例中 _h 的作用域在 main() 函数的末尾——它的析构函数会被自动的调用,相应的资源也就释放掉了。