C++語言機制保證當對象創建時候自動調用構造函數,當對象超出作用域時自動調用析構函數。利用RAII機制(即將資源與對象生命週期綁定),我們可以優雅地實現資源管理。
智能指針(std::shared_ptr和std::unique_ptr)是RAII最具代表的實現,使用智能指針,可以實現自動的內存管理,再也不需要擔心忘記delete造成的內存泄漏。毫不誇張的來講,有了智能指針,代碼中幾乎不需要再出現delete了。
內存只是資源的一種,在這裏我們討論一下更加廣義的資源管理。比如說文件的打開與關閉、windows中句柄的獲取與釋放等等。利用RAII原理,我們可以將一個對象(堆內存、Socket連接、文件句柄、數據庫連接等)的生命週期與一個資源管理者綁定,當資源管理者生命週期結束時自動釋放資源。下面給出一個資源管理者類(Keeper)的實現源碼。
//資源管理者類
class Keeper
{
private:
function<void(void)> release;
public:
//構造時傳入釋放資源方法(一般爲lambada表達式)
Keeper(function<void(void)> func)
{
this->release = release;
}
//資源管理者生命週期結束時調用釋放資源方法
~Keeper()
{
release();
}
};
相應的測試代碼如下(點擊查看完整代碼)
class Test
{
public:
Test()
{
puts("創建Test對象");
}
~Test()
{
puts("銷燬Test對象");
}
};
int main(int argc, char** argv)
{
Test* obj = new Test();
//創建一個資源管理者對象
Keeper defer([&](){
if (obj) delete obj;
});
return 0;
}
你們要以將上面的資源管理者(Keeper)類移植到你的項目中去,具體怎麼使用可以參考下面兩個版本的保存文件方法。
傳統方法在返回之前必須顯示調用fclose方法來關閉文件,當分異常支過多時容易漏寫fclose方法,從而導致文件不能關閉。
int save(const string& path, const string& title, const string& content)
{
FILE* fp = fopen(path.c_str(), "w+");
if (fp == NULL) return -1;
if (fputs(title.c_str(), fp) < 0)
{
fclose(fp);
return -1;
}
if (fputs(content.c_str(), fp) < 0)
{
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
將文件對象與資源管理者綁定,當資源管理者析構時自動調用fclose方法關閉文件。
int save(const string& path, const string& title, const string& content)
{
FILE* fp = fopen(path.c_str(), "w+");
if (fp == NULL) return -1;
Keeper keeper([&](){
if (fp) fclose(fp);
});
if (fputs(title.c_str(), fp) < 0) return -1;
if (fputs(content.c_str(), fp) < 0) return -1;
return 0;
}
通過上述對比,我們發現利用RAII原理可以寫出更優雅的代碼,析構函數由編譯器控制調用,你完全不用擔心綁定到資源管理者的對象不會釋放的問題。