智能指針

智能指針誕生的緣由:

在動態內存管理過程,要注意釋放動態內存的正確時間,若是不釋放的話,會形成內存泄露;可是釋放時若還有指針引用內存的話,會產生引用非法內存的指針。而智能指針就是爲了解決這一難點而誕生的,就如它的名字同樣,若是你使用智能指針的話,它就會智能的在合適的時機釋放掉內存。智能指針包括shared_ptr,unique_ptr和weak_ptr.安全

shared_ptr和unique_ptr支持的操做

shared_ptr和unique_ptr支持的操做;函數

shared_ptr<T>sp; //T爲參數類型,默認爲空指針
unique_ptr<T>up;
*p;//解引用
p->get();//返回智能指針保存的指針
swap(p,q);//交換p和q種的指針
//構造一個shared_ptr
make_shared<T>(arg);//返回一個shared_ptr對象,並用arg來初始化它
shared_ptr<T>p(q);//用q來初始化p
p.use_count();//返回p種的計數。

智能指針的實現機制:

咱們能夠認爲每一個智能指針都有綁定一個計數器,一般稱其爲引用計數。不管咱們什麼時候拷貝一個智能指針,計數器都會遞增,例如指針

①用一個shared_ptr初始化另外一個shared_ptrcode

shared_ptr<T>p(q);//q中的計數器會加一

②將智能指針做爲參數傳遞對象

int func(std::shared_ptr<int>p){
    return p->use_count(); 
}
int mian(){
    std::shared_ptr<int>p = std::make_shared<int>(10);
    std::cout<<func(p);//輸出的結果爲2
}

③做爲參數的返回值:內存

shared_ptr<T> func();

shared_ptr和new結合使用:

shared_ptr的構造函數時explict修飾的,所以咱們不能將一個指針隱式轉換爲智能指針。作用域

shared_ptr<int>p = new int(10);//錯誤,不能將內置指針轉換爲智能指針
shared_ptr<int>p(new int(10));//ok

另外shared_ptr還支持reset操做來改變智能指針綁定的指針。get

p.reset();//p取消與指針的綁定,變成默認的nullptr;
p.reset(new int(20));//p與新的匿名地址綁定。

unique_ptr

unique_ptr與shared_ptr不一樣的是,它不可以拷貝,也就是說它的計數器是能小於等於1.當unique_ptr被銷燬的時候,它指向的對象也將被銷燬。內存管理

unique不支持拷貝和賦值:io

std::unique_ptr<int>p(new int(10));//ok,直接初始化。
std::unique_ptr<int>q(p);//錯誤,不能拷貝
q = p;//錯誤,不能賦值

unique支持reset和release操做:

reset的操做和shared_ptr的reset操做相同,release操做會返回當前智能指針保存的對象並將智能指針置爲空。經過這兩個操做能夠進行unique_ptr指向對象的專業。

p.reset(q.release());//將q保存的對象轉移給p。

智能指針自定義刪除器

shared_ptr和unique_ptr默認爲用delete刪除對象,也能夠自定義刪除器

shared_ptr的自動刪除器定義以下:

假設如今有class 爲connection,刪除函數爲disconnect(connection *con);那麼自動刪除定義刪除器爲:

std::shared_ptr<connection>p(new connection(),disconnect);
//第二個參數爲自定義的刪除器函數。

unique_ptr的自動刪除器要在參數列表指明刪除器的函數指針類型,這裏用decltype函數自動推導。

std::unique_ptr<connection,decltype(disconnect)*>p
(new connection(10),disconnect);

weak_ptr

weak_ptr是一種不控制對象生長週期的智能指針,它指向一個shared_ptr指向的對象,將weak_ptr綁定到一個shared_ptr保存的對象上,也不會增長shared_ptr的計數器。一旦最後一個指向對象的shared_ptr被銷燬,對象將被釋放,即便還有weak_ptr綁定這個對象。

std::shared_ptr<int>p(new int(10));
std::weak_ptr<int>wp(p);//wp若共享p,p的引用計數不會改變

因爲對象不存在,因此不能用wp直接訪問對象,而是用成員函數lock來檢查指向的對象是否存在,lock就返回一個指向的shared_ptr.與其它shared_ptr不一樣,只要此shared_ptr存在,它的底層對象就一直存在。

if(shared_ptr<int>np = wp.lock())//只有np不爲空時才能進入if
{
    //在if中使用共享對象np是安全的,np和p共享對象。
}

weak_ptr能夠解決shared_ptr的循環引用:

class B;
class A{
public:
    A(){
        std::cout<<"A construction"<<std::endl;
    }
    ~A(){
        std::cout<<"A destruction"<<std::endl;
    }
    std::shared_ptr<B>pb;
};
class B{
public:
    B(){
        std::cout<<"B construction"<<std::endl;
    }
    ~B(){
        std::cout<<"B destruction"<<std::endl;
    }
    std::shared_ptr<A>pa;
};
int main(){
    std::shared_ptr<A>a(new A());
    std::shared_ptr<B>b(new B());
    a->pb = b;
    b->pa = a;
}

上面的代碼的輸出是什麼呢?

A construction
B construction

沒錯,能夠很顯然的看到A和B在主函數結束後並無被銷燬,由於A和B互相持有對方的引用計數,二者的引用計數都不會較小到0,所以內存也不會被釋放。

而將代碼修改到以下後:

 
class B;
class A{
public:
    A(){
        std::cout<<"A construction"<<std::endl;
    }
    ~A(){
        std::cout<<"A destruction"<<std::endl;
    }
    std::weak_ptr<B>pb;
};
class B{
public:
    B(){
        std::cout<<"B construction"<<std::endl;
    }
    ~B(){
        std::cout<<"B destruction"<<std::endl;
    }
    std::weak_ptr<A>pa;
};
int main(){
    std::shared_ptr<A>a(new A());
    std::shared_ptr<B>b(new B());
    a->pb = b;
    b->pa = a;
}

輸入爲

A construction
B construction
B destruction
A destruction

前面咱們說過weak_ptr時弱共享,不會增長shared_ptr的計數引用,所以A和B只有自己一個計數引用,當自身離開做用域後,計數器歸0,釋放內存。

相關文章
相關標籤/搜索