程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++智能指針初學小結,智能指針初學小結

C++智能指針初學小結,智能指針初學小結

編輯:C++入門知識

C++智能指針初學小結,智能指針初學小結


  本篇隨筆僅作為個人學習《C++ Primer》智能指針一節後的部分小結,抄書嚴重,伴隨個人理解。主要介紹shared_ptr、make_shared、weak_ptr的用法和聯系。

  C++通過一對運算符 newdelete 進行動態內存管理,new在動態內存中為對象分配空間並返回一個指向該對象的指針,delete接受一個動態對象的指針,銷毀對象並釋放與之相關的內存。然而這樣的動態內存的使用很危險,因為無法確保始終能在合適的時間釋放內存對象。如果忘記釋放內存,可能造成內存洩露;如果在尚有指針引用內存的情況下釋放內存,會產生非法訪問內存的指針。

  C++11中,新的標准庫提供了兩種智能指針smart pointer)類型來更安全地管理對象。智能指針的使用和常規指針類似,只是它們多了自動釋放所指向的對象的功能。兩種指針的區別在於管理底層指針的方式:shared_ptr允許多個指針指向同一個對象,unique_ptr不支持。標准庫還提供了weak_ptr這一弱指針,指向shared_ptr所管理的對象。三種類型都定義在頭文件memory中。

  shared_ptr的使用和vector很相似,在尖括號內說明所指向對象的類型:

shared_ptr<string> p1              // p1是shared_ptr,指向string類型
shared_ptr<list<int>> p2     // p2是shared_ptr,指向list的int

  解引用一個智能指針就能獲得它所指向的對象,在if語句中使用智能指針可以判斷它指向的對象是否為空:

// 如果p1非空,檢查p1是否指向一個空的string對象
if (p1 && p1->empty())
    *p1 = "creat";        // 如果p1非空且指向一個空的string對象,解引用p1,為其賦新值creat

  最安全的分配和使用shared_ptr的方法是調用名為make_shared這一標准庫函數。此函數在動態內存中分配並初始化它,返回指向此對象的shared_ptr。該函數定義在memory中。

  make_shared的定義和shared_ptr相似,必須制定要創建對象的類型,如:

// 指向一個值為1的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>)(1);
// 指向一個值為“www”的string的shared_ptr
shared_ptr<string> p4 = make_shared<string>(3, "w");
// 指向一個初始化的int,值為0
shared_ptr<int> p5 = make_shared<int>)();

也可以使用auto定義對象保存make_shared,可以省去書寫shared_ptr的麻煩。

  shared——ptr中有一個關聯的指示器,稱為引用計數。可以看做一個計數器,每當shared_ptr對象進行拷貝操作,如用一個shared_ptr對象初始化另一個shared_ptr對象、作為函數的實參、作為函數返回值時,引用計數都會遞增(視為數值+1)。當賦予shared_ptr新值或者shared_ptr被銷毀時,引用計數遞減。當引用計數減為0,通過析構函數,shared_ptr自動銷毀所管理的對象,釋放內存。

  需要注意的是,如果多個對象共享底層數據,當某一對象被銷毀,不能單方面銷毀底層數據,例如:

Blob<string> b1;
{ // 新作用域
    Blob<string> b2 = { "x", "b", "b" };
    b1 = b2;
}    // 當離開局部作用域,b2被銷毀,然而b2中的元素xbb並不會被銷毀
     // b1指向最初由b2創建的元素,即“x”, "b", "b",b1依舊可以它們

  weak_ptr是指向shared_ptr管理的對象的一種智能指針,然而它不控制所指向對象的生存期。將一個weak_ptr綁定在shared_ptr上,不會改變shared_ptr的引用計數,一旦最後一個shared_ptr的指向對象被摧銷毀,對象就會被釋放,有無weak_ptr並無卵影響。我的理解是,weak_ptr提供了指向shared_ptr底層數據的功能,控制了shared_ptr對底層數據的訪問。

  因為weak_ptr指向的對象可能不存在(shared_ptr指向的最後一個對象被銷毀時),因而用它不能直接訪問對象,必須調用lock函數檢查其指向的對象是否存在。很容易寫出一個選擇語句進行控制:

auto bb = make_shared<string>(2, 'b');
weak_ptr<string> xbb(bb);
if (shared_pr<int> np = xbb.lock()) {  // np不為空條件成立
    // 在if語句內,np和xbb共享對象    
}

補充weak_ptr相關的函數,便於理解:

w.reset 將w置為空 w.use_count() 與w共享對象的個數 w.expired() 若w.use_count()為0,返回true,否則返回false w.lock() 若w.expired()為true,返回一個空shared_ptr,否則返回一個指向w的對象的shared_ptr

加入《C++ Primer 5th》中的12.19題參照,題中和“智能指針和異常”並未在本篇隨筆中介紹

 1 #include <iostream>    
 2 #include <string>
 3 #include <vector>
 4 #include <memory>
 5 #include <initializer_list>
 6 using namespace std;
 7 using std::string;
 8 using std::vector;
 9 class StrBlobPtr;
10 class StrBlob {
11 public:
12     friend class StrBlobPtr;            // 友元
13     StrBlobPtr begin();                 // 聲明StrBlob類中的begin()和end()
14     StrBlobPtr end();                   // 返回一個指向它自身的StrBlobPtr
15 public:
16     typedef vector<string>::size_type size_type;    // 類型別名,size_type = vector<string>::size_type
17 
18     StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {};    // 接受一個initializer_list參數的構造函數將其參數傳                                                            遞給對應的vector構造函數,通過拷貝列表
19     StrBlob::StrBlob() : data(make_shared<vector<string>>()) {};                                 // 構造函數,初始化data成員,指向動態分配的vector                                                                     中的值初始化vector元素
20     void push_back(const string &t) { data->push_back(t); }
21     string& StrBlob::front() {
22         check(0, "front on empty StrBlob");
23         return data->front();
24     }
25     string& StrBlob::back() {
26         check(0, "back on empty StrBlob");
27         return data->back();
28     }
29     void StrBlob::pop_back() {                         // 刪除尾元素
30         check(0, "pop_back empty StrBlob");
31         return data->pop_back();
32     }
33     string& front() const { return data->front(); };
34     string& back() const { return data->back(); };
35 private:
36     shared_ptr<vector<string>> data;
37     void StrBlob::check(size_type i, const string &msg) const {   // 檢查元素是否存在
38         if (i >= data->size())                                    // 若不存在
39             throw out_of_range(msg);                              // 拋出異常
40     }
41 };
42 class StrBlobPtr {
43 public:
44     StrBlobPtr() : curr(0) {};
45     StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) {};
46     string & deref() const {
47         auto p = check(curr, "dereference past end");
48         return (*p)[curr];    // check成功,返回一個p指針,指向make_shared指向的vector
49     }                        // 解引用,make_shared獲取vector,用下表運算符返回curr位置上的對象
50     StrBlobPtr& incr() {
51         check(curr, "increment past end of StrBlobPtr");
52         ++curr;
53         return *this;
54     }
55     bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
56 
57 private:
58     weak_ptr<vector<string>> wptr;
59     size_t curr;
60     shared_ptr<vector<string>> check(size_t i, const string& msg) const
61     {
62         auto rent = wptr.lock();
63         if (!rent)
64             throw runtime_error("unbound StrBlobPtr");
65         if (i >= rent->size())
66             throw out_of_range(msg);
67         return rent;
68     }
69 };
70 StrBlobPtr StrBlob::begin()
71 {
72     return StrBlobPtr(*this);
73 }
74 StrBlobPtr StrBlob::end()
75 {
76     return StrBlobPtr(*this, data->size());
77 }

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved