最近一段時間一直在忙公司的項目, c++程序寫的比較多,在寫程序的過程中考慮的比較多的就是動態內存的釋放問題,所謂的動態內存指的是在堆上分配的內存(棧上的內存由程序本身負責分配和釋放)。同java語言不同,c++不提供內存垃圾回收機制,它把內存管理的任務交給了程序員,也就是說每次程序員new或者malloc出來的內存,都需要去手動的釋放它(delete或者free),否則就會造成內存洩露,這大大的加大了開發的難度和風險。
有些c++程序員認為只要他每次分配內存時都能注意到去手動釋放它,應該就能有效的避免內存的洩露,這種想法其實是大錯特錯的,且不說他是否能關注到所有使用到動態內存分配的地方,程序流程過於復雜或者程序出現異常過早退出等情況都容易造成delete或者free語句不能正常執行,從而導致內存洩露。以下兩個簡單的例子就很直觀的說明了這種情況:
1、
void function() {
Object obj = new Object();
if (condition) {
return; // 如果condition為true則函數返回,obj分配的內存將得不到釋放
}
delete obj;
}
2、
void function() {
Object obj = new Object();
if (condition) {
throw exception; // 如果拋出異常,delete obj語句仍然得不到執行
delete obj;
return;
}
delete obj;
}
可以看出,不論程序員多麼的小心翼翼,c++內存洩露的問題還是時不時的困擾著程序員。為了有效的解決內存洩露問題,人們提出了智能指針的概念,智能指針其實就是一個管理棧內存資源的一個對象,由它來負責內存資源的分配和釋放,其核心思想其實是很簡單的:棧內存資源分配後立即由智能指針接管,智能指針在析構函數中對棧內存資源進行釋放。為了更好的理解智能指針的概念,我們首先來看下最簡單的智能指針實現—c++標准庫提供的std::auto_ptr,它能夠方便的管理一個堆內存對象:
Struct stObj {
int age;
std::string name;
stObj(int age, std::string name) : age(age),name(name) {}
};
void autoPtrDemo1{
std::auto_ptr<stObj> p_Obj(new stObj(18, “jac”)); // 新建一個智能指針管理stObj對象
cout << p_Obj->age << p_Obj->name; // 使用operator->調用內部對象的屬性
p_Obj.get()->age = 20; // get()返回內部對象的指針
*(p_Obj).name = “jim”; // operator*返回內部對象
} // 函數結束後,析構棧對象p_Obj,p_Obj在析構函數中釋放stObj的內存
從上面的例子中,我們可以看出智能指針可以很好的幫助我們管理內存,再也不需要顯式的去使用delete來釋放內存了。當然std::auto_ptr只是一個功能簡單的入門級智能指針,它在很多方面都存在不足,看一下下面這個例子:
void autoPtrDemo2{
std::auto_ptr<stObj> p_Obj(new stObj(18, “jac”)); // 新建一個智能指針管理stObj對象
std::auto_ptr<stObj> p_Obj1;
p_Obj1 = p_Obj;
p_Obj1->name = “jim”; // 正常
p_Obj->name = “jim”; // 程序崩潰
}
上面的例子乍看之下其實沒啥問題,但是由於std::auto_ptr的復制行為會導致指針的所有權轉移,因此p_Obj的指針將懸空,使用它將會導致程序崩潰,十分危險。std:: auto_ptr的這種古怪行為能夠很容易的讓程序員不知不覺掉入陷阱當中,更令人感到可怕的是,std:: auto_ptr的這種古怪行為還不在少數,在這裡我就不一一的講述了,有興趣的小盆友可以自己去看看資料。總結下自己使用std:: auto_ptr的心得:
1、 在確定對象的引用只有一個時且不會進行參數傳遞時,可以考慮使用std:: auto_ptr
2、 std:: auto_ptr最好不要進行復制,賦值,release等操作,且不要當成參數進行傳遞
3、 std:: auto_ptr對象不能放入vector等容器對象中