メモリリーク検出

VC++では_Crt〜関数でメモリリークを容易に検出できるが、使い方を間違えてありもしないメモリリークで悩んでいる人がいた。コードはこんな感じ。

//foo.cpp
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__)

//てきとー
class Foo {
private:
  char* buf;
public:
  Foo() {
    buf = new char[1];
  }
  virtual ~Foo() {
    delete [] buf;
  }
};

void main(void) {

  //ダンプ先を標準出力に設定
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);

  Foo f;

  //メモリリーク情報ダンプ
  _CrtDumpMemoryLeaks();
}

で、コンパイル

Z:\>cl /D_DEBUG /MDd foo.cpp

で、実行。

Z:\>foo
Detected memory leaks!
Dumping objects ->
foo.cpp(13) : {48} normal block at 0x00372EF0, 1 bytes long.
 Data: < > CD
Object dump complete.

プログラム終了時にはリークはないが_CrtDumpMemoryLeaks()の時点ではFooクラスのデストラクタが呼ばれていないのでダンプされる。Java使いの彼はデストラクタが呼ばれるタイミングなんて考えたことがないそうだ…。で、面倒だったので対応はこう。

void main(void) {
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);

  {
    Foo f;
  }

  _CrtDumpMemoryLeaks();
}

これで_CrtDumpMemoryLeaks()に前にFooクラスのデストラクタが呼ばれるのでダンプされなくなる。
本来?は_CrtDumpMemoryLeaks()を直接呼ばずに_CrtSetDbgFlag()を使う。_CRTDBG_LEAK_CHECK_DFを指定することでプログラム終了時に_CrtDumpMemoryLeaks()を呼んでくれるという優れもの。

void main(void) {
  _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
  _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

    Foo f;
}