smart_ptr




スマートポインタは、yaneSDK2ndのころからあったので、活用されてきた方も多いと思います。詳しくは、「天才ゲームプログラマ養成ギプス」など参考にしていただくとして、ここでは、yaneSDK2ndからの変更点を簡単におさらいしておきます。

1.ディフォルトで所有権を持つようになった

枠-1
    smart_ptr<CHoge> p( new CHoge );


これで、このポインタpは所有権を持つようになります。

2.所有権を持たせたくない場合、

枠-2
    smart_ptr<CHoge> p( new CHoge , false );


というように第2パラメータにfalseを指定します。

3.基底クラスにアップキャストできる。かつ、ポリモーフィックな配列を使える

枠-3 例)ポリモーフィックな配列
    class CBase { int a; };
    class CDerived : public CBase { int b; };

    smart_ptr<CDerived> pDerived;
    pDerived.AddArray(new CDerived[100]);

    //  基底クラスに配列ポインタをキャスト
    smart_ptr<CBase> pBase;
    pBase.UpCase(pDerived);

    //  何と驚くべきことに、これは、正しくアクセスできる
        pBase[30]->a = 100;


また、配列の要素外に対してアクセスすると、CIndexOutOfBoundsException例外が発生します。

特徴はそんなところです。詳しくは、「やね本1」のほうもご覧ください。ここで、この本で書き漏らしたことをいくつか書いておきます。

☆ smart_ptrの受け渡しについて

あるメソッドがsmart_ptrを引数に取る場合、smart_ptrのコピーは、それほど軽くも無いので、オーバーヘッドを避ける意味でも

枠-4
    smart_ptr<CHoge>&


というように、参照で渡したほうが良いでしょう。また、このポインタの値を変更しないのであれば、

枠-5
    const smart_ptr<CHoge>&


というように、const修飾子もつけておくべきでしょう。

☆ 何をsmart_ptrにするか。あるいは、しないか。

smart_ptrの実装は、参照カウント方式です。smart_ptrは、コピーするごとに参照カウントは+1,デストラクトされるときに−1されます。

すなわち、相互参照していては、いつまでたっても解放されない、という状況を引き起こします。(たいていは“解放されないというバグ”です)

list-1 解放されない例
    class CHogeB; // 前方宣言
    class CHogeA {
        smart_ptr<CHogeB> pB;
    };
    class CHogeB {
        smart_ptr<CHogeA> pA;
    };

/*
    こういう状況で、ふたつのオブジェクトが相互参照した場合、
    参照カウントがいつまでたっても0にならないので解放されない
*/


参照カウント方式のスマートポインタでは、こういう状況が考えられるので、無条件にすべてをsmart_ptrにすれば良い、というわけでも無いのです。

(ガーベジコレクタ付きのsmart_ptrを実装する手は無くは無かったのですが、実装コストや、実行速度から考えて参照カウント付きが現実的な妥当なラインだと判断しました)
そこで、どういうときに、smart_ptrを用いれば良いのか、そのガイドラインを示します。

☆ 所有権をあずけたいとき/あずけることがあるとき

一番大きな指針は、これです。

呼び出し側では、いったんそのオブジェクトを作ってしまえば、あとはもうそのオブジェクトを参照することは無いような場合、そのメソッドはsmart_ptrをとってくれると、助かります。

枠-6 メソッド
    void    hoge(const smart_ptr<CHoge>&pHoge);


枠-7 呼び出し側
    some_object.hoge(smart_ptr<CHoge>(new CHoge));


逆に、CMouse(マウスクラス)やCFastDraw(描画クラス)のように、CAppFrame派生クラスが保持していて、アプリケーション当たり1つしかないことが明確であるクラスの場合、所有権を移動させることは有り得ないので、smart_ptrを用いる必要はありません。(用いたところでオーバーヘッドが大きくなるだけです)