C++中的智能指針是用來管理外界資源的,如在堆上邊new的資源,當程序還沒來得及執行delete操作,函數遇到異常了,沒有釋放資源,導致內存泄露 如下代碼:
void fun()
{
A *ptr = new A();
/*...
...*/ //程序可能在這行代碼中拋出異常,導致ptr所指向堆上的資源沒有釋放
delete ptr;
}
智能指針是怎麼解決這個問題的
智能指針在實現上也是一個類,即有構造函數、析構函數,在函數結束之前 類會自動調用析構函數,析構函數會釋放資源,所以當遇到異常時,資源也會被正確的釋放掉。
接下來介紹 auto_ptr、unique_ptr、 share_ptr、weak_ptr四種智能指針
auto_ptr
auto_ptr是弱智能指針 一個資源只能被一個auto_ptr擁有,當智能指針賦值的時候擁有權就會發生轉移,如下代碼
因爲會發生所有權的轉移所以auto_ptr不能用在STL標準容器中
void Fun(auto_ptr<A> &ptr)
{
cout << ptr -> a << endl;
cout << "Fun faction end" << endl;
}
//auto_ptr
void Test()
{
auto_ptr<A> aptr(new A(5));
auto_ptr<A> bptr;
bptr = aptr; //賦值運算符重載函數
auto_ptr<A> cptr(bptr); //拷貝構造函數
Fun(cptr); //會導致aptr失效,將aptr對資源的擁有權轉向了ptr智能指針
cout << cptr -> a << endl;
}
share_ptr:
share_ptr 資源可以被多個指針共享,share_ptr 使用引用計數對資源管理, 不可以調用release(), 調用reset()函數當前的share_ptr會釋放資源的所有權,引用計數減一,當引用計數爲0時,沒有指針指向該資源,資源會被釋放, 因爲資源可以被多個指針指向,所以指針可以賦值、在函數之間傳遞,存放在STL容器裏, use_count() 返回當前指針指向資源的引用計數(use_count()的效率不高)。
share_ptr使用不當會出現循環引用,資源得不到釋放,暫時先不看這個問題。
下邊時使用share_ptr的相關代碼
示範一:
class A{};
shared_ptr<A> aptr(new A());
shared_ptr<A> bptr = aptr;
cout << aptr.use_count() << endl;
輸出
A()
2
~A()
示範二:
A *a = new A();
shared_ptr<A> aptr(a);
shared_ptr<A> bptr = aptr;
//這樣寫會報錯,導致堆上的同一資源被delete了兩次
/*A *a = new A();
shared_ptr<A> aptr(a);
shared_ptr<A> bptr(a);*/
unique_ptr
c++11棄用了auto_ptr,unique_ptr替代了auto_ptr,在同一時刻只有一個unique_ptr指向給定的對象,該智能指針是禁止使用拷貝構造函數的 禁止使用賦值運算符重載函數
- 只能使用move()函數對對象的所有權進行轉移,
- reset()函數用來重新指定資源
- release 釋放所有權
void Fun(unique_ptr<A>& ptr) //必須寫成引用
{
cout<< ptr -> a << endl;
}
//unique_ptr
void Test1()
{
unique_ptr<A> aptr(new A(10));
unique_ptr<A> bptr;
// bptr = aptr; //error
// unique_ptr<A> cptr(aptr); //error
bptr = move(aptr);
cout<< bptr -> a << endl;
aptr.reset(new A(20));
cout<< aptr -> a << endl;
Fun(aptr);
aptr.release();
// cout<< aptr -> a << endl; //error
cout<< "Test1() end" <<endl;
}
weak_ptr
weak_ptr 弱智能指針,能看到資源的引用計數,但是不會去用這個引用計數,使用weak_ptr指向資源不會使引用計數增加。
爲了配合shared_ptr而引入了weak_ptr,weak_ptr是從share_ptr 或 從另一個weak_ptr對象那裏構造,不能直接指向一個新new出來的資源,weaker_ptr可以觀察資源的引用情況,他沒有重載* 和 -> 運算符。 use_count()函數返回資源的引用計數。 在weak_ptr智能指針類中可以有一個成員函數 lcok(); 該函數的作用是從被觀測的對象返回一個可用的shared_ptr對象,可以將這個對象賦值給一個新的shred_ptr智能指針.
示範代碼如下:
//weak_ptr
void Test2()
{
shared_ptr<A> aptr(new A(10));
shared_ptr<A> bptr(aptr);
weak_ptr<A> wptr(aptr);
cout << wptr.use_count() << endl;
shared_ptr<A> cptr = wptr.lock(); //獲得shared_ptr對象
cout << cptr -> a << endl;
cout << wptr.use_count() << endl; //獲得引用計數
aptr.reset(new A(20)); //重新指向一個新的資源
cout << aptr -> a << endl;
aptr.reset(); //指向一個空的資源
bptr.reset();
cptr.reset();
cout << wptr.use_count() << endl;
shared_ptr<A> dptr = wptr.lock(); //當堆上的對象引用計數爲0,被釋放的時候,weak_ptr調用lock()返回一個指針空值(nullptr)
cout << dptr.use_count() << endl;
}
在所有的智能指針中析構函數調用的是delete,不是delete[]; 所以在創建智能指針的時候不可以寫成 new A[10]; 不能new一個數組,導致資源釋放的時候發生錯誤
整體的代碼稍後傳到Github上
下一篇總結強智能指針shared_ptr引起的循環引用,weak_ptr配合shared_ptr的案例之一.
之後會把Effective C++上對智能指針的條例總結到後續博客中