#pragma twice

KAB-studio > プログラミング > #pragma twice > 378 Version 17.23 スマートポインタ

#pragma twice 378 Version 17.23 スマートポインタ

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

 Version 17.23
スマートポインタ

前回は delete で解放する方法についていくつか説明しました
なんかトリッキーな感じだったんだけど……
まぁ、メモリを自由に操作できる C++ 言語だから、それだけ自由度が必要
ってところかな
自由すぎ……
さて、今回はもうひとつの問題について

・ポインタをいつ解放するか
 ・使用中に解放してはいけない(存在しない変数を使ってしまう)
 ・解放し忘れてはいけない(メモリリークになる)
 ・解放前にアドレスを上書きしてはいけない(同上)

これは結構大きな問題よね
この問題を解決するための方法のひとつが【スマートポインタ】というも
のです
やせてるポインタ?
違います。スマート、っていうのは利口な、っていう意味
あー、そういえばそういう意味で使うことあるよね。スマートなやり方と

そうそう、そっちの意味。スマートポインタの考え方は

・スマートポインタはクラス。
・中に、実際に使用するクラスのポインタを持つ。
・スマートポインタはポインタではなく普通の変数として使う。
・【参照カウンター】へのポインタをメンバ変数に持つ。
・複製ができたら参照カウンターを増やし、変数が消えたら減らす。
・参照カウンターがゼロになったときだけ解放する。

というものです
わけわかりません
なので実際に例を使って見てみましょう。まずはプログラム全文

// Main.cpp
#include <Windows.h>
#include <stdio.h>

// 出力を行うクラスのインターフェイス。
class CPrinter
{
public:
    // 出力関数。純粋仮想関数です。
    virtual void Output( const char *p_pch ) = 0;
};

// デバッグ出力を行うクラス。
class CDebugPrinter : public CPrinter
{
public:
    // 出力関数。オーバーライドしています。
    void Output( const char *p_pch )
    {
        // デバッグ出力します。
        OutputDebugString( p_pch );
    }
};

// ダイアログ出力を行うクラス。
class CDlgPrinter : public CPrinter
{
public:
    // 出力関数。オーバーライドしています。
    void Output( const char *p_pch )
    {
        // ダイアログに出力します。
        MessageBox( NULL, p_pch, "デバッグ", MB_OK );
    }
};

// スマートポインタクラス。
class CSmartPointer
{
    // これが対象のポインタ。
    CPrinter *m_pcPrinter;
    // 参照カウンターへのポインタ。
    int *m_piRefCounter;
public:
    // コンストラクタでポインタを渡します。
    CSmartPointer( CPrinter *p_pcPrinter )
        : m_pcPrinter( p_pcPrinter )
        , m_piRefCounter( new int )
    {
        // 参照カウンターに 1 をセットします。
        *m_piRefCounter = 1;
    }

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

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

    // = 演算子をオーバーロードします。
    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;
    }

    // 対象ポインタが持つアドレスを返します。
    CPrinter *GetPointer()
    {
        return m_pcPrinter;
    }

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

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

// フラグ用定数値。
const int DEBUG_PRINTER = 0;
const int DLG_PRINTER = 1;

// 出力クラスを返す関数。
// 引数が DEBUG_PRINTER なら CDebugPrinter クラスのポインタを、
// DLG_PRINTER なら CDlgPrinter クラスのポインタを返します。
CSmartPointer GetPrinterInstance( int p_iFlag )
{
    // フラグによって出力を変更します。
    if( p_iFlag == DEBUG_PRINTER )
    {
        CSmartPointer cSmartPointer( new CDebugPrinter() );
        return cSmartPointer;
    }
    // else
    CSmartPointer cSmartPointer( new CDlgPrinter() );
    return cSmartPointer;
}

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;
}

長!
最初の CPrinter 、 CDebugPrinter 、 CDlgPrinter の3クラスは 
Version 17.20 ( No.375 ) の時のものと同じです
あ、ホントだ
残りは次回説明します!

/*
    Preview Next Story!
*/
久々に長いプログラムねー
スマートポインタは複雑だから
メンバ関数がいっぱいある……
でも、これが理解できればクラスの大部分が理解できたことになるかも
おお!
というわけで次回
< Version 17.24 スマートポインタと参照カウンター >
につづく!
ま、それだけ難しいってことなんだけどね
なんですとー!
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。