利口で賢くて理知的で聡明なポインタ 。。 スマートポインタ の話。 | ゲームプログラマ志望が福岡で叫ぶ 『絶望』

ゲームプログラマ志望が福岡で叫ぶ 『絶望』

プログラマーになりたい!!!!! あ、風のうわさで聞いた最近若者で流行っているトゥイッターなるものを始めてみました (・ト・) @toshi_desu_yo


久々のプログラミング。

お仕事に追われる日々から開放されてちょっと日常が楽しくなってきた(^^ゞ



=============================================


テンプレートの練習もかねて スマートで利口なポインタ


 スマートポインタをやっていきます(`・ω・´)




C++ でプログラムをしていると必ず立ちはだかるメモリリーク。


(例) ↓



まどかさんがまさか現実に存在しないなんて・・・






_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );



を書くことで

プログラム終了時メモリリークがあれば出力画面に出してくれます。
すごく便利


↑の画像では 1Byte のメモリリークが発生していることがわかります。



まぁこれは見てわかるように、

オブジェクトをnewで生成したのに条件によっては
  delete せずにプログラムを終了させています。



そらメモリリークです。




『実際ありえないε- (´ー`*)』


と思われますが、、、、、、、、ありえます( ゚д゚ )
むしろありありです。



こういうミスを無くすためにスマートポインタはあります。



【スマートポインタ】

 :自動的にdeleteを行い、メモリリークを発生させないようにする機構。



使い方としては、


SmartPtr< GodClass > spGod( new Ore );


↑ みたく、スマートポインタに削除依頼をするオブジェクトのポインタを渡します。



あとは、spGodが 誰からも参照されなくなった
スマートポインタが自動的に内部のオブジェクトを delete してくれます。

※ 要するに世界の誰からも相手され無くなったら寂しさの余り自分で自分をころすんですね。
^^ まるで・・・ いや、なんでもないです。






べつにスマートポインタを書かなくても

***************************************************************************
○ new を書いたら delete を忘れずに書く。

***************************************************************************



その約束事を自分の中に確実な決め事としておくと、
スマートポインタなぞいらないかもしれません。



だけどそんな契約書も無ければ罰則もない約束事など締め切り前になると

スッポオオーーーン!! と地平線の彼方に飛んでいきます。




早く完成させないと..! 早く完成させないと..!! 早く完成させないと..!!!!
ガタガタガタガタタガタブrブrブrブrブルbルbルbr



この状態になると new を書いたところで delete の事なぞ覚えているはずがありません。



仮にまだ精神が正常だったとして delete を書く癖が残っていても
  必ずどこかでメモリリークは出ます。 

必ずです。。。。。。(慟哭)




僕も最初は  

『スマートポインタなんて記述がめんどくさいしコード汚くなるし、メモリリーク出たらその時に考えるから別にいらねーよks』



 スマートポインタさんを罵倒していた時期がありました。







結果、痛い目見ました('Д')


『new だけを自分が行い、Deleteを自動的にやってくれる機能があればどれだけうれしいか・・・』

そうプロジェクト開発末期、バグ取りが終わった幻想を見かけた時に思いました。



なのでメンドクサイだろうけどスマートポインタは使っていきましょう!

delete が無いだけでとてもプログラムが気持ちよくなります。




*******************************



ポインタとなるクラスの型は様々です。



MyClass。 PantsuClass。 EroClass。。。。。 etc...


それぞれの型に合わせたスマートポインタクラスを作ろう!

と考えた場合、、 



くssssssssssssssssっそメンドクサイです。



【個別でスマートポインタクラスを作成】

class MyClassSmartPtr
{
    MyClass* mPtr;
...

    MyClassSmartPtr( MyClass* ptr )
        : mPtr( ptr )
    {}
    ~MyClassSmartPtr()
    {
        delete mPtr;
    }
};

class PantsuClassSmartPtr
{
    PantsuClass* mPtr;
...

    PantsuClassSmartPtr( PantsuClass* ptr )
        : mPtr( ptr )
    {}  
    ~
 PantsuClassSmartPtr()
    {
        delete mPtr;
    }
};

class EroCLassSmartPtr
{
    EroClass* mPtr;
...

    EroCLassSmartPtr( EroClass* ptr )
        : mPtr( ptr )
    {}
    
~ EroCLassSmartPtr()
    {
        delete mPtr;
    }

};




↑ デストラクタ時に保持してるポインタを削除。





なので、自由に型を決めることができる テンプレート を使用します。



【型にとらわれないスマートポインタクラス】

template< class T >
class SmartPtr
{
    T* mPtr;
...

    SmartPtr( T* ptr )
        : mPtr( ptr )
    {}
};



こうすることで型という縄( 亀甲 )に縛られない
スマートポインタクラスを作ることができます


テンプレート は最初わけわかめだったですが、使っていくと超便利・・・



SmartPtr< MyClass >     spMyClass( new MyClass );
SmartPtr< PantsuClass> spPantsuClass( new PantsuClass );
SmartPtr< EroClass >     spEroClass( new EroClass );



こんな感じに書いて、あとは放置プレイでOKです。
勝手にdelete をしてくれます。。。。。。。。      


  というクラスを作っていきます!




世の中に出回っているスマートポインタの大半は参照カウンタを内部に持つ

参照カウンタ方式を採用しているのではないかと思います。



SmartPtrクラスの内部に unsigned int 型の変数を一つ持ち、



SmartPtr< MyClass >     spMyClass( new MyClass );

生成が行われると 1 になり、


ほかのスマートポインタにコピーされる  ↓


spMyClass2 = spMyClass;


など、内部のポインタが別のスマートポインタとの共有になるたび、
カウンタが 1 増やされ



スマートポインタが消える時、つまりデストラクタが呼ばれると 1 減らされます。


減らされたときに
  カウンタが 0 になれば、内包してあるポインタを delete する。



こんな感じです。



スマートポインタ間のコピーのことはあとで考える(・・・)として、

とりあえずクラスとしてはこんな感じになるんじゃないでしょうか。



template< class T >
class SmartPtr
{
protected:

    /* 内包されるポインタ */
    T* mPtr;

    /* 参照カウント */
    unsigned int mCount;

public:
    

    /**
      * デフォルトコンストラクタ
      */
    SmartPtr( T* ptr )
        : mPtr( ptr ), mpCount( 0 )
    {
        if ( mPtr )
        {
            mpCount = 1;
        }
    }

    /**
      * デストラクタ
      */
    ~SmartPtr()
    {
        if ( mPtr && --mpCount == 0 )
        {
            delete mPtr;
            mPtr = NULL;
        }
    }

};



実際に使って確認してみる。


int main( void )
{
    //** 自動メモリリークチェッカー

    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );


    SmartPtr< MyClass > spMyClass( new MyClass );

 

    return 0;
}




SmartPtr を MyClass 型のスマートポインタにして MyClass のポインタを保持させる。



終了時にデストラクタが呼ばれて、参照カウンタが0。 なのでdeleteされて、、、


結果、無事メモリリークは起きていません!(`・ω・´)




これを元にスマートポインタクラスを創りあげていけばいいんじゃないかと!

取り敢えずこんなしょぼいスマートポインタ。


・コピー未対応。



使い物にならない・・・




関数の引数や変数間でコピーなんてぬるぬると存在します。

せめてコピーぐらいしとかないといけない。。。



まずコピーするにあたって、 ↑のソースだと


コピーしたスマートポインタ先で参照カウンタが増加、減少してもコピー元のスマートポインタの参照カウンタは影響を受けません。




これだと元のスマートポインタのデストラクタが呼ばれた時点で参照カウンタが0になり、
まだ他のスマートポインタクラスが 0x1234abc 参照しているにもかかわらず delete されます。



ダングリングポインタ( 不正なポインタを指す状態 )になってしまいます。




なので、
参照カウンタもポインタにします。


これだと、



コピーをいたる所で行い参照カウンタが増加、減少してもスマートポインタの参照カウンタも共有しているので、


全ての共有したスマートポインタが使われなくなった時( つまり参照カウンタが0 )に初めてdeleteを行なってくれます。




これならばスマートポインタ同士のコピーにも対応できそうです。


取り敢えず コピーコンストラクタを実装した現時点でのスマートポインタ ↓






template< class T >
class SmartPtr
{
protected:

/* 内包されるポインタ */
T* mPtr;

/* 参照カウント */
unsigned int* mpCount;

public:

/**
* デフォルトコンストラクタ
*/
explicit SmartPtr( T* ptr = NULL )
: mPtr( ptr ), mpCount( NULL )
{
if ( mPtr )
{
mpCount = new unsigned int;
mpCount = 1;
}
}
/**
* コピーコンストラクタ
* この時、まだポインタは保持していないのでReleaseは行わない。
*/
explicit SmartPtr( SmartPtr< T >& sp )
{
mPtr = sp.mPtr;
mpCount = sp.mpCount;

( *mpCount )++;
}


/**
* デストラクタ
*/
~SmartPtr()
{
Release();
}



protected:
/**
* 参照カウンタを 1 減少させる
*/
void Release()
{
if ( mPtr && --*mpCount == 0 )
{
delete mPtr;
delete mpCount;

mPtr = NULL;
mpCount = NULL;
}
}
};



これを使ってみた。


出力画面にスマートポインタの流れを記述。





ちゃんとメモリリークは起きていない。
explicitは今は取り敢えず まぁつけといたら助かるわ。ぐらいで。。。


****************************

① spMyClassAでMyClassのポインタを保持させた時に参照カウンタが 1 になる。
② spMyClassBのコピーコンストラクタでspMyClassAを渡すことで参照カウンタが 2 になる。
③ main関数が終了。
③ spMyClassBのデストラクタが呼ばれて参照カウンタが 1 になる。
④ spMyClassAのデストラクタが呼ばれて参照カウンタが 0 になり delete される。

****************************




後は 

spMyClassB = spMyClassA; 

というコピーもやりたいのでどんどん組み込んでいこうと思います!


取り敢えず以上です!はい!



スマートポインタ は ほんとうに つかうように しま しょ う・・・(´;ω;`)




************************
【 (URL) スマートポインタ01
************************