c++智能指針

1.應用與原理ios

  智能指針和普通指針的區別在於智能指針其實是對普通指針加了一層封裝機制,這樣的一層封裝機制的目的是爲了使得智能指針能夠方便的管理一個對象的生命期。c++經過new和delete來處理動態內存的申請和釋放,可是new以後處理很差delete便會致使內存泄漏。可是智能指針的刪除是智能指針自己決定的,用戶不須要管理釋放。c++

  智能指針的封裝是一個模板,因此使用的方式是shared_ptr<int>,shared_ptr<string>這種格式。ide

2.智能指針的類型函數

  [1]shared_ptr,shared_ptr類的本質就是維護了一個引用計數,因此能夠有不少shared_ptr同時指向這個內存,每次使用時計數加1,不使用了計數減1,減到0時自動銷燬對象。this

#include <iostream>  
#include <stdio.h>  
#include <string.h>  
#include <vector>  
using namespace std;  

void fun()
{
    auto tmp = make_shared<int>(6);
    return ;           //離開做用域以後,tmp指向的內存計數減爲0,由於自動釋放內存
}

int main(int argc, char *argv[])  
{  
    // shared_ptr的定義
    shared_ptr<int> i1;                              // 空指針
    shared_ptr<int> i2 = make_shared<int>(2);        //指向一個值爲2的int指針,即*i2 是2
    auto i3 = make_shared<int>(3);                   //auto自動設置類型
    
    shared_ptr<string> s1 = "s1";

    //shared_ptr的拷貝和賦值
    auto i4 = i3;                                   //遞增i3指向的int內存引用計數
    i3 = i1;                                        //遞減i3指向的int內存引用計數,遞增i1指向的int內存引用計數

    fun();
    
    return 0;
} 

  [2]unique_ptr,這個指針獨享一個指向的對象,就是說只能有一個unique_ptr指向一個對象,不能被多個同時指向而已。這個由於不能賦值和拷貝,因此須要用兩個函數來處理這些操做:spa

      release():調用後會釋放全部權,並返回所指向對象的指針。從這裏能夠看到release只是釋放對象的全部權,並無釋放對象(而是返回對象的指針),因此必需要接管這個對象指針,不然會形成內存泄漏。指針

               s1.release();  //錯誤,s1指向的內存沒有釋放,這個內存指針也沒法再獲取到。正確的應該是s2 = s1.release();code

      reset():調用會釋放內存(若是unique_ptr不爲空),並從新指向一個新的指針。對象

               s1.reset(s2.release());  //首先s2釋放全部權,返回一個指針,而後s1釋放當前指向的內存(若是s1不爲空),並接管這個s2返回的指針。blog

               s1.reset();        //s1釋放當前指向的內存(若是s1不爲空),並把s1置空。

      這個意思就是release()只是釋放全部權,而且須要其餘的變量接管這個對象,不接管會內存泄漏。reset就是釋放內存,若是有參數,則指向把這個參數指向的對象。

#include <iostream>  
#include <stdio.h>  
#include <string.h>  
#include <vector>  
using namespace std;  

int main(int argc, char *argv[])  
{  
    // unique_ptr的定義
    unique_ptr<int> i1;                              // 空指針
    unique_ptr<int> i2(new int(2));                  //指向一個值爲2的int指針,即*i2 是2

    i1 = i2;                                         //錯誤,不能被多個指向

    // release和reset的使用
    unique_ptr<int> s1(new string("s1"));  
    unique_ptr<int> s2(s1.release());                  //s1.release() : s1全部權釋放後被置空,而後把全部權賦值給s2
    unique_ptr<int> s3(new string("s3"));  
    s2.reset(s3.release());                            //s3.release() : s3全部權釋放後被置空,而後s2當前的全部權被釋放,而後把全部權賦值給s2
    
    return 0;
}  

      unique_ptr的例外狀況:能夠傳遞unique_ptr參數和返回unique_ptr。這個意思就是說unique_ptr能夠當成函數參數傳遞,由於傳參就有拷貝的操做,也能夠當成函數的返回值,由於返回一個值,也會有拷貝的操做。可是這兩種狀況是容許的,例以下面的例子:

unique_ptr<int> fun(int p)
{
    return unique_ptr<int>(new int(p));
}

  [3]weak_ptr,是一種不控制所指對象生存期的智能指針,指向一個有shared_ptr管理的對象。將一個weak_ptr綁定到一個shared_ptr不會增長所指對象的引用計數。因此訪問weak_ptr時,對象能夠已經被刪除,所以訪問weak_ptr時,須要調用lock()函數,這個lock()函數返回一個指向共享對象的shared_ptr,所以會能夠正確使用這個shared_ptr,由於返回shared_ptr會加指向對象的計數。

      總結一下就是weak_ptr不保證指向的對象存在,可是能夠經過lock()函數隨時獲取,獲取到了就可使用,獲取不到(lock()返回空)就說明對象被釋放了。參考下面的例子:

shared_ptr<int> i1(new int(10));
weak_ptr<int> i2 = i1;

{
    shared_ptr<int> i3 = i2.lock();    //lock確實是加鎖的意思,只不過這裏是經過增長i2指向的內存的引用計數(返回一個shared_ptr)來實現加鎖
    if(i3)
        printf("weak ptr i2 is valid.\n");
    else
        printf("weak ptr i2 is invalid.\n");
    
    //i3只在此做用域,出了此做用域,i3指向的對象便會減計數,所以會自動解鎖
}

3.手動實現一個shared_ptr

  這個主要是用到了引用計數的方式。

#include <iostream>  
#include <stdio.h>  
#include <string.h>  
#include <vector>  
using namespace std;  

//每一個smart_ptr都有一個ptr和use_count,其中ptr指向共享的對象。use_count指向共享對象的引用計數。
//這個計數固然是須要共享一份。若是各自有各自的計數(例如使用int use_count;),那確定出問題。
template<class T> class smart_ptr
{
public:
    smart_ptr(T *p);
    ~smart_ptr();
    smart_ptr(const smart_ptr<T> &orig);
    smart_ptr<T>& operator=(const smart_ptr<T> &rhs);
    
private:
    T *ptr;
    int *use_count;
};

// 常規的構造函數
template<class T> smart_ptr<T>::smart_ptr(T *p) : ptr(p)
{
    use_count = new int(1);
    printf("smart_ptr(T *p) is called!");
}

//帶參數的構造函數,適應這種初始化smart_ptr<int> p2(p1);
template<class T> smart_ptr<T>::smart_ptr(const smart_ptr<T> &orig)
{
    ptr = orig.ptr;
    use_count = orig.use_count;
    ++(*use_count);
    printf("smart_ptr(const smart_ptr<T> &orig) is called!");
}

template<class T> smart_ptr<T>::~smart_ptr()
{
    if (--(*use_count) == 0){
        delete ptr;
        delete use_count;
        ptr = NULL;
        use_count = NULL;
        printf("~smart_ptr() is called!");
    }
}

//重載"="操做符:增長右側計數,減小左側計數。
template<class T> smart_ptr<T>& smart_ptr<T>::operator=(const smart_ptr<T> &rhs)
{
    ++(*rhs.use_count);

    if (--(*use_count) == 0){
        delete ptr;
        delete use_count;
        printf("operator=(const smart_ptr<T> &rhs) is called, delete left side.\n");
    }

    ptr = rhs.ptr;
    use_count = rhs.use_count;
    
    printf("operator=(const smart_ptr<T> &rhs) is called.\n");
    return *this;
}

int main()
{
    smart_ptr<int> p1(new int(0));
    p1 = p1;
    
    smart_ptr<int> p2(p1);
    
    smart_ptr<int> p3(new int(1));
    p3 = p1;
    
    return 0;
}
相關文章
相關標籤/搜索