在c++98中,智能指針通過一個模板“auto_ptr”來實現,auto_ptr以對象的方式來管理堆分配的內存,在適當的時間(比如析構),釋放所獲得的內存。這種內存管理的方式只需要程序員將new操作返回的指針作為auto_ptr的初始值即可,程序員不能顯式的調用delete。如 auto_ptr(new int)。
這在一定程度上避免了堆內存忘記釋放造成的問題。不過auto_ptr也有一些缺點(拷貝是返回一個左值,不能調用delete[]等),所以在c++11中被廢棄了。c++11標准照中改用unique_ptr,share_ptr,及weak_ptr等智能指針自動回收堆分配的對象。
下面是一個用c++11中智能指針實現的一個例子:
#include <memory> #include <iostream> using namespace std; int main() { unique_ptr<int> up1(new int(11)); //無法復制的unique_ptr unique_ptr<int> up2 = up1; //不能通過編譯 cout << *up1 << endl; //11 unique_ptr up3 = move(up1); //現在up3是數據唯一的unique_ptr只能指針 cout << *up3 << endl; //11 cout << *up1 << endl; //運行時錯誤 up3.reset(); //顯示釋放內存 up1.reset(); //不會導致運行時錯誤 cout << *up3 << endl; //運行時錯誤 share_ptr<int> sp1(new int(22)); share_ptr<int> sp2 = sp1; //OK cout << *sp1 << endl; //22 cout << *sp2 << endl; //22 sp1.reset(); cout << *sp2 << endl; //22 }
例子使用兩種不同的智能指針unique_ptr和share_ptr來自動釋放堆對象的內存。在析構或者調用reset都能釋放對象。
unique_ptr與所指的對象內存綁定緊密,不能與其他的unique_ptr類型的指針共享所指對象的內存。每個unique_ptr都唯一的擁有所指對象的內存。但是這種所有權可以通過標准庫的move函數來轉移,如unique_ptr up3 = move(up1),一旦轉移成功,原先的 unique_ptr就失去了對象內存的所有權,再使用已經失去權力的unique_ptr就會出現運行錯誤。
從實現上講,unique_ptr是刪除了拷貝構造函數,保留了移動構造函數的封裝類型,程序僅可以使用右值對unique_ptr進行構造,一旦構造成功,右值對象中的指針失效。
share_ptr則不同,它允許多個該智能指針共享的永遠同一堆分配對象的內存。與unique_ptr不同的是在實現上采用了引用計數,所以,一旦某個share_ptr失去了所有權,其他指針並沒有影響,例子中sp1和sp2都共享指針,在sp1 reset之后只會使計數器降低,而不會導致內存的釋放,當sp2 reset之后,導致引用計數器為零,share_ptr才會真正釋放占有的內存空間。
在c++11標准中,除了share_ptr和unique_ptr之外 ,還有weak_ptr。
weak_ptr是一種不控制所指向對象生存期的智能指針,它指向一個shared_ptr管理的對象,卻並不擁有該對象。將一個weak_ptr綁定到shared_ptr不會改變shared_ptr的引用計數。一旦最后一個指向對象的shared_ptr被銷毀,對象就會被釋放,即使有weak_ptr指向對象,對象還是會被釋放。
當我們創建一個weak_ptr時,要用一個shared_ptr來初始化它:
auto p=make_shared_ptr<int>(42); weak_ptr<int> wp(p); //wp弱共享p,p的引用計數未改變
由於對象可能不存在,我們不能使用weak_ptr直接訪問對象,而必須調用lock()。此函數檢查weak_ptr指向的對象是否存在。使用weak_ptr的成員lock,則可返回內存中的一個share_ptr對象,並在所指向的對象無效時候,返回空。
#include <memeroy> #include <iostream> using namespace std; void Check(weal_ptr<int> & wp) { share_ptr<int> sp = wp.lock(); //轉換為share_ptr<int> if(sp != nullptr) { cout << "still" << *sp << endl; } else { cout << "pointer is invalid." << endl; } } int main() { share_ptr<int> sp1(new int(22)); share_ptr<int> sp2 = sp1; weal_ptr<int> wp = sp1;//指向share_ptr<int> 所指對象 cout << *sp1 << endl; //22 cout << *sp2 << endl; //22 Check(wp); //still 22 sp1.reset(); cout << *sp2 << endl; //22 Check(wp); //still 22 sp2.reset(); Check(wp); //pointer is invalid. return 0; }
例子中,sp1和sp2為共享對象,而weak_ptr指針wp也指向該內存,在sp1和sp2都有效的時候,調用weak_ptr的lock函數返回一個可用的share_ptr使用;當sp1和sp2都調用reset之后,會使堆內存的引用計數器降低為0,而一旦計數器降低為0,share_ptr就會釋放內存,使之失效,此時調用weak_ptr的lock時,返回空指針nullptr。