手寫代碼是理解C++的最好辦法,以幾個例子說明C++四個智能指針的用法,轉載請注明出處。
一、auto_ptr
auto_ptr這是C++98標准下的智能指針,現在常常已經被C++標准的其他智能指針取代。它的缺點是在轉移所有權后會使運行期不安全。C++11新標准,用unique_ptr來代替auto_ptr原有功能,其用法介紹見第四部分unique_ptr。
#include <iostream> #include <memory> #include <string> using namespace std; void main(){ auto_ptr<string> country[5] = { auto_ptr<string>(new string("USA")), auto_ptr<string>(new string("CHN")), auto_ptr<string>(new string("RUS")), auto_ptr<string>(new string("FRA")), auto_ptr<string>(new string("GRB")) }; auto_ptr<string> pwin; pwin = country[2]; //將所有權從country[2]轉讓給pwin,此時country[2]不再引用該字符串從而變成空指針,在運行到循環時就會崩潰 for (int i = 0; i < 5; ++i) cout << *country[i] << endl;//運行到[2]時崩潰,因為country[2]為空 cout << "The best is " << *pwin << endl; system("pause"); }
二、share_ptr
share_ptr是C++11新添加的智能指針,它限定的資源可以被多個指針共享。
用法見下例:
#include <iostream> #include <memory> #include <string> using namespace std; void fun(){ shared_ptr<string> pa(new string("CHN")); shared_ptr<string> pb(new string("USA")); cout << "*pa " << *pa << endl;//CHN cout << "pa.use_count " << pa.use_count() << endl;//1 cout << "*pb " << *pb << endl;//USA cout << "pb.use_count " << pb.use_count() << endl;//1 pa = pb; cout << *pa << endl;//USA cout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一個資源USA了,該資源的計數為2,所以pb、pb都輸出2 cout << "pb.use_count " << pb.use_count() << endl;//2 pa.reset(); pb.reset(); cout << "pa.use_count " << pa.use_count() << endl;//0 cout << "pb.use_count " << pb.use_count() << endl;//0 } void main() { fun(); system("pause"); }
與數組相結合應用,見另一個例子:
#include <iostream> #include <memory> #include <string> using namespace std; void main(){ shared_ptr<string> country[5] = { shared_ptr<string>(new string("USA")), shared_ptr<string>(new string("CHN")), shared_ptr<string>(new string("RUS")), shared_ptr<string>(new string("FRA")), shared_ptr<string>(new string("GRB")) }; shared_ptr<string> pwin; cout << pwin.use_count() << endl;//輸出0 pwin = country[2]; /*使用shared_ptr時運行正常,因為shared_ptr采用引用計數,pwin和films[2]都指向同一塊內存, 在釋放空間時因為事先要判斷引用計數值的大小因此不會出現多次刪除一個對象的錯誤。 從名字share就可以看出了資源可以被多個指針共享,它使用計數機制來表明資源被幾個指針共享。 可以通過成員函數use_count()來查看資源的所有者個數。 */ cout << pwin.use_count() << endl;//輸出2 for (int i = 0; i < 5; ++i) cout << *country[i] << endl;//運行到[2]時崩潰,因為country[2]為空 cout << "The best is " << *pwin << endl; system("pause"); }
三、weak_ptr
weak_ptr是一種用於解決shared_ptr相互引用時產生死鎖問題的智能指針。如果有兩個shared_ptr相互引用,那么這兩個shared_ptr指針的引用計數永遠不會下降為0,資源永遠不會釋放。weak_ptr是對對象的一種弱引用,它不會增加對象的use_count,weak_ptr和shared_ptr可以相互轉化,shared_ptr可以直接賦值給weak_ptr,weak_ptr也可以通過調用lock函數來獲得shared_ptr。
先看一下兩個shared_ptr指針互相引用導致的資源釋放失敗的例子:
#include <iostream> #include <memory> #include <string> using namespace std; class B; class A { public: shared_ptr<B> pb_; ~A() { cout << "A delete\n"; } }; class B { public: shared_ptr<A> pa_; ~B() { cout << "B delete\n"; } }; void fun(){ shared_ptr<B> pb(new B()); cout << "pb.use_count " << pb.use_count() << endl;//1 shared_ptr<A> pa(new A()); cout << "pa.use_count " << pa.use_count() << endl;//1 pb->pa_ = pa; cout << "pb.use_count " << pb.use_count() << endl;//1 cout << "pa.use_count " << pa.use_count() << endl;//2 pa->pb_ = pb; cout << "pb.use_count " << pb.use_count() << endl;//2:由於share_ptr是共享資源,所以pb所指向的資源的引用計數也會加1 cout << "pa.use_count " << pa.use_count() << endl;//2 }//程序結束時,沒有調用A和B的析構函數 void main() { fun(); system("pause"); }
而使用weak_ptr:把A中的shared_ptr<B> pb_改為weak_ptr<B> pb_weak,這樣改為了弱引用,傳遞時不會增加pb引用計數use_count()的值,所以最終能夠使A、B資源正常釋放:
#include <iostream> #include <memory> #include <string> using namespace std; class B; class A { public: weak_ptr<B> pb_weak; ~A() { cout << "A delete\n"; } }; class B { public: shared_ptr<A> pa_; ~B() { cout << "B delete\n"; } void print(){ cout << "This is B" << endl; } }; void fun(){ shared_ptr<B> pb(new B()); cout << "pb.use_count " << pb.use_count() << endl;//1 shared_ptr<A> pa(new A()); cout << "pa.use_count " << pa.use_count() << endl;//1 pb->pa_ = pa; cout << "pb.use_count " << pb.use_count() << endl;//1 cout << "pa.use_count " << pa.use_count() << endl;//2 pa->pb_weak = pb; cout << "pb.use_count " << pb.use_count() << endl;//1:弱引用不會增加所指資源的引用計數use_count()的值 cout << "pa.use_count " << pa.use_count() << endl;//2 shared_ptr<B> p = pa->pb_weak.lock(); p->print();//不能通過weak_ptr直接訪問對象的方法,須先轉化為shared_ptr cout << "pb.use_count " << pb.use_count() << endl;//2 cout << "pa.use_count " << pa.use_count() << endl;//2 }//函數結束時,正確的情況下,應該調用A和B的析構函數 /*資源B的引用計數一直就只有1,當pb析構時,B的計數減一,變為0,B得到釋放, B釋放的同時也會使A的計數減一,同時pa自己析構時也會使資源A的計數減一,那么A的計數為0,A得到釋放。 */ void main() { fun(); system("pause"); }
四、unique_ptr
unique_ptr 是一個獨享所有權的智能指針,它提供了嚴格意義上的所有權。它取代了C++98中的auto_ptr。
用法和auto_ptr類似,詳情見一下代碼:
#include <iostream> #include <memory> #include <string> using namespace std; unique_ptr<string> fun2(){ return unique_ptr<string>(new string("RUS")); } void fun(){ unique_ptr<string> pa(new string("CHN")); //unique_ptr沒有use_count()方法 unique_ptr<string> pb(new string("USA")); pb = move(pa); //p2=p1;//錯誤,不能直接用等於號 if (pa == nullptr) cout << "pa現在為空" << endl; cout << "*pb " << *pb << endl;//pb變成了“CHA” string* ps = pb.release();//清空當前智能指針所指的資源對象,並返回指針 cout << "*ps " << *ps << endl;//ps變成了“CHA” pa.reset(ps);//重置指向另一個對象 cout << "*pa " << *pa << endl;//pa變成了“CHA” pb = fun2();//接收函數的返回值可以用等於號,因為使用了移動構造函數 cout << "*pb " << *pb << endl;//pb變成了“RUS” } void main() { fun(); system("pause"); }