C++ 資源管理(RAII)--智能指針

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 }
相關文章
相關標籤/搜索