C++資源管理技術
RAII
是“Resource Acquisition Is Initialization”的首字母縮寫。也稱爲“資源獲取就是初始化”,是c++等編程語言常用的管理資源、避免內存泄露的方法。它保證在任何情況下,使用對象時先構造對象,最後析構對象。
我們在C++中經常使用new申請了內存空間,但是卻也經常忘記delete回收申請的空間,容易造成內存溢出,於是RAII技術就誕生了,來解決這樣的問題。RAII機制是是一種利用對象生命週期來控制程序資源(如內存、文件句柄、網絡連接、互斥量等等)的簡單技術。
當我們在一個函數內部使用局部變量,當退出了這個局部變量的作用域時,這個變量也就別銷燬了;當這個變量是類對象時,這個時候,就會自動調用這個類的析構函數,而這一切都是自動發生的,不要程序員顯示的去調用完成。基於此想法:由於系統的資源不具有自動釋放的功能,而C++中的類具有自動調用析構函數的功能。如果把資源用類進行封裝起來,對資源操作都封裝在類的內部,在析構函數中進行釋放資源。當定義的局部變量的生命結束時,它的析構函數就會自動的被調用,如此,就不用程序員顯示的去調用釋放資源的操作。
確保能運行資源釋放代碼的地方就是在這個程序段(棧)中放置的對象的析構函數了,因爲stack winding會保證它們的析構函數都會被執行。RAII就利用了棧裏面的變量的這一特點。
Stack Winding&Unwinding
當程序運行時,每一個函數(包括數據、寄存器、程序計數器,等等)在調用時,都被映射到棧上。這就是 stack winding。
Unwinding 是以相反順序把函數從棧上移除的過程。正常的 stack unwinding 發生在函數返回時;不正常的情況,比如引發異常,調用setjmp
和longjmp
,也會導致 stack unwinding。可見 stack unwinding 的過程中,局部對象的析構函數將逐一被調用。這也就是 RAII 工作的原理,它是由語言和編譯器來保證的。
RAII的具體做法:在對象構造時獲取資源,接着控制對資源的訪問使之在對象的生命週期內始終保持有效,最後在對象析構的時候釋放資源。藉此,我們實際上把管理一份資源的責任託管給了一個存放在棧空間上的局部對象。
這種做法的好處:
- 不需要顯示地釋放資源
- 採用此方式,對象所需的資源在其生命週期內始終保持有效
在計算機系統中,資源是數量有限且對系統正常運轉具有一定作用的元素。
比如,內存,文件句柄,網絡套接字(network sockets),互斥鎖(mutex locks)等等,它們都屬於系統資源。由於資源的數量不是無限的,有的資源甚至在整個系統中僅有一份,因此我們在使用資源時必須嚴格遵循的步驟是:獲取資源,使用資源,釋放資源。在資源的獲取到釋放之間,我們往往需要使用資源,但常常一些不可預計的異常是在使用過程中產生,就會使資源的釋放環節沒有得到執行。——RAII技術可以很好解決
RAII的實現原理很簡單,利用stack上的臨時對象生命期是程序自動管理的這一特點,將我們的資源釋放操作封裝在一個臨時對象中。
#include <iostream>
using namespace std;
class ArrayOperation
{
public :
ArrayOperation()
{
m_Array = new int [10];
}
void InitArray()
{
for (int i = 0; i < 10; ++i)
{
*(m_Array + i) = i;
}
}
void ShowArray()
{
for (int i = 0; i <10; ++i)
{
cout<<m_Array[i]<<endl;
}
}
~ArrayOperation()
{
cout<< "~ArrayOperation is called" <<endl;
if (m_Array != NULL )
{
delete[] m_Array; // 非常感謝益可達非常犀利的review,詳細可以參加益可達在本文的評論 2014.04.13
m_Array = NULL ;
}
}
private :
int *m_Array;
};
bool OperationA();
bool OperationB();
int main()
{
ArrayOperation arrayOp;
arrayOp.InitArray();
arrayOp.ShowArray();
return 0;
}