#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のトップページをご覧ください。