#pragma twice

KAB-studio > プログラミング > #pragma twice > 382 Version 17.27 スマートポインタの自動解放、の続き

#pragma twice 382 Version 17.27 スマートポインタの自動解放、の続き

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

 Version 17.27
スマートポインタの自動解放、の続き

では前回の続きから。まずこの行の話でした

    cSmartPointer = GetPrinterInstance( DLG_PRINTER );

右側の GetPrinterInstance() 関数もスマートポインタを返すんだよね
そう、だからこの関数を呼び出している部分が戻り値に置き換わります

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

でも、左右の持ってるアドレスが違う、と
このとき、 = 演算子のオーバーロードメンバ関数が呼び出されます

    // = 演算子をオーバーロードします。
    CSmartPointer &operator =( const CSmartPointer &p_rcSmartPointer )

この中でアドレスが異なる場合には Release() メンバ関数を呼び出すよ
うにしてあります。で、 Release() メンバ関数は、参照カウンターが0に
なったら持っている変数を delete します

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

つまりこれが、〈スマートポインタが自動的に delete してくれた〉って
ことなんだよね
そういうこと。スマートポインタの最大のメリットは〈アドレスを誰も
持っていなかったら delete する〉っていう機能を持つこと。そうすること
で、適切なタイミングで delete されるわけです
まだ使っている時に delete することもないし、 delete し忘れて
メモリリークすることもない。必要なくなったらすぐに delete する、って
わけね
そういうこと。スマートポインタを使用することで〈使わなくなったら 
delete する〉っていう処理が自動的にできるわけです
なるほど、確かに便利……
さて、これで cSmartPointer 変数(左)が持つふたつのメンバ変数が
空きました。ここに、 cSmartPointer 変数(右)のアドレスをコピーします

    // = 演算子をオーバーロードします。
    CSmartPointer &operator =( const CSmartPointer &p_rcSmartPointer )
    {
// (略)
        // コピーします。
        m_pcPrinter = p_rcSmartPointer.m_pcPrinter;
        m_piRefCounter = p_rcSmartPointer.m_piRefCounter;
        // 参照カウンターを増やします。
        AddRef();

アドレスをコピーして AddRef() メンバ関数を呼び出すことで、以下のよ
うにふたつのスマートポインタからひとつの〈 CDlgPrinter クラスの変数〉
のアドレスを持つことになるわけです

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

前回の最初と同じ状態、ってことね
そういうこと。この時点で、左右同じアドレスを持つわけです

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

左の方のが削除されて、右のに置き換わったわけね……
で、同じように、 cSmartPointer(右) は関数の戻り値だから
なくなってデストラクタが呼び出されるわけね
そして Release() メンバ関数が呼び出されて参照カウンターが1減らされ
ます。だから図にするとこうなります

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

こうして、 cSmartPointer(左) が持つアドレスが〈 CDlgPrinter クラス
の変数〉のアドレスに置き換わりました
おお
なので、次の行で GetPointer() メンバ関数を呼び出すと、この
〈 CDlgPrinter クラスの変数〉のアドレスが返されます

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

前回と同じで、ここでポリモーフィズムが機能してるわけね
そういうこと。 GetPointer() メンバ関数の戻り値の型は CPrinter クラス
のポインタだけど、実際にはその派生クラスの CDlgPrinter クラスの
アドレスが返される、というわけです
で、 Output() メンバ関数は仮想関数でオーバーライドされてるからそっ
ちが呼び出される、と。なるほどねー
さて、最後の締め
あれ? これで終わりじゃないの?
まだ残ってるよ。今 cSmartPointer 変数が持っている、
〈 CDlgPrinter クラスの変数〉と参照カウンターの〈 int 型変数〉を
delete しないと
あ、そか、それしないとメモリリークしちゃうね。でも、さっきは 
= 演算子をオーバーロードしていて、それで = で代入したからそれが呼ば
れたけど、今度はどうするの?
今実行してる WinMain() 関数をもう一度見てみようか

int WINAPI WinMain
    ( HINSTANCE p_hInstance
    , HINSTANCE p_hPrevInstance
    , LPSTR p_pchCmdLine
    , int p_iCmdShow
    )
{
    // 出力用に、 CSmartPointer クラスを受け取ります。
    // まずデバッグ用を受け取ります。
    CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );
    // 出力します。
    cSmartPointer.GetPointer()->Output( "あいうえお\n" );

    // 次にダイアログ用を受け取ります。
    cSmartPointer = GetPrinterInstance( DLG_PRINTER );
    // 出力します。
    cSmartPointer.GetPointer()->Output( "あいうえお\n" );
    ←ここまで来た。
    return 0;
}

これから WinMain() 関数から抜けます。すると cSmartPointer 変数は?
なくなる……そか、デストラクタが呼ばれる!!
そういうこと。クラス型変数がなくなるときにはデストラクタが呼び出さ
れます
ってことは、 GetPrinterInstance() 関数から抜けるときと同じで、
デストラクタが呼び出されて、さらに Release() メンバ関数が呼び出され
ると

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

このメンバ関数では、まず参照カウンターがひとつ減ります

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

参照カウンターが 0 になった!!
ということは、その下の if の中に入るので delete が実行されます

            delete m_pcPrinter;
            delete m_piRefCounter;

で、前回と同じように CDlgPrinter クラスの変数と int 型変数が解放
されるわけね

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

というわけで、 WinMain() 関数から抜けたときには、 new で作られた
ふたつの変数はちゃんと delete されて、メモリリークは起きない、という
わけです
すご……

/*
    Preview Next Story!
*/
もう頭がこんがらがっちゃう!
まぁ、これは作るのも使うのも難しいからね
……使うくらいならなんとかならない?
実は使う時も注意が必要なんです
ええー!?
というわけで次回
< Version 17.28 スマートポインタのまとめ >
につづく!
こういう奥の深さが面白くなってくると……
それマゾよ絶対!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。