#pragma twice

KAB-studio > プログラミング > #pragma twice > 380 Version 17.25 戻り値の一時変数

#pragma twice 380 Version 17.25 戻り値の一時変数

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

 Version 17.25
戻り値の一時変数

前回はスマートポインタの概要を説明しました
まだ全然分からないけど
というわけで前回の続きから。 GetPrinterInstance() 関数の以下の箇所
で、 CSmartPointer クラスの変数が作られます

        CSmartPointer cSmartPointer( new CDebugPrinter() );

この cSmartPointer 変数は、コンストラクタでこんな感じに作られます

┌ cSmartPointer───┐          ┌ CDebugPrinter クラスの変数 ─┐
│m_pcPrinter →→→→→→→→→→│Output()                      │
│m_piRefCounter →→→→→→     └───────────────┘
└──────────┘    ↓     ┌ int 型変数 ┐
                             →→→│ 1          │
                                   └──────┘
 cSmartPointer 変数が【スマートポインタ】、右の CDebugPrinter クラス
が Version 17.20 ( No.375 ) から使用している【使用したいクラス】
この CDebugPrinter クラスがポリモーフィズムするんだよね
そういうこと。正確に言うと、 cSmartPointer 変数の中にある 
m_pcPrinter ポインタがポリモーフィズムする感じかな
で、最後の int 型変数が、参照カウンタっていうのなんだよね
そう、これから説明するけど、 CDebugPrinter クラスの変数のアドレス
は複数のスマートポインタが持つことになります
その数をここに取っておく、ってことなんだよね
そういうこと。実際にそうなる流れを見ていきます。先ほどの変数を作っ
ている箇所の次の行は、以下のようになっています

        return cSmartPointer;

さっきの変数を return で返してるわけね
この return で返す変数は、以下の GetPrinterInstance() 関数を呼び出
している箇所に置き換わります

    CSmartPointer cSmartPointer = GetPrinterInstance( DEBUG_PRINTER );

つまり、これは以下のようになっている、ということです

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

関数が return で返した変数に置き換わる、ってわけね
ここのポイントは以下の2点

・左の cSmartPointer 変数が、今、作られようとしている。
 →コンストラクタが呼び出される。
・ = の左と右が同じ型。
 →コピーコンストラクタが呼び出される。

コピーコンストラクタ?
そう、 Version 16.12 ( No.339 ) で説明したように、 = 演算子でも
〈変数をを作るとき〉ならコンストラクタが呼び出されます。加えて、同じ 
CSmartPointer クラスだから
コピーコンストラクタが呼び出される、ってわけね
そこで CSmartPointer クラスのこのコピーコンストラクタが呼び出され
ます

    // コピーコンストラクタ。
    CSmartPointer( const CSmartPointer &p_rcSmartPointer )
        : m_pcPrinter( p_rcSmartPointer.m_pcPrinter )
        , m_piRefCounter( p_rcSmartPointer.m_piRefCounter )
    {
        AddRef();
    }

 m_pcPrinter と m_piRefCounter のメンバ変数をコピーしてるんだね
この時点で、図にすると以下のようになります。

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

あ、そっか。さっきの〈関数が戻り値に置き換わる〉のだと、

    CSmartPointer cSmartPointer = cSmartPointer;
                     (左)              (右)

左右の cSmartPointer って、両方のメンバ変数が同じ変数指してるんだ
そう、両方とも同じアドレスを持ってるからね。さらに、このあと
コピーコンストラクタは AddRef() メンバ関数を呼び出しています

    // 参照カウンターを 1 増やします。
    void AddRef()
    {
        // 参照カウンターをひとつ増やします。
        ++( *m_piRefCounter );
    }

この m_piRefCounter メンバ変数は、上の図の〈 int 型変数〉を指して
るから
ってことは、この 1 が増えるんだ
だからこうなります

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

前回説明したように、この int 型変数は参照カウンター、つまり
〈CDebugPrinter クラスの変数〉のアドレスを持っているスマートポインタ
の数を持っています
あ、ってことは、 cSmartPointer(左) が作られたから、 1 増やしたって
こと?
そういうこと。重要なことは〈コピーコンストラクタが呼び出される〉と
いうことは必ず〈元々 CSmartPointer クラスの変数があってそれが渡され
る〉ということ。だから、ポインタをコピーして、参照カウンターを増やす
んです
……絶対にそれ以外はない、ってこと?
そう、コピーコンストラクタは同クラスの変数が渡された時にしか呼ばれ
ない、っていうルールがあるからね。そのルールをふまえて、こういうふう
に作っているわけ
なんかパズルみたい……
それはあるかもね。さて、ここからまた複雑になります
げげ!!
さっきの、関数が戻り値に置き換わった部分を見てみます

    CSmartPointer cSmartPointer = cSmartPointer;
                     (左)              (右)

この右側の cSmartPointer 変数は、元々は GetPrinterInstance() 関数
の変数です
そだね、 return で返したのだから
で、関数の中で作った変数は……
関数から抜けるとなくなる!
そう、だから cSmartPointer (左)のコピーコンストラクタが呼び出され
たあと、 cSmartPointer (右)のデストラクタが呼び出されるんです。

            先にこちらのコピーコンストラクタが呼ばれる。
                       ↓    そのあとこちらのデストラクタが呼ばれる。
                       ↓               ↓
    CSmartPointer cSmartPointer = cSmartPointer;
                     (左)              (右)

デストラクタって Version 11.11 ( No.211 ) でやった、変数がなくなる
時に呼び出されるメンバ関数だよね
そう、 CSmartPointer クラスだと以下のメンバ関数がそうです

    // デストラクタ。
    ~CSmartPointer()
    {
        Release();
    }

そうそう、クラス名に【~】を付けたのがそうなんだよね
このデストラクタが呼び出されると、その中で Release() メンバ関数を
呼び出します。このメンバ関数はこうなっています

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

このメンバ関数では、まず参照カウンターを減らします

        --( *m_piRefCounter );

お、 AddRef() メンバ関数と逆だ
こうすることで、 int 型変数が 1 減ります

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

そのあと、この参照カウンターが 0 になったかチェックします

        // 参照カウンターがゼロになったら解放します。
        if( *m_piRefCounter == 0 )

でも 0 になってないよね
うん、だからこの段階では if の中には入りません。これはあとでまた説
明します
で、このメンバ関数は終わり、かな
そう、デストラクタからも抜けて cSmartPointer (右)はなくなります

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

ちゃんと参照カウンターの整合性が取れてることに注目してください
 CDebugPrinter クラスの変数を指してるスマートポインタが 1 つだけ、
だから int 型変数は 1 、おおホントだ!
というわけで次回に続く!

/*
    Preview Next Story!
*/
なんか本当にパズルみたい
 C++ っていうルールで遊ぶ感じかな
すんごく複雑なルールだけど
そのルールをちゃんと理解すれば……
楽しめるようになる?
というわけで次回
< Version 17.26 スマートポインタの自動解放 >
につづく!
ま、楽しくなってはまるとそれはそれで問題だけど
だめじゃん
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。