2014年5月13日火曜日

C++ 標準ライブラリ auto_ptr の代替の実装

C++ 標準ライブラリであるauto_ptrの代替を実装する。

auto_ptrについての念のため前提知識。
C++ではnewで確保したメモリは、使用後にdeleteを呼び出して解放しなければならない。
しかしながら、returnが複数あったり、例外が絡むと解放忘れが起こりえる。
そういった場合には標準ライブラリにあるauto_ptrを使い、
auto_ptrの変数がスコープから消える際に、
自動でポインタの解放処理を行わせる。

実際はいろいろと複雑なことをしているのだろうが、
下記のコードと大筋やっていることは同じである。


#include <iostream>
#include <string.h>

template<typename T>
class My_auto_ptr
{
  private:
  T *mP;

  public:
  My_auto_ptr(T *p = 0) ※1
    : mP(p)
  {
    std::cout << "constructor" << std::endl;
  }

  ~My_auto_ptr()
  {
    std::cout << "destructor" << std::endl;
    delete mP;
  }

  // 本筋ではないがこういったものがあると便利である
  T* operator->() {return mP;} ※2
  T& operator*() {return *mP;} ※3
};


int main()
{
  My_auto_ptr <int> hoge = new int(10);

  return 0;
}
※1
未初期化を防ぐバグ防止のためである。

※2
オペレータのオーバーロードである。
mPがクラスへの参照であった場合、
ポインタのように見せられる。
hoge->func();
下記と同じである。
(hoge.operator->())->func();

※3
同じく、オペレータのオーバーロードである。
代入を行わせる。
書き換えの可能性があるので参照を返すこと(T &)。
*hoge = 10;
下記と同じである。
hoge.operator*() = 10;


その他
改善すべき点が多々おもいつくだろう。

(改良点1)
delete時は0ポインタを入れて解放するようにした方がいい。
そのためのマクロとしてSAFE_DELETEというものを用意しておくと便利である。

(改良点2)
こういったことをすればhoge1、hoge2で2回deleteされてしまう。
My_auto_ptr hoge1 =new int(10)
My_auto_ptr hoge2 = hoge1

代入を禁止をすればいいだろうか。
しかし関数の引数として使う場合(func(hoge1)など)にコピーされる問題をどうするか。
参照で乗り切るか。
ただし用途によっては参照ではなく実体のフィールドを入れたい場合もある。
その対策として簡易的な参照カウンタなどを作ればいいだろう。