天天看點

對RAII資源管理的了解

RAII是C++語言中常見習慣用法,全稱為“Resource Acquisition Is Initialization”意為資源擷取就是初始化。通常用來管理對象記憶體資源,已經比如檔案描述符、互斥鎖等資源。RAII基本原理就是使用局部對象管理資源,依賴于構造函數和析構函數的性質以及它們與異常處理的互動作用。我們考慮如下情況:

class A { ..... };         //某資源對象
A * create(){...} ;        //建立動态對象A 并傳回指針 
//考慮某處理函數f
void f()
{
	A *pImp=create();      
	.....
	
	delete pImp;
}

           

沒錯這代碼看起來沒啥問題,但是考慮中間"....."部分加入一些控制流程語句如:if  else 語句若其中包含return 語言,則必須在return語句傳回之前都加上delete pImp;若分支語言很多則需要大量的delete語句。又例如:在"....."部分中包含其它函數調用導緻異常發生,使得後面delete pImp 語句無法執行,進而導緻記憶體洩漏。在這種情況下RAII技術就非常适合!如下:

void f()
{
	std::shared_ptr<A> pImp(create());   //利用智能指針(基本原理就是利用RAII技術)
	.....        
}
           

上述代碼create傳回的資源被當做其管理者shared_ptr的初始值。不論控制流程如何離開區塊,在這之中若無發生shared_ptr對象拷貝、複制等(引用計數情況),一旦對象被銷毀(對象離開作用域)其析構函數自動會被調用,于是自動對其所指對象調用delete語句釋放資源。即使發生異常情況,我們依然無需多操心!

在考慮如下情況:

int go(){....}      //傳回一個處理參數

void processGo(std::shared_ptr<A> pImp,int v){....};   //處理過程
           

假如我們使用如下函數調用寫法:

void processGo(std::shared_ptr<A> (new A),go());

代碼看起來也沒有啥問題,但是上述調用可能造成記憶體洩漏。分析如下:

調用函數processGo() 之前會做以下三件事:

1.調用go()         2.執行“new A”     3.調用shared_ptr構造函數。

C++ 編譯器 對于以上三件事會按照什麼次序完成呢?當然2、3  事件  ,一定是2事件優先于3事件。 但是1事件執行就比較任意了,若按照如下執行次序: 第一步: 執行new A        第二步:調用go()      第三步:調用 shared_ptr構造函數   。

萬一go函數的調用導緻異常,在此情況下new A 傳回的指針将會遺失,因為它尚未被置入shared_ptr内。進而發生洩漏。

避免這類問題發生也很簡單。就是寫成如下形式:

std::shared_ptr<A> pImp(new A); //單獨語句内以智能指針存儲 new 對象

processGo(pImp,go());          //這樣就不至于造成洩漏
           

本文參考于effective C++