operator new/delete 覚え書

●operator new() のオーバーライドでC2660エラー
MFCのコードをコンパイル中に、operator new() をオーバーライドしようとしてる箇所で、「operator new():3つ以上の引数は指定できません」とかわけのわからんエラーで怒られて困ることがあるが、これはMFCの場合は、メモリリークチェックだか何だかのために、

#ifdef _DEBUG
#define new DEBUG_NEW 
#endif

というとんでもない記述が、コードのどこかにあるためな場合があるので気をつけたい。こんなものはまるごとコメントアウトして切り抜けてくれよな。マイクロソフトのサイトにもそう書かれているわけやし。
http://support.microsoft.com/?scid=kb;en-us;317799&x=11&y=14

●よくよく考えてみれば operator delete() オーバーライドなんかで振る舞いが変えられるはずもない、実体宣言されたメンバ変数の運命とは
たとえあるクラスで空のoperator delete()をオーバーライドしていようとも、delete時に、実体宣言されたメンバ変数のデストラクタが、勝手に呼ばれてしまうのは避けられない。オブジェクトへのポインタではない、オブジェクトを実体として保持しているメンバ変数があった場合は、そのメンバ変数に関しては自動的にそのデストラクタが走ってしまうことは避けられないので気をつけたい。例えばvector< int > とかをメンバに持っていた場合、それも問答無用に空になってしまうよ。これはC++言語の仕様というものだ。

自分で確保したメモリプール上での話だからって、delete 呼ばれても何もしない、空振るようにしてやろうと、空のoperator delete()をオーバーライドしたつもりだが、delete呼んだ時点で、実体で持ってるメンバは全部デストラクタ呼ばれちゃうから気をつけよう。
だからそういう場合、つまり、実体宣言したメンバ変数のデストラクタがいちいち呼ばれてほしくない場合は、デストラクタと同じ内容を書いた、オリジナルなDelete()メソッドを作ってそれを呼ぶようにするなどの工夫が必要だね。継承階層がある場合は「下からつくり、上から崩す」の原則に則った順番でDelete()が呼ばれるようにしたりね。あとはメンバ変数は実体で持たないようにするのも手かも。でもやっぱそういう場合はdelete呼ばないしくみにするのが気分いいかもね。

#include <iostream>

class A
{
public:
  A( void ){}
  ~A( void ){ std::cout<<"u cant stop this dtor"<<std::endl; }
};

class B
{
public:
  B( void ){}
  ~B( void ){}
  void* operator new( size_t sz ){ std::cout<< "op new"<<std::endl; return &m_pool[0]; }
  void  operator delete( void* p ){ std::cout<< "op delete"<<std::endl;}
  A a;// B のインスタンスの delete で、必ず、aに対してデストラクタが呼ばれる
  A *c;// ポインタの先のオブジェクトはそのまま。そのためのデストラクタだ。
  static char m_pool[];
};
char B::m_pool[ sizeof(B) ];

void main( void )
{
  {
    B* b = new B();
    delete b; // "u cant stop this dtor "
  }
  return;
}


結果は以下のようになる。operator delete() は、実体メンバのデストラクタ呼び出しが終わったその後に呼ばれるものに過ぎないということが分かる。つまりそもそもメンバ変数のデストラクタ呼び出しとoperator delete()のオーバーライドは全く別の話なわけで。operator delete()は、独自に確保したメモリをシステムに返すとか、そういう処理を記述するところだからね。よくよく考えてみれば。

op new
u cant stop this dtor
op delete