スマートポインタshared_ptrとunique_ptr詳細

16075 ワード

ダイナミックメモリを使用する理由
プログラムは自分がどれだけのオブジェクトを必要とするか分からない.プログラムはオブジェクトの正確なタイプを知らない.プログラムは複数のオブジェクト間でデータを共有する必要がある.
ダイナミックメモリはどこですか?
プログラムには静的メモリ、スタックメモリがあります.静的メモリは、ローカルstaticオブジェクト、クラスstaticデータメンバー、および任意の関数以外に定義された変数を保存するために使用されます.スタックメモリは、関数内に定義された非staticオブジェクトを保存するために使用されます.静的メモリまたはスタックメモリに割り当てられたオブジェクトは、コンパイラによって自動的に作成または破棄されます.スタックオブジェクトの場合、定義されたブロックが実行されている場合にのみ存在します.staticオブジェクトは使用前に割り当てられ、プログラム終了時に破棄されます.静的メモリとスタックメモリに加えて、各プログラムにはメモリプールがあります.このメモリは、フリースペースまたはスタックと呼ばれます.プログラムは、動的に割り当てられたオブジェクト、すなわち、プログラムの実行時に割り当てられたオブジェクトをスタックで格納します.動的オブジェクトの生存期間はプログラムによって制御されます.すなわち、動的オブジェクトが使用されなくなった場合、コードは明示的に破棄されなければなりません.(c++ primer P400)
フリー・ストレージとヒープ
フリーストレージは、c++でnewとdeleteによってオブジェクトを動的に割り当てて解放する抽象的な概念であり、newによって申請されるメモリ領域は、オペレーティングシステムが維持するメモリと呼ぶことができるが、c++コンパイラはデフォルトではスタックを使用してフリーストレージを実現する.両者は等価ではない
ダイナミックメモリとスマートポインタ
c++の注意すべき点の1つは、メモリの管理です.ダイナミックメモリの使用でメモリが漏れることが多いか、不正なメモリを参照するポインタを生成する新しい標準ライブラリでは、ダイナミックオブジェクトを管理するための2つのスマートポインタタイプが提供されています.(1)shared_ptrは、複数のポインタが同じオブジェクト(2)unique_を指すことを可能にするptrが指すオブジェクトを独占的に定義するmemoryヘッダファイルでは、指すオブジェクトを自動的に解放する役割を果たします.
インテリジェントポインタの本質
インテリジェントなポインタの実質はオブジェクトですが、動作はポインタのように表現されています.
unique_ptrの「独占」?
なぜshared_ptrは、動的オブジェクトの所有権が不確定であるため、複数のポインタが同じオブジェクトを指すことを許可する.オブジェクトは複数の役割ドメインで共有でき、スタックオブジェクトのように自由に値コピーすることはできません.オブジェクト役割ドメインがまだこのダイナミックオブジェクトを持っている限り、彼は破棄することはできません.彼が役に立たないときは、自動的に破棄します.このメカニズムは後で説明します.unique_ptrの「排他」とは、他のスマートポインタが内部のポインタを共有することを許さず、unique_を付与することを許さないことを意味する.ptrは別のuniqueに値を割り当てます.ptr.例:
std::unique_ptr p (new int);
std::unique_ptr q = p; //error

でもunique_ptrは、関数を介して他のuniqueに返すことを許可する.ptrは、std::moveによって他のunique_に移行することもできます.ptr、このときそれ自体が元のポインタの所有権を持っていないことに注意してください.
std::unique_ptr p (new int);
std::unique_ptr q = std::move(p); //ok

shared_ptrベース
shared_ptrもテンプレートなので、作成するときにポインタが指すタイプを提供する必要があります.
shared_ptr<string> p1;  //  string
shared_ptr<list<int>> p2;   //  int list

デフォルトで初期化されたスマートポインタは空のポインタで、スマートポインタの使用は通常のポインタと似ています.
if (p1 &&p1->empty()) //  p1    p1     string 
{
    *p1 = "lvbai";  //  
}
  • shared_ptrとunique_ptrがサポートするいくつかの操作
  •     shared_ptr<T> p1; //     ,       T   
        unique_ptr<T> p2;
    
        if (p1) // p1        , p      , true 
        {...}
    
        *p1;    //   p1,        
    
        p->member;  //   *(p1).member
    
        p1.get();   //  p1      。          ,               
    
        swap(p1, q);     //  p q     p1.swap(q);
  • shared_ptr独自の操作
  •     make_shared (args) //    shared_ptr,            T   ,  args   
    
        shared_ptr p(q)  //p shared_ptr q   ;      q     ,q          T*
    
        p = q //p q  shared_ptr,              ,      p     ,  q     ; p       0,           
    
        p.unique()  // p.use_count() 1,  true
    
        p.use_count()   //   p            ,    ,      

    make_shared関数
    何がmake_shared関数、彼の役割は何ですか?make_shared関数は、ダイナミックメモリを安全に割り当て、使用する方法です.ダイナミックメモリにオブジェクトを割り当てて初期化し、指定したタイプのshared_を返します.ptrは、memoryのヘッダにも定義されています.
        //      42 int shared_ptr
        shared_ptr<int> p = make_shared<int> (42);
        //      "999" string
        shared_ptr<string> q = make_shared<string> (3, '9');
        //         int,   0
        shared_ptr<int> w = make_shared<int> ();
  • make_sharedはmakeを初期化する方法sharedはそのパラメータを用いて所与のタイプのオブジェクトを構築する.すなわち、我々のパラメータは、所与のタイプの構造関数に合致しなければならず、パラメータを伝達せずにデフォルトの初期化
  • を行う.
  • とshared_ptrの違いは、なぜmake_を使うのかsharedのほうがいいsharedよりptrはメモリ割り当ての回数を減らし、メモリ割り当ての代価が高い.彼はすぐに申請のヌードポインタを取得し、メモリの漏洩を起こさない
  • make_sharedの欠点構造関数が保護またはプライベートである場合make_を使用できないshared;オブジェクト調整メモリは、
  • をタイムリーに回収できない可能性があります.
    shared_ptの強引用、弱引用
    スマートポインタがオブジェクトを自動的に解放する方法については、簡単に言えば、スマートポインタの内部に参照カウントがあり、ポインタがオブジェクトを指すと参照カウントが++になります.逆に、0に減算すると、オブジェクトが自動的に解放されます.
                                 ,              。               shared_ptr

    強い参照と弱い参照がshared_ptrは参照カウントの情報を維持するために使用される
  • 強引用現在どれだけ生存しているかを記録するためのshared_ptrsはそのオブジェクトを保持する.共有するオブジェクトは、最後の強い参照が離れたときに破棄されます(解放される可能性もあります).
  • 弱参照は、現在何個のオブジェクトが観察されているかを記録するために使用されるweak_ptrs. 最後の弱い参照が離れると、共有内部情報制御ブロックは破棄され、解放する(共有オブジェクトも解放され、まだ解放されていない場合).
  • コピーまたは付与操作が行われるとき、shared_ptrは他のsharedを何個記録しますか?ptrは同じオブジェクトを指す
  • オブジェクトの最後のshared_を指す場合ptrは破棄されましたshared_ptrクラスは、対応する構造関数を呼び出すことによってこのオブジェクトを破棄する
  • shared_ptrは、関連するメモリ
  • を自動的に解放します.
    //       T          ,          Q          
    shared_ptr Fun(Q arg) 
    {
        // arg    
        //shared_ptr      
        return make_shared(arg);
        //      shared_ptr,                     
    }
    void use_Fun(Q arg) 
    {
        shared_ptr p = Fun(arg);
        //  p
    }
    //    ,p      ,           
    shared_ptr use_Fun(Q arg) 
    {
        shared_ptr p = Fun(arg);
        //  p
        return p; //  p ,      
    }
    //p      ,           

    shared_ptrとnewを組み合わせて使用
        shared_ptr<int> p1(new int(1));//     
        shared_ptr<int> p2 = new int(1);//  ,int     

    ポインタパラメータを受け入れるスマートポインタ構築関数はexplicitであり、直接初期化を使用する必要があり、暗黙的なタイプ変換はできません.
    shared_ptr<int> FUn(int p) 
    {
        return new int(p); //error
        return shared_ptr<int>(new int(p)); //right
    }

    自分のリリース操作を定義
    shared_ptr p(q) //p      q,q    new     ,      T*  
    
    shared_ptr p(u) //p unique_ptr u           ,  u  
    
    shared_ptr p(q, d) //p       q          ,q       T*  。p        d   delete
    
    shared_ptr p(p2, d) //p shared_ptr p2   ,  d   delete
    
    //               , ,            T*   
    
    p.reset() 
    p.reset(q)
    p.reset(q, d)
    // p         shared_ptr,reset      。             q,  p  q,    p  。       d,    d   delete   q
    
    
    //  :
    void del(int* p)
    {...}
    shared_ptr<int> p(new int(1), del);
    shared_ptr<int> p(new int, del);
    

    インテリジェントポインタのトラップと欠陥
  • スマートポインタと通常ポインタ
  • を混合しないでください.
    void process(shared_ptr ptr) 
    {
        ...
    }
    //ptr     ,   
    int* x(new int(1024));
    process(x); //error
    process(shared_ptr<int>(x)); //  ,       
    int j = *x; //   ,x       
    
    //                            ,               
  • は、getを使用して別のスマートポインタを初期化したり、スマートポインタにスマートポインタを付与するために定義されたget関数の戻り値が内蔵されたポインタであり、スマートポインタ管理のオブジェクトを指すため、前述したように、空のサスペンションポインタが発生し、未定義の動作が発生する可能性があります.

  • この関数は、スマートポインタを使用できないコードに内蔵ポインタを渡す必要があるためです.
    void f(int* q) 
    {
        shared_ptr<int> tmp(q);
    }//   q     
    int main()
    {
        shared_ptr<int> p(new int(1));
        int* q = p.get();
        f(q);
        int qq = *q; //      
    }
    //                 
    getdeleteget

    unique_ptr
    unique_ptrの直感的な認識は「独占」「持つ」だろう.ある時点でuniqueが1つしかないという意味です.ptrは、指定されたオブジェクトを指します.すなわち、コピーおよび付与はできません.
        int* p (new int(3));
        shared_ptr<int> p1(p);
        auto p2 = p1; //ok
        unique_ptr<int> p3(p);
        unique_ptr<int> p4(p); //ok,                    ,               
        unique_ptr<int> p5 = p3; //error
  • uniqueの初期化ptrはmakeに似ていませんsharedの操作は、
  • のみ直接初期化できます.
        unique_ptr<int> p(new int(1024)); //ok
        unique_ptr<int> p1 = new int; //error
        unique_ptr<int> p2(p); //error
  • unique_ptrベース
  • 
        unique_pre p1; // unique_ptr,       T   ,p1   delete       ,p2        D               
    
        unique_ptr p2; 
        unique_ptr p(d); // unique_ptr,     T   ,    D   d   delete
    
        p = nullptr; //  p     , p   
        p.release(); //p         ,    ,  p  
        p.reset(); //  p      
        p.reset(q); //         q, p      ;
        p.reset(nullptr); 
  • コピーと付与はできませんが、上記のresetとreleaseを呼び出してポインタの所有権を1つの(constではない)unqiue_から呼び出すことができます.ptrは別のuniqueに移行するptr
  • //     p1   p2
        (1)unique_ptr p2(p1.release());
        (2)p2.reset(p1.release());
  • 単純呼び出しreleaseはエラーの
  • である.
    p.release(); //  ,release            ,     
    auto q = p.release(); //  ,  delete q
  • 特別バージョンunique_ptrはnewから出てくる配列の特殊化に対して、特殊化されたバージョン
  • である.
    unique_ptr<int []> q(new int[10]);
    q.release();
    //   delete[]         
  • unique_ptrはパラメータとして値uniqueを渡し、返します.ptrのコピー不可には例外があります:
  • unique_ptr<int> Fun(int p) 
    {
        return unique_ptr<int>(new int(p));
    }
    //           
    unique_ptr<int> Fun(int p) 
    {
        unique_ptr<int> ret(new int(p));
        //...
        return ret;
    }

    コンパイラは、返されるオブジェクトが破棄されることを知り、特殊な「コピー」(移動操作)を実行します.