C++基礎回顧4——智慧指標shared_ptr

愛上冰激凌發表於2018-06-04

———-引入智慧指標的目的,個人;理解有如下兩點:
1.自動釋放記憶體:智慧指標類能記錄多少個shared_ptr指向相同的物件,並在引用計數值為0時自動釋放物件所佔用的記憶體。
2.記憶體共享:讓多個物件共享底層資料。

C++動態記憶體(malloc & free, new & delete)

程式使用動態記憶體出於以下三種原因之一:

  • 程式不知道自己需要使用多少物件
  • 程式不知道所需物件的準確型別
  • 程式需要在多個物件間共享資料
vector<string> v1;

    {//新作用域
        string str[] = {"a", "an", "the"};
        int size = sizeof(str) / sizeof(str[0]);
        vector<string> v2(str, str + size);
        v1 = v2;
    }//v2被銷燬,v1中包含三個元素

當拷貝一份vector時,原vector和副本vector中元素是相互分離的。
一般而言,如果兩個物件共享底層的資料,當某個物件被銷燬時,我們不能當方面的銷燬底層資料。

Blob<string> b1;

    {//新作用域
        Blob<string> b2 = {"a", "an", "the"};
        b1 = b2;//b1和b2共享相同的元素
    }

物件b1與b2具有引用相同的底層元素。
實現Blob類如下:

class strBlob
{
public:
    typedef std::vector<string>::size_type size_type;
    strBlob();
    strBlob(initializer_list<string> i1);
    size_type size() const { return data->size(); };
    bool empty() const{ return data->empty(); };

    //新增和刪除元素
    void push_back(const string &t) {data->push_back(t); };
    void pop_back();

private:
    shared_ptr<vector<string>> data;
    void check(size_type i, const string &msg) const;

}

shared_ptr與new的結合使用
預設情況下,一個用來初始化智慧指標的普通指標必須指向動態記憶體,因為智慧指標預設使用delete釋放它所關聯的物件。

 shared_ptr<string> clone(int p)
 {
 //必須顯示地呼叫int *建立shared_ptr<int>
     return shared_ptr<int>(new int(p));
 }

不要混合使用普通指標和智慧指標

 void  process(shared_ptr<int> ptr)
 {
 //使用ptr

 }//ptr離開作用域,被銷燬

process採用傳值方式傳遞形參,因此實參會被拷貝給一個臨時變數ptr,拷貝實參會遞增引用計數。

 shared_ptr<int> p(new int(42));//引用計數為1;
 process(p); //引用計數為2,執行完process後引用計數變為1
 int i = *p;//p的引用計數為1;

智慧指標刪除器

struct destination;
struct connection;
connection connect(destination *d);
void disconnection(connection);

void f(destination *dd)
{
    connection conn = connect(dd);

    //使用完conn後並未及時關閉,需要單獨呼叫disconnection
}

以上程式碼中若connection存在解構函式,還可以在解構函式中呼叫disconnect。但是不存在解構函式,就可以使用shared_ptr,與採用shared_ptr自動會呼叫delete避免記憶體洩漏的原理是一樣的。
修改如下:

void end_connection(destionation *d){ disconnect(p); };
void f(destination *dd)
{
    connection conn = connect(dd);
    shared_ptr<connection> p(&conn, end_connection);
}

unique_ptr

某一時刻只能有一個unique_ptr指向一個給定物件,unique_ptr與物件是一一對應的關係。
    unique_ptr<string> p1(new string("Stegosaurus"));
    unique_ptr<string> p2(p1); //錯誤:unique_ptr不支援拷貝,unique_ptr沒有對應的拷貝建構函式
    unique_ptr<string> p3;
    p3 = p2; //錯誤:unique_ptr不支援拷貝,unique_ptr沒有對應的拷貝建構函式

unique_ptr操作

  • unique_ptr〈T〉 u1:空unique_ptr,可以指向型別為T的物件;u1會使用delete來釋放它的指標。
  • unique_ptr〈T, D〉 u2:u2會使用一個型別為D的可呼叫物件來釋放它的指標。
  • unique_ptr〈T, D〉 u(d)
  • u = nullptr
  • u.release() :u放棄對指標的控制權,返回指標,並將u置為空
  • u.reset():釋放u指向的物件
  • u.reset(q):如果提供了內建q,令u指向這個物件;否則將u置位空。
    指向release之後,unique_ptr會切斷與之前指向物件的聯絡,並返回當初指向的物件,這時就需要使用返回的物件初始化另一個智慧指標。
p2.release();//錯誤:p2不會釋放記憶體,而且我們丟失了指標
auto p = p2.release(); //正確,用完後必須delete(p)

weak_ptr操作
weak_ptr是一種不控制所指向物件生命週期的智慧指標,它指向一個由shared_ptr管理的物件,將一個weak_ptr繫結到shared_ptr上不會改變shared_ptr的引用計數。

  • weak_ptr〈T〉w:指向型別為T的空weak_ptr
  • weak_ptr〈T〉w(sp):與shared_ptr sp指向相同物件的weak_ptr,但是T必須能轉換為sp可以指向的型別
  • w.reset()
  • w.use_count();
  • w.expired();
  • w = p

相關文章