C++ 11 智能指針

C++ 11 智能指針

前言:html

  近來,學習STL,忽然發現有智能指針,作了一週的學習(工做以外的時間),斷斷續續的學習,特此作下記錄。ios

誕生的緣由:c++

   爲了防止內存泄露,和二次釋放的問題。無非就是嫌棄本身管理內存太費勁,能夠寫個更簡單管理堆內存的類。編程

利用C++的特性:安全

   類結束會調用析構函數,無非就是棧空間出棧,同時釋放掉動態建立的空間。網絡

智能指針的做用:多線程

  將指針封裝成類,利用了一種叫RAII(資源獲取即初始化的技術,聽着有點高大上),重載操做符(->和*),行爲表現的像指針併發

  1. 防止屢次釋放該指針,致使崩潰(前提是這個指針被你釋放是否賦值爲空指針,若是賦值爲空,就沒有所謂的崩潰問題,習慣決定代碼的健壯性)
  2. 智能指針做用把值語義轉爲引用(老是搞個二傳手,這就是所謂的安全,下降性能爲代價,在內存無比大的今天,隨意了)

C++ 11 中的智能指針:函數

  包含在頭文件<memory>中,分別有三個智能指針,分別爲shared_ptr,unique_ptr,weak_ptr。性能

  1. 介紹下shared_ptr(別人寫的很好,我只作網絡的搬運工,來豐富本身的知識體系)

    1)原理:shared_ptr的多個對象指向同一個指針(大可能是new出來的空間指針),該指針使用引用計數,每使用一次,內部計數器加1,每析構一次,內部的引用計數器減1,減爲0的時候,自動刪除指向的堆內存。

    2)實現:就是一個模板類,沒事的時候強烈建議看下里面的具體實現,挺有意思的。

    3)不要用一個原始指針初始化多個shared_ptr,不然會形成二次釋放同一內存

     4)注意避免循環使用,會在下面舉例,並講解。

  2.unique_ptr

    「惟一」擁有所指對象,同一時刻只能有一個unique_ptr指向給定對象(禁止使用拷貝語義,只能用移動語義將其移動)。對比原始指針,也是利用了RAII的特性。用戶能夠定義delete操做。

 

#include <iostream>
#include <memory>
using namespace std;

int main()
{
          unique_ptr<int> uptr(new int(10)); //初始化
          unique_ptr<int> uptr1 = move(uptr); //轉移全部權
          uptr2.release();  //釋放全部權
          return 0;  
}

 

   3.weak_ptr,配合shared_ptr而引入的的智能指針,是弱引用,相對於shared_ptr強引用來講的。看似就像一個觀察者,觀測資源的使用狀況。weak_ptr能夠從一個shared_ptr獲取另外一個weak_ptr來構造,獲取資源的觀察權 。 並不會引發計數加的狀況,成員有use_count(),查看資源的引用計數,expired(),判斷是否指向的資源被釋放, 當返回爲true的時候,這個資源的引用計數爲0,至關於被釋放,反之就沒有被釋放掉。lock(),返回當前分享指 針,計數器並加1.

#include <iostream>
#include <memory>

using namespace std;
int main()
{
            shared_ptr<int> s_ptr = make_shared<int>(10);
            cout<<s_ptr.use_count() <<endl;
            weak_ptr<int> wp(s_ptr);
            cout<<wp.use_count() <<endl;
            if(!wp.expired())
            {
                   shared_ptr s_ptr2 = wp.lock();  //引用計數器加1
                   *s_ptr = 100;
                   cout<<wp.use_count()<<endl;
             }    
}    

運行結果:
1
1
2

應用場景及其問題:

  1.循環引用,場景:考慮一個簡單的場景--家長和孩子,一個父母有一個孩子,一個孩子有一雙父母。

  使用原始指針的實現:

  

#include <iostream>
using namespace std;

class Child;
class Parent;

class Parent 
{
private:
    Child* myChild;
public:
    void setChild(Child* ch)
    {
        this->myChild = ch;
    }
    void doSomething() 
    {
        if(this->myChild)
        {
            cout<<"Child alive"<<endl;
        }
    }
    ~Parent() {
        cout<<"delete myChild"<<endl;
        delete myChild;    
}
};

class Child
{
private:
    Parent* myParent;
public:
    void setParent(Parent* p)
    {
        this->myParent = p;
    }
    void doSomething() {
        if(this->myParent)
        {
            
            cout<<"myParent alive"<<endl;                                    }
    }
    ~Child() {
    cout<<"delete myParent"<<endl;
    delete myParent;    
}
};


int main()
{
    Parent* p = new Parent;
    Child* c = new Child;
    p->setChild(c);
    c->setParent(p);
    delete c;
    return 0;
}

  如何使用智能指針解決該問題呢:引入智能指針,兩個類只要保證一個類是shared_ptr(強引用)一個是weak_ptr(弱引用)

#include <memory>
#include <iostream>
class Child; class Parent; class Parent{ private: std::weak_ptr<Child> ChildPtr; public: void setChild(std::shared_ptr<Child> child) { this->ChildPtr = child; } void doSomething() { } ~Parent() {} }; class Child { private: std::shared_ptr<Parent> ParentPtr; public: void setParent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent; } void doSomething() {} ~Child() {} }; int main() { std::weak_ptr<Parent> wpp; std::weak_ptr<Child> wpc; { std::shared_ptr<Parent> p(new Parent); std::shared_ptr<Child> c(new Child); p->setChild(c); c->setParent(p); wpp = p; wpc = c; std::cout<<p.use_count() <<std::endl; std::cout<<c.use_count() <<std::endl; } std::cout <<wpp.use_count() << std::endl; std::cout << wpc.use_count() << std::endl; return 0; }
運行結果:
2
1
0
0
注意若是使用g++編譯,請添加參數-std=c++11(弱引用是C++11引入)

2..返回shared_ptr自己,並不引發計數器加+1

#include<iostream>
#include<memory>

using namespace std;

class Test: public enable_shared_from_this<Test>
{
public:
    Test(){}
    ~Test()
    {cout <<"~Test()"<<endl;}
    shared_ptr<Test> sget()
    {
        return shared_from_this();
    }
};

int main()
{
    weak_ptr<Test> wp;
    {
    shared_ptr<Test> sp(new Test);
    wp = sp;
    cout<<"sp is "<<sp.use_count()<<endl;
    sp->sget();
    cout<<"sp is "<<sp.use_count()<<endl;
    }
    cout<<"wp is"<<wp.use_count()<<endl;
    return 0;

    return 0;
}
運行結果:
sp is 1
sp is 1
~Test()
wp is 0

3.註冊銷燬函數

#include<iostream>
#include<memory>

using namespace std;

struct MyStruct
{
    int *p;
    MyStruct():p(new int(10)){}
};

int  main()
{
    MyStruct st;
    {
        shared_ptr<MyStruct> sp(&st, [](MyStruct *ptr){
            delete(ptr->p);
            ptr->p = nullptr;
            cout<<"destructed"<<endl;
        });
    }
    if(st.p != nullptr)
        cout<<"no destroyed"<<endl;
    else
        cout<<"be destroyed"<<endl;
    return 0;
}
運行結果:
destructed
be destroyed

4.線程安全討論

  官方文檔:1)同一個shared_ptr對象能夠被多線程同時讀取

       2) 不一樣的shared_ptr對象能夠被多線程同時修改(提起12分注意力,這裏有坑)

       3)任何其餘併發訪問的結果都是無定義的(什麼軟,這個目前沒法理解

  對1)來講,都能理解,讀必定是安全,當在2)狀況下,因爲內部shared_ptr有兩個成員,一個計數,一個指向

  實際內存的指針,具體內部實現也沒有上鎖,同時操做兩個數據成員,讀寫操做沒法作到原子化, 在多線程編程           中,在多個線程同時訪問同一個shared_ptr的時           候,請加mutex保護。

  下面來詳細的分析下爲何:

                    1)首先看下shared_ptr內存結構,加入該指針指向一個Foo的類

                      

          

         2)考慮一個簡單的場景,有三個shared_ptr<Foo> 對象 x, g,n;

       

       shared_ptr<Foo> g(new Foo);   //線程之間共享的shared_ptr 

                        shared_ptr<Foo> x; //線程A的局部變量

       shared_ptr<Foo> n(new Foo);//線程B的局部變量

 

       開始:還挺整齊的,符合咱們的預想

       

            線程A執行x = g;即(read g),如下圖示:可是還沒來的急將引用計數+1,切換到線程B

             

                        同時線程B執行g=n;(即寫g),以下圖

                     

 

                       這個時候就已經將Foo1申請的動態內存歸還給操做系統了,出現空懸指針,以下圖:

                    

                      最後將回到線程A,以下圖:

                      

                    如今這個狀態,整我的都很差了。

                    多線程無保護的讀寫,形成了"x空懸指針"的後果,綜上,論證爲啥對shared_ptr讀寫要加鎖的緣由。

以上就是對智能指針的理解

具體參考:http://www.cnblogs.com/gqtcgq/p/7492772.html

                  vs2010中關於C++11 智能指針的源碼

                                                                                                      23:37:57  2019-04-26

相關文章
相關標籤/搜索