C++:スマートポインタのshared_ptr

8994 ワード

1.スマートポインタ
C++ではnewでメモリを動的に割り当て、deleteは手動でメモリを解放してメモリを動的に管理する目的を達成します.正しい時間にメモリを解放することは非常に難しいため、メモリを解放するのを忘れるとメモリが漏洩します.
動的メモリをより安全かつ容易に使用するために、C++11標準ライブラリには、動的メモリを管理するための新しいスマートポインタクラスが用意されています.スマートポインタは、通常のポインタと動作が同じですが、メモリが適切なときに自動的に解放されることを保証します.新しい標準ライブラリは、下位ポインタを管理する方法とは異なる2つのスマートポインタ(ヘッダファイル)を提供します.
  • shared_ptrは、複数のポインタが同じオブジェクトを指すことを可能にする.
  • unique_ptr「排他」が指すオブジェクト.

  • ここではsharedのみについて説明します.ptrの関連用法.
    2.shared_の作成ptrインテリジェントポインタ
    vectorと同様に、スマートポインタもテンプレートなので、スマートポインタを作成するときに、ポインタが指すタイプを示す追加の情報を提供する必要があります.shared_ptr p1;// p1, int shared_ptr> p2;// p2, list これは、スマートポインタを作成しただけで、メモリを割り当てていません.通常shared_には2つの方法がありますptr割り当てメモリ
    make_shared関数割り当てメモリ
    最も安全な割り当てとダイナミック内部の使用方法はmake_という名前を呼び出すことです.sharedの標準ライブラリ関数.この関数は、ダイナミックメモリにオブジェクトを割り当てて初期化し、そのオブジェクトを指すshared_を返します.ptr.
    shared_ptr<int> p1 = make_shared<int>(42);
    //    int shard_ptr   42   
    shared_ptr<string> p2 = make_shared<string>(10, '9');
    //      string shared_ptr    string (n,char)       
    shared_ptr p3 = make_shared();
    //    my_class shared_ptr    my_class          

    コンテナ操作に似たemplace,make_shared関数は、そのパラメータを使用して、所与のタイプのオブジェクトを構築します.だからクラスを構築するshared_ptrは、初期化時に構造関数と一致しなければならないことに注意してください.
    前述したようにshared_ptrは、複数のポインタが同じオブジェクトを指すことを可能にする.だからsharedはptrには、参照カウンタと呼ばれる関連するカウンタがあります.参照カウンタは、下記の条件を満たすとインクリメントされる.
  • 初期化shared_ptr:shared_ptr p1 = make_shared(42);このときp 1が指すオブジェクトはp 1の参照者のみであり、参照カウントは1
  • である.
  • コピーshared_ptr:shared_ptr q(p);このときqとpは同じオブジェクトを指し、このオブジェクトの参照カウントは2
  • である.
  • shared_ptrは関数としてのパラメータ伝達:
  • shared_ptr<int> p1 = make_shared<int>(42);
    printCount(p1);
    void printCount(shared_ptr<int> p)
    {
        cout << p.use_count() << endl;//  p         
    }

    出力結果は2であり,p 1がパラメータとして伝達されるためである.
  • shared_ptrが関数として使用する戻り値
  • shared_ptr<vector<int>> getVector()
    {
        return make_shared<vector<int>>();
    }
    shared_ptr<vector<int>> p = getVector();
    cout << p.use_count() << endl;

    出力結果は1で、初期化時に明示的にmake_を使っていませんがshared関数はpにメモリを割り当てますが、getVector()関数を呼び出してshard_を返します.ptrは、pが指すオブジェクト参照カウンタを増加させる.
    オブジェクトの参照カウントが0に減少するとshared_ptrはオブジェクトを自動的に破棄し、メモリを解放します.
    次の2つの状況が発生した場合、shared_ptrの参照カウントは減少します
  • shared_へptrは新しい値を付与する:
  • auto p1 = make_shared<int>(42);
    p1 = p2;

    p 1にp 2が指すオブジェクトを割り当てると、p 2が指すオブジェクトの参照カウントが増加し、p 1がオブジェクトを指す参照カウントが減少し、このときp 1がオブジェクトを指す参照カウントが0となり、自動的に破棄される.
  • ローカルshared_ptrはその役割ドメインを離れる:
  • void printCount(shared_ptr<int> p)
    {
        shared_ptr<int> x = make_shared<int>();
        cout << x.use_count() << endl;
    }

    xはローカルshared_ptrは、関数が終了すると、xが指すオブジェクト参照カウントが0に減少し、xが指すオブジェクトが破棄され、使用メモリが解放される.
    shared_ptrとnewを組み合わせたスマートポインタの作成
    前述したように、スマートポインタを初期化しないと、空のポインタに初期化されます.makeを使う以外はshared関数はnew戻りポインタでスマートポインタを初期化することもできます.例えば、shared_ptr p(new int(42));文は、通常のポインタであるスマートポインタの構造関数を実際に呼び出す.この構造関数は明示的であるため,shared_ptr p = new int(42);という方式は誤りである.同じ理由でsharedを返しますptrの関数は、その戻り文で通常のポインタを暗黙的に変換することはできません.
    shared_ptr<int> clone(int p)
    {
        return new int(p);
        //  ,             shared_ptr
    }

    正しい書き方は次のとおりです.
    shared_ptr<int> clone(int p)
    {
        return shared_ptr<int> (new int(p));
    } 

    また、デフォルトではshared_を初期化するために使用される点を強調する必要があります.ptrの通常のポインタは、スマートポインタがデフォルトでdeleteで関連するメモリを解放するため、動的に割り当てられたメモリを指す必要があります.いくつかの目的で、動的にメモリを割り当てないポインタでスマートポインタを初期化する必要がある場合は、メモリを解放する方法(deleteの再ロードと理解できる)をカスタマイズする必要があります.詳細例は3.shared_ptrがサポートする操作で説明します.
    3.shared_ptrがサポートする操作
    操作
    説明する
    shared_ptr< T > sp
    空のスマートポインタを作成し、Tタイプのオブジェクトを指すことができます.
    sp.get()
    spに保存されているポインタを返します.PS:注意して使用してください.スマートポインタがオブジェクトを解放すると、戻ってきたポインタが指すオブジェクトも消えてしまいます
    sp.reset()
    spがオブジェクトを一意に指すshared_である場合ptr,resetはオブジェクトを解放する
    sp.reset(p)
    spは元のオブジェクトを指していません.内蔵ポインタpが指すオブジェクトを指しています.ここでpはnewによってメモリが動的に割り当てられています.
    sp.reset(p,d)
    spは、元のオブジェクトを指しておらず、内蔵ポインタpが指すオブジェクトを指し、ここでpは呼び出し可能なオブジェクトdによって解放される
    sp.use_count()
    spがオブジェクトを指す参照数を返します
    sp.unique()
    sp.use_の場合count()==1、trueを返します.そうでなければfalseを返します.
    swap(sp1,sp2)
    2つのスマートポインタをスワップ
    shared_ptr< T > sp(p)
    spは内蔵ポインタpを管理し、pはnewによってメモリを動的に割り当てられ、T*タイプに変換できる必要がある.
    shared_ptr< T > sp(p,d)
    spは内蔵ポインタpがオブジェクトの所有権を指すことを引き継ぎ、pはT*タイプに準換わる必要があり、spはdeleteの代わりに呼び出し可能なオブジェクトdを使用する
    shared_ptr< T > sp1(sp2,d)
    sp 1はshared_ptr sp 2のコピーは、deleteの代わりに呼び出し可能なオブジェクトdを使用する
    上記の操作テーブルでは、呼び出し可能なオブジェクトdを用いて内蔵ポインタを解放する新しい方法が導入されている.
    これは、内蔵ポインタpが指すオブジェクトがnewによって動的に割り当てられていないという仮定に基づいているが、sp(shared_ptr)をpで初期化した場合、delete関数を「リロード」し、pのメモリをスムーズに解放する必要がある.これはとても面倒で、具体的な例はここを見ることができます:C++Primer第5版の練習12.4
    C++Primerは、通常のポインタとスマートポインタを混合しないことを推奨し、スマートポインタの使用をさらに推奨します.そのため、いくつかの例を紹介します.
    関数が存在する場合:
    void process(shared_ptr<int> ptr)
    {
        //do something 
    }//ptr     , ptr   ,             

    この関数の正しい使い方shared_を渡すptr:
    shared_ptr<int> ptr(new int(42));
    //ptr         1
    process(ptr);
    //process ptr          2
    int i = *ptr;
    //  ,     2

    典型的な誤った使い方は以下の通りです.
    int * x(new int(1024));
    process(x);
    //  ,            shared_ptr
    process(shared_ptr<int> (x));
    //  ,process ptr.use_count() == 1,process   ptr         !
    int i = *x;
    //     ,x     !

    まず、文shared_ptr(x)は強制変換に相当し、一時変数shared_を得る.ptrは、processを呼び出すとvoid process(shared_ptr ptr(x)){}に相当し、processが終了するとptrが指すオブジェクトの参照カウントが0になり、メモリが解放され、すなわちxポインタが指すメモリが解放され、xが空のサスペンションポインタになる.
    覚えておいてください:shared_ptrが内蔵ポインタにバインドされている場合.メモリの管理責任をsharedに教えましたptr、内蔵ポインタでメモリにアクセスしないでください.
    ほぼ同じ理由でgetが返す内蔵ポインタを使用して他のスマートポインタを初期化しないでください.