C++如何優雅地釋放資源

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原理可以寫出更優雅的代碼,析構函數由編譯器控制調用,你完全不用擔心綁定到資源管理者的對象不會釋放的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章