#pragma twice

KAB-studio > プログラミング > #pragma twice > 381 Version 17.26 スマートポインタの自動解放

#pragma twice 381 Version 17.26 スマートポインタの自動解放

前のページへ 表紙・目次へ 次のページへ

 Version 17.26
スマートポインタの自動解放

それでは前回の続き。前回は、

    CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );

この右側の GetPrinterInstance() 関数が、 return で返す変数に置き換
わって以下のようになって、

                           return cSmartPointer;
                                     ↓
    CSmartPointer cSmartPointer = cSmartPointer;
                     (左)              (右)

この時は、ふたつの cSmartPointer がひとつの CDebugPrinter クラスの
変数のアドレスを持ってるんだよね
そう、こうなります

┌ cSmartPointer(右)──┐        ┌ CDebugPrinter クラスの変数 ─┐
│m_pcPrinter →→→→→→→→→→│Output()                      │
│m_piRefCounter →→→→→→     └───────────────┘
└───────────┘  ↓     ┌ int 型変数 ┐      ↑
   ↑1つめ。                 →→→│ 2 ←増えた │      ↑
   ↓2つめ。                ↑     └──────┘      ↑
┌ cSmartPointer(左)──┐  ↑                           ↑
│m_piRefCounter →→→→→→                            ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘ 

そのあと、右側の cSmartPointer は関数の戻り値なのでなくなるため、
以下のようになります

                                  ┌ CDebugPrinter クラスの変数 ─┐
         (なくなった)             │Output()                      │
                                  └───────────────┘
                                   ┌ int 型変数 ┐      ↑
                             →→→│ 1          │      ↑
                            ↑     └──────┘      ↑
┌ cSmartPointer(左)──┐  ↑                           ↑
│m_piRefCounter →→→→→→                            ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘ 

ここまでが以下の行

    CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );

この1行にこんな濃い内容が……
この cSmartPointer 変数内にある〈CDebugPrinter クラスの変数〉の
アドレスは GetPointer() メンバ関数で取得できるので、以下のように取得
して、そこからメンバ関数を呼び出すことができます

    // 出力します。
    cSmartPointer.GetPointer()->Output( "あいうえお\n" );

ここ、ポリモーフィズムしているからね
あ、そか、 Version 17.20 ( No.375 ) のここと同じなんだもんね

    // 出力します。
    pcPrinter->Output( "あいうえお\n" );

 GetPointer() メンバ関数の戻り値は CPrinter クラスのポインタだから
だけどアドレスは CDebugPrinter クラスの変数ので、 
Output() メンバ関数は仮想関数でオーバーライドされてるから、ってこと

そういうこと。こうやって中に保存してあるポインタを取得できること、
ポリモーフィズムされるからこそ、このスマートポインタを使って 
Version 17.20 ( No.375 ) と同じことができるんだからね
そのためのスマートポインタなんだもんね
そういうこと。で、加えてスマートポインタには〈自動的に delete する
機能〉が備わっています
それがスマートポインタの目的、なんだよね
そう。これからその仕組みを説明します。次の行を見てください

    // 次にダイアログ用を受け取ります。
    cSmartPointer = GetPrinterInstance( DLG_PRINTER );

これって最初に取得したところとほとんど同じね
うん、ほとんど同じ。まず GetPrinterInstance() 関数を呼び出します。
ただ、今度は DLG_PRINTER を渡しています
そうすると CDlgPrinter クラスの変数を作るんだよね
そう、 DLG_PRINTER を渡すことで以下の箇所が実行されます

// 出力クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CSmartPointer GetPrinterInstance( int p_iFlag )
{
// (略)
    // else
    CSmartPointer cSmartPointer( new CDlgPrinter() );
    return cSmartPointer;
}

これがスマートポインタの cSmartPointer 変数に渡されて返される、と
図にするとこうなります

┌ cSmartPointer────┐        ┌ CDlgPrinter クラスの変数 ─┐
│m_pcPrinter →→→→→→→→→→│Output()                    │
│m_piRefCounter →→→→→→     └──────────────┘
└───────────┘  ↓     ┌ int 型変数 ┐
                             →→→│ 1          │
                                   └──────┘

これが戻り値で返されると、関数と置き換わるから

    cSmartPointer = GetPrinterInstance( DLG_PRINTER );



             return cSmartPointer;
                          ↓
    cSmartPointer = cSmartPointer;
         (左)            (右)

って置き換わるんだよね
そういうこと。で、ここで重要なのは、左がすでに作られている 
cSmartPointer 変数だっていうこと。さっきの部分と比較してみようか

                     ↓変数を宣言している(新しく作っている)
    CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );
...
         ↓すでに存在している
    cSmartPointer = GetPrinterInstance( DLG_PRINTER );

そして、 この cSmartPointer は〈CDebugPrinter クラスの変数〉の
アドレスを持ってる、ってことを思い出して
そだ、ってことは……

    CDebugPrinter クラスの変数のアドレスを持つ
          ↓             ↓CDlgPrinter クラスの変数のアドレスを持つ
    cSmartPointer = cSmartPointer;
         (左)            (右)

つまり、この左と右とでは、持っているアドレスが違うわけです
そうなると……?
左が持っているアドレスが、右が持っているアドレスで置き換わります。
具体的には = 演算子のオーバーロードメンバ関数が呼び出されます

    // = 演算子をオーバーロードします。
    CSmartPointer &operator =( const CSmartPointer &p_rcSmartPointer )
    {
        if( m_pcPrinter != p_rcSmartPointer.m_pcPrinter )
        {
            // 対象ポインタのアドレスが違う場合は
            // 参照カウンターを減らします。
            Release();
        }

        // コピーします。
        m_pcPrinter = p_rcSmartPointer.m_pcPrinter;
        m_piRefCounter = p_rcSmartPointer.m_piRefCounter;
        // 参照カウンターを増やします。
        AddRef();

        return *this;
    }

うぉ、こんなの用意してあったんだ
このメンバ関数は、まず左右のアドレスを比較して、異なる場合には
Release() メンバ関数を呼び出して参照カウンターを減らします
ここではアドレスが違うから呼び出されるね
そこで、 Release() メンバ関数をもう一度見てみます

    // 参照カウンターを 1 減らし、ゼロなら解放します。
    void Release()
    {
        // 参照カウンターをひとつ減らします。
        --( *m_piRefCounter );
        // 参照カウンターがゼロになったら解放します。
        if( *m_piRefCounter == 0 )
        {
            delete m_pcPrinter;
            delete m_piRefCounter;
        }
    }

ここで参照カウンターを 1 減らします。すると、先ほどの図だと以下の
ようになります


                                  ┌ CDebugPrinter クラスの変数 ─┐
                                  │Output()                      │
                                  └───────────────┘
                                   ┌ int 型変数 ┐      ↑
                             →→→│ 0←減った  │      ↑
                            ↑     └──────┘      ↑
┌ cSmartPointer(左)──┐  ↑                           ↑
│m_piRefCounter →→→→→→                            ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘ 

あ、参照カウンターが0になった!! ってことは……
その次の if の条件に合うから、 delete されます

            delete m_pcPrinter;
            delete m_piRefCounter;

つまり、このふたつの変数が delete されるというわけです!


                                  ┌ CDebugPrinter クラスの変数 ─┐
                     →           │Output()                      │
      両方の変数が                └───────────────┘
      deleteされます               ┌ int 型変数 ┐      ↑
                     →      →→→│ 0          │      ↑
                            ↑     └──────┘      ↑
┌ cSmartPointer(左)──┐  ↑                           ↑
│m_piRefCounter →→→→→→                            ↑
│m_pcPrinter →→→→→→→→→→→→→→→→→→→→→→
└───────────┘ 

……ってことは、つまりこれが〈スマートポインタが自動的に delete 
してくれた〉ってこと……?
そういうこと! というわけで次回に続く!

/*
    Preview Next Story!
*/
や、やっとスマートポインタの目的まで来たね……
この自動解放ができないとスマートポインタの意味がないからね
……あれ、でも続く?
スマートポインタはもっともっと複雑なんです

というわけで次回
< Version 17.27 スマートポインタの自動解放、の続き >
につづく!
スマートポインタはここからがおもしろいんです
……もうやめにしない?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。