1. 智能指針(Smart Pointer)ios
i. 是存儲指向動態分配(堆)對象指針的類c++
ii. 在面對異常的時候格外有用,由於他們可以確保正確的銷燬動態分配的對象api
iii. RAII類模擬智能指針,見備註函數
2. C++11提供瞭如下幾種智能指針,位於頭文件<memory>,它們都是模板類this
i. std::auto_ptr(複製/賦值)spa
ii. std::unique_ptr c++11指針
iii.std::shared_ptr c++11c++11
iv.std::weak_ptr c++11code
g++ -std=c++11 xx.cc對象
3. std::auto_ptr在構造時獲取對某個對象的全部權(ownership),在析構時釋放該對象
4. std::auto_ptr要求其對「裸」指針的徹底佔有性 -> 在拷貝構造或賦值操做時,會發生全部權的轉移
5. 自己存在缺陷
6. std::unique_ptr是一個獨享全部權的智能指針,它提供了一種嚴格語義上的全部權,包括:
i. 擁有它所指向的對象
ii. 沒法進行復制、賦值操做(例題)
iii.保存指向某個對象的指針,當它自己被刪除釋放的時候,會使用給定的刪除器釋放它指向的對象
iv.具備移動(std::move)語義,可作爲容器元素
7. std::shared_ptr是一個引用計數智能指針,用於共享對象的全部權
i. 引進了一個計數器shared_count,用來表示當前有多少個智能指針對象共享指針指向的內存塊
ii. 析構函數中不是直接釋放指針對應的內存塊,若是shared_count大於0則不釋放內存只是將引用計數減1,只是計數等於0時釋放內存
iii. 複製構造與賦值操做符只是提供通常意義上的複製功能,而且將引用計數加1.
iv. 在堆內存中只有一份申請的資源,可是有好幾個對象都是指向這個內存的資源的。
v. Shared_ptr的功能就是讓不具備值語義的對象擁有值語義,假如說一個對象自己是不但願被複制的,可是要是把這個對象交給shared_ptr管理的時候,使用這個指針的時候就能夠對對象進行復制,實際不是真實的對象複製,而是經過一個引用計數的方式去使用。
8. 問題:會有一個問題就是循環引用,會致使內存泄漏。
9. std::shared_ptr是強引用智能指針
10. std::weak_ptr 是弱引用智能指針
11. 強引用,只要有一個引用存在,對象就不能被釋放
12. 弱引用,並不增長對象的引用計數,但它知道對象是否存在。若是存在,提高爲shared_ptr成功;不然,提高失敗
13. 經過weak_ptr訪問對象的成員的時候,要提高爲shared_ptr
1 auto_ptr.cc 2 3 #include<iostream> 4 #include<memory> 5 6 int main(void) 7 { 8 double *pd = new double(7.77); 9 std::auto_ptr<double> apd(pd); //apd自己是個對象,由於它重載了星號訪問運算符,因此能夠加* 用。 10 //或者std::auto_ptr<double> apd(new double(7.77)); 11 12 std::cout << 「*apd=」 << *apd <<std::endl; 13 //經過* 去訪問的時候,就至關於對他所託管的指針(pd)所指向的對象進行訪問 . 14 15 //std::cout << 「apd = 」 << apd << std::endl; //會出錯,對象不能這樣直接打印 16 17 std::cout << 「apd.get() = 」 << reinterpret_cast<long>(apd.get()) << std::endl; 18 19 double *pd = new double(8.88); 20 std::auto_ptr<double> apd2(pd); 21 std::cout << 「pd = 」 << reinterpret_cast<long>(pd) << std::endl; 22 std::cout << 「apd2.get() = 」 << reinterpret_cast<long>(apd2.get()) << std::endl; //經過get()能夠得到原生的指針所在的地址。 23 24 int *pi = new int(7); 25 std::auto_ptr<int> api1(pi); 26 std::auto_ptr<int> api2(api1); //複製, 發生了全部權的轉移。首先把api1裸指針所指向的值交給了api2, 同時又把api1所指向的值設爲了空。至關於api1對pi的全部權徹底交給了api2。發生全部權的轉移,與常規的認識矛盾。自己有缺陷,不推薦使用。 27 // std::auto_ptr要求其對「裸」指針的徹底佔有性在拷貝構造或賦值操做時,會發生全部權的轉移 28 29 std::cout << 「*api1= 」 << *api1 <<std::endl; //如今訪問api1指向的值,發現沒有了,發生一個段錯誤。 30 std::cout << 「*api2 = 」 << *api2 <<std::endl; 31 32 33 return 0; 34 }
1 unique_ptr.cc 2 3 #include<iostream> 4 #include<memory> 5 #include<vector> 6 7 8 std::unique_ptr<int> getVal() //這裏返回一個unique_ptr對象,這個對象是個右值。 9 { 10 std::unique_ptr<int> up(new int(66)); 11 return up; 12 } 13 14 int main(void) 15 { 16 // 沒法進行復制、賦值操做 17 std::unique_ptr<int> ap(new int(99)); 18 //std::unique_ptr<int> one(ap); //編譯出錯,不可以進行復制。 19 20 std::unique_ptr<int> two; 21 //two = ap; //編譯出錯,不能進行賦值。 22 23 std::cout << 「*ap = 」 << *ap << std::endl; 24 std::cout << 「ap.get() = 」 << reinterpret_cast<long>(ap.get()) << std::endl; //獲取指針的值 25 26 //能夠進行移動構造和移動賦值操做 27 std::unique_ptr<int> up = getVal(); //getVal()函數返回一個右值,這個右值會優先綁定到右值引用上去。unique_ptr是具備移動語義的,意思就是說它提供了一個移動構造函數和一個移動賦值函數。而這裏就優先調用了移動賦值函數。並無調用複製構造函數。 28 std::cout << 「*up = 」 << *up << std::endl; 29 30 31 //實際上上面的操做有點相似於以下操做 32 Unique_ptr<int> up(int new int(99)); 33 Unique_ptr<int> uPtr2 = std::move(up); //這裏是顯式的全部權轉移。把up所指的內存轉給uPtr2了,而up再也不擁有該內存。 34 35 36 37 //能夠做爲容器的元素(就是由於具備移動語義) 38 std::unique_ptr<int> sp(new int(55)); //sp如今是一個左值,就要綁定到複製構造函數上面去,由於複製構造函數的參數是一個左值引用。 39 std::vector<std::unique_ptr<int> vec; 40 //vec.push_back(sp); //會出錯,由於會調用複製構造函數 41 vec.puch_back(std::move(sp)); //將左值轉成右值引用,這時就會調用 移動構造函數,而不是 複製構造函數。 42 std::cout << *vec[0] << std::endl; //打印剛添加的值。 43 44 return 0; 45 }
1 Shared_ptr.cc 2 3 #include<iostream> 4 #include<memory> 5 6 class Child; 7 class Parent; 8 9 typedef std::shared_ptr<Child> Child_ptr; 10 typedef std::shared_ptr<Parent> Parent_ptr; 11 12 class Child 13 { 14 public: 15 Child() 16 { 17 std::cout << 「Child()」 << std::endl; 18 } 19 ~Child() 20 { 21 std::cout << 「~Child()」 << std::endl; 22 } 23 //private: 24 Parent_ptr parent_; 25 }; 26 27 class Parent 28 { 29 public: 30 Parent() 31 { 32 std::cout << 「Parent()」 << std::endl; 33 } 34 ~Parent() 35 { 36 std::cout << 「~Parent()」 << std::endl; 37 } 38 39 //private: 40 Child_ptr child_; 41 }; 42 43 int main(void) 44 { 45 Parent_ptr parent(new Parent); //交給shared_ptr進行管理 46 Child_ptr child(new Child); 47 48 std::cout << 「parent’s count = 」 << parent.use_count() << std::endl; 49 std::cout << 「child’s count = 」 << child.use_count() << std::endl; 50 51 std::cout << 「進行復制以後:」 << std::endl; 52 parent->child_ = child; //shared_ptr複製操做 53 std::cout << 「child’s count = 」 << child.use_count() << std::endl; //打印出引用計數爲2. 54 child->parent_ = parent; 55 std::cout << 「parent’s count = 」 << parent.use_count() << std::endl; //打印出引用計數爲2. 56 57 return 0; 58 } 59 //由於相互引用,當程序結束時,引用計數都減一,都成爲1。內存中還有這兩個對象的存在,就不會調用析構函數。這樣就帶來了內存泄漏,是循環引用存在的問題。
1 weak_ptr1.cc 2 3 #include<iostream> 4 #include<memory> 5 6 class Child; 7 class Parent; 8 9 typedef std::shared_ptr<Child> Child_ptr; 10 typedef std::shared_ptr<Parent> Parent_ptr; 11 12 class Child 13 { 14 public: 15 Child() 16 { 17 std::cout << 「Child()」 << std::endl; 18 } 19 ~Child() 20 { 21 std::cout << 「~Child()」 << std::endl; 22 } 23 //private: 24 Parent_ptr parent_; 25 }; 26 27 class Parent 28 { 29 public: 30 Parent() 31 { 32 std::cout << 「Parent()」 << std::endl; 33 } 34 ~Parent() 35 { 36 std::cout << 「~Parent()」 << std::endl; 37 } 38 39 std::weak_ptr<Child> child_; //弱引用 40 }; 41 42 int main(void) 43 { 44 Parent_ptr parent(new Parent); //交給shared_ptr進行管理 45 Child_ptr child(new Child); 46 47 std::cout << 「parent’s count = 」 << parent.use_count() << std::endl; 48 std::cout << 「child’s count = 」 << child.use_count() << std::endl; 49 50 std::cout << 「進行復制以後:」 << std::endl; 51 parent->child_ = child; //child_是weak_ptr,引用計數並無加1 52 std::cout << 「child’s count = 」 << child.use_count() << std::endl; //打印出引用計數爲2. 53 child->parent_ = parent; 54 std::cout << 「parent’s count = 」 << parent.use_count() << std::endl; //打印出引用計數爲2. 55 56 return 0; 57 } 58 //由於使用的是弱引用,複製的時候引用計數不會加1. 程序結束時會調用析構函數。
1 Weak_ptr.cc
2 #include<iostream> 3 #include<memory> 4 5 class X 6 { 7 public: 8 X() {std::cout << 「X()」 << std::endl;} 9 ~ X() {std::cout << 「~X()」 << std::endl;} 10 11 void fun() 12 { 13 std::cout << 「fun()」 << std::endl; 14 } 15 }; 16 17 int main(void) 18 { 19 std::weak_ptr<X> p; 20 { 21 std::shared_ptr<X> p2(new X); //全部new出來的東西都放到這個棧對象p2裏面,由於當這個語句塊結束的時候p2會調用析構函數,咱們在析構函數中加上delete來釋放託管過來的指針。 22 std::cout << 「p2’s count = 」 << p2.use_count() <<std::endl; 23 24 p = p2; //std::weak_ptr 不會增長引用計數 25 std::cout << 「after p = p2 」 <<std::endl; 26 std::cout << 「p2’s count = 」 << p2.use_count() <<std::endl; 27 28 std::shared_ptr<X> p3 = p.lock(); //lock()函數就是用來提高weak_ptr爲shared_ptr的函數。 29 30 if(p3) //提高成功 31 { 32 p3->fun(); 33 std::cout << 「p3’s count = 」 << p3.use_count() <<std::endl; 34 } 35 else //提高失敗 36 { 37 std::cout << 「object has been destroied」 << std::endl; 38 } 39 } 40 41 //new X 已經被釋放了 42 //經過weak_ptr訪問對象的成員的時候,要提高爲shared_ptr 43 std::shared_ptr<X> p4 = p.lock(); 44 if(p4) //提高成功 45 { 46 P4->fun(); 47 std::cout << 「p4’s count = 」 << p4.use_count() <<std::endl; 48 } 49 else //提高失敗 50 { 51 std::cout << 「object has been destroied」 << std::endl; 52 } 53 54 //智能指針做爲棧對象來使用,不要採用堆對象的方式來使用。由於智能指針做爲棧對象來使用,它能夠具備自動管理、自動回收的功能。 55 //智能指針的實現原理:棧對象生命週期結束的時候,會自動調用析構函數。 56 //std::shared_ptr<X> *pthis = new std::shared_ptr<X>(new X); 57 //上述方式就是生成堆對象,不推薦這麼作。並且這時候只能經過顯示調用delete函數,這樣就跟咱們的初衷不符。 58 59 return 0; 60 }