インテリジェントポインタの原理分析と自分のshared_ptr実装

15253 ワード

げんりぶんせき
メモリ管理は従来、C++プログラミングの注意を払う必要がある仕事である.C++自体はGCメカニズムを持たないため、すべてのメモリ管理は手動で実現する必要がある.malloc / freeからnew / delete、さらにallocatorの出現は、メモリ漏洩をより合理的かつ簡単に避けるためにほかならない.
ポインタ自体は非常に柔軟で強力なツールですが、メモリの直接的なコントロールは、ポインタを削除することを忘れたり、ポインタを2回削除したりするエラーが非常に隠れているため、メモリ漏洩の暗い鍋を背負わなければなりません.この問題を解決するため、boostのスマートポインタはついにC++11に標準ライブラリに組み込まれた.
例を見てみましょう
std::string* stringPtr1 = new std::string; std::string* stringPtr2 = new std::string[100]; …//do something delete stringptr1;//オブジェクトdelete[]stringPtr 2;//を削除オブジェクトからなる配列を削除
见たところすべてすべてとても良いです:new出てくる2つの対象はすべて任务を完成した后にすべてdelete落ちました.しかしdo somethingで何が起こったのか、例えば異常を投げ出したり、プログラムがジャンプしたり、関数が戻ったりした場合、何が起こるのでしょうか.明らかに、この2つdeleteは実行されません.つまり、メモリが漏れています.上記のポインタをスマートポインタとして定義すると、以下のようにすべての問題が解消されます.
shared_ptr stringPtr1(new std::string); shared_ptr stringPtr2 = new std::string[100]; …//do something
では、スマートポインタはどのようにしてメモリの管理を支援しますか?実際、スマートポインタは、スタック上で宣言したクラステンプレートであり、あるスタックに割り当てられたオブジェクトを指す元のポインタを使用して初期化できるリソース管理の役割を果たすクラスです(RAII).
スマートポインタを初期化すると、元のポインタがあります.これは、スマートポインタが元のポインタで指定されたメモリを削除することを意味します.スマートポインタの構造関数には削除する呼び出しが含まれています.スマートポインタが範囲を超えた場合、その構造関数が呼び出され、構造関数は自動的にリソースを解放します.
現代C++の3種類のスマートポインタを簡単に紹介します(MSDNスマートポインタ(現代C+)):
  • unique_ptrは、ベースポインタの1人の所有者のみを許可します.sharedが必要だと確信しない限りptr、そうでない場合はPOCOのデフォルトオプションとしてポインタを使用します.新しい所有者に移動できますが、コピーや共有はできません.破棄されたauto_を置換ptr. とboost::scoped_ptr比較.unique_ptrは小型で効率的で、サイズは1つのポインタに等しく、rvalue参照をサポートし、STLセットの迅速な挿入と検索を実現します.
  • shared_ptrはカウントを参照するスマートポインタを採用する.元のポインタを複数の所有者に割り当てる場合は(たとえば、コンテナからポインタのコピーを返して元のポインタを保持したい場合)このポインタを使用します.すべてのshared_ptr所有者が範囲を超えたり、所有権を放棄したりするまで、元のポインタは削除されません.サイズは2つです.1つはオブジェクト、もう1つは参照カウントを含む共有制御ブロックです.
  • weak_ptr結合shared_ptrで使用される特例スマートポインタ.weak_ptrは1つ以上のshared_を提供するptrインスタンスが持つオブジェクトへのアクセスは、参照カウントには関与しません.オブジェクトを観察したいが、アクティブな状態を維持する必要がない場合は、このインスタンスを使用します.場合によってはshared_を切断するために使用されます.ptrインスタンス間のループ参照.

  • shared_ptr解析shared_ptrの原理は引用カウント法reference counting複数のスマートポインタが同一のオブジェクトを指す場合、参照+1であるが、解析は逆にカウントがゼロであれば保存したポインタは削除される.
    通常のポインタの機能はスマートポインタがあります(以下mySmartPtr代替shared_ptr、これは自分で実現したスマートポインタの名前です)
    int *pi = new int(42);     
    mySmartPtr *hpa(new mySmartPtr(pi));    //       
    mySmartPtr *hpb = new mySmartPtr(*hpa); //         
    mySmartPtr  hpd = *hpa;                      //         
    

    また、複製shared_ptr何度でも、ループ参照が出ない限り、常に解析可能である.
    vector > obj;        
    vector > obj2;  
    obj2.push_back(obj[0]);
    

    オブジェクトを解析する作業に加えて、shared_ptrタイプ変換の問題を解決するのに役立つ必要があります.1つは、ベースクラスポインタで派生クラスオブジェクトを指す多態性の表現です.
    vector > obj;        //     vector
    obj.push_back(new Derived1);          //     1
    obj.push_back(new Derived2);          //     2
    

    もう1つはdynamic_castモデルチェンジで、以下のように使用dynamic_cast(static_cast(new Derived1))空のポインタが得られます.
    mySmartPtr d1 = new Derived1;
    mySmartPtr b = d1;              // b    Derived1
    mySmartPtr d2 = b.Cast();  //          
    

    具体的には、スマートポインタの実装には、次の機能が必要です.
  • パラメータ構造がない場合、初期化が空、すなわちオブジェクトと参照カウントの両方のポインタが0
  • ポインタをパラメータ構造として使用する場合、このポインタを持ち、スマートポインタがそれを指していない場合に解析を行う
  • スマートポインタコピーの場合、2つのスマートポインタが共通に内部ポインタを持ち、参照カウントが同時に+1
  • スマートポインタは、スマートポインタまたは通常ポインタを使用して値を再割り当てできます.リロード=オペレータで、スマートポインタに値を付与するには、自己分析後に値を再付与しないように、自己付与するかどうかを考慮する必要がありますが、通常のポインタがスマートポインタに値を付与する場合は、自己付与を考慮する必要はありません.両者自体が2つのタイプであるため
  • 下位ポインタのアクセスを取得し、定義getPtrPointer()およびgetPtrCounter()それぞれ下位ポインタと参照カウントを返し、定義operator bool()スマートポインタを暗黙的にboolに変換する場合
  • リロード->および×オペレータで、通常のポインタと同じポインタアクセスを実現
  • 暗黙的なポインタタイプ変換をサポートする必要があるstatic_castサポートされていないdynamic_castサポートされている変換はCast()メンバー関数で解決する.派生クラスを指すスマートポインタがベースクラスの内部オブジェクトにアクセスできないように、メタクラスを定義することを考慮します.転換が成功しない場合は空に戻る
  • 裸のポインタが2つのスマートポインタを直接作成するために使用される場合、2つのスマートポインタが析出したときに、そのポインタがdeleteによって2回もクラッシュすることが望ましい(これはshared_ptrの特徴)
  • ループ参照を処理しない(もshared_ptrの特徴)weak_ptrと連携することでループを破ることができる
  • しばらくは実現していないdeleterメカニズム、すなわち伝達のみmySmartPtr1パラメータ
  • 実際、第8/第9の2点は、スマートポインタを使用して最も問題が発生する場所であり、普段の使用中は注意しなければならない.
    簡単な実装
    テストケース
    /**********************************************
        > File Name: testmySmartPtr.cpp
        > Author: ye_create
        > Mail: 
        > Created Time: 2015 05 28      13 03 42 
     ***********************************************/
    #include 
    #include 
    #include "mySmartPtr.h"
    using namespace std;
    
    class Base             //       
    {
    public:
        virtual ~Base(){
            cout << "class Base" << endl;
        }
    };
    
    class Derived1 : public Base   //    1
    {
    public:
        ~Derived1(){
            cout << "class Derived1" << endl;
        }
    };    
    
    class Derived2 : public Base    //    2
    {
    public:
        ~Derived2(){
            cout << "class Derived2" << endl;
        }
    };
    
    int main()
    {   
        int *pi = new int(42);     
        mySmartPtr<int> *hpa(new mySmartPtr<int>(pi));        //       
        mySmartPtr<int> *hpb = new mySmartPtr<int>(*hpa);     //         
        mySmartPtr<int>  hpd = *hpa;                          //         
        //          
        cout << hpa->getPtrCounter() << " " << hpb->getPtrCounter() << " "<< hpd.getPtrCounter() << endl;  
        delete hpa;  
        cout << hpd.getPtrCounter() << endl;  
        delete hpb;  
        cout << hpd.getPtrCounter() << endl;
    
        //              
        vector > obj;        //     vector
        obj.push_back(new Derived1);          //     
        obj.push_back(new Derived2);
    
        vector > obj2;  
        obj2.push_back(obj[0]);
    
        if (obj2[0])
            cout << "Cast Derived1 to Base successd" << endl;
        else
            cout << "Cast Derived1 to Base failed" << endl;
    
        //            
        mySmartPtr d1 = new Derived1;
        mySmartPtr b = d1;              
        mySmartPtr d2 = b.Cast();  
        // d2  ,  b    Derived1   Derived2
        if (d2)
            cout << "Cast Derived1 to Derived2 successd" << endl;
        else
            cout << "Cast Derived1 to Derived2 failed" << endl;
    
        return 0;
    }

    mysartPtr実装
    /*************************************************************************
        > File Name: mySmartPtr.cpp
        > Author: ye_create
        > Mail: 
        > Created Time: 2015 05 28      13 35 00 
     ************************************************************************/
    
    template <typename T>
    class mySmartPtr
    {
    public:
        //          
        mySmartPtr(): pointer(0), counter(0)
        {
        } 
    
        //           
        mySmartPtr(T* p): pointer(0), counter(0){
            *this = p;
        }
    
        //       
        mySmartPtr(const mySmartPtr &p): 
        pointer(p.pointer), counter(p.counter){
            Increase();
        }
    
        ~mySmartPtr(){
            Decrease();
        }
    
        //         ,    ,     
        mySmartPtr operator=(T* p){
            Decrease();
            if (p){
                pointer = p;
                counter = new int(1);
            }
            else {
                pointer = 0;
                counter = 0;
            }
            return *this;
        }
    
        //          
        mySmartPtr operator=(mySmartPtr &p){
            //      
            if (this != &p){
                Decrease();
                pointer = p.pointer;
                counter = p.counter;
                Increase();
            }
            return *this;
        }
    
        operator bool() const{
            return counter != 0;
        }
    
        // ×     
        T* operator*() const{
            return this;
        }
    
        // ->     
        T* operator->() const{
            return pointer;
        }
    
        //       
        int getPtrPointer() const{
            return *pointer;
        }
    
        //       
        int getPtrCounter() const{
            return *counter;
        }
    
        //          , ptr     ptr     
        template<typename C> friend class mySmartPtr;
    
        template<typename C> 
        mySmartPtr(const mySmartPtr &p): 
            pointer(p.pointer), counter(p.counter){
                Increase();
            }
    
        template<typename C>
        mySmartPtr & operator=(const mySmartPtr &p){
            Decrease();
            pointer = p.pointer;
            counter = p.counter;
            Increase();
            return *this;
        }
    
        //        dynamic_cast      ,      
        template<typename C>
        mySmartPtr Cast() const{
            C* converted = dynamic_cast(pointer);
            mySmartPtr result;
            if (converted){
                result.pointer = converted;
                result.counter = counter;
                result.Increase();
            }
            return result;
        }
    
    private:
        T*      pointer;
        int*    counter;
    
        void Increase(){
            if (counter) 
                ++*counter;
        }
    
        void Decrease(){
            if (counter && --*counter == 0){
                delete pointer;
                delete counter;
                counter = 0;
                pointer = 0;
            }
        }
    
    };