基底クラスのデストラクタをvirtualにしなくてもメモリリークしない場合

こんな感じのコードを目にしました。

#include <vector>

struct Base {
  ~Base(){} // non-virtual
  virtual void doSomething() = 0;
};

struct Derived1 : public Base {
  virtual void doSomething(){ ++x; }
  int x;
};

struct Derived2 : public Base {
  virtual void doSomething(){ x ^= y, y ^= x, x ^= y; }
  int x;
  int y;
};

int main(){
  std::vector<Base*> vec;
  for (int i = 0; i < 100000; ++i) {
    vec.push_back(new Derived1());
    vec.push_back(new Derived2());
  }
  for (Base* base : vec) {
    delete base;
  }
  return 0;
}

「おおっとーBaseのデストラクタがvirtualになってないぞーサブクラスの拡張部分が解放されない!」と思ったけど、これ実際に動かしてみるとメモリリークしません(VC2005とg++4.6で確認)。
デバッガ使ってみると、確かにDerivedのデストラクタは呼ばれていないのだけど…。


推測ですが、new Derived1()したときに返るポインタが内部的には malloc(sizeof(Derived1)) によって取得されたもので、それに対してdeleteするとfree()が呼ばれるようになっている。つまり結局はmallocしたものをfreeしてるというだけなので解放できてしまっているんじゃないかと。

newやdeleteのデフォルトの実装がそうなるよう決められているわけではないと思うので、きっと環境依存の振る舞いでしょうが…


もちろん、次のようにDerivedのメンバに要解放リソースを確保していたらリークします。

#include <vector>

struct Base {
  ~Base(){} // non-virtual
  virtual void doSomething() = 0;
};

struct Derived1 : public Base {
  Derived1() { x = new int(1); }
  virtual void doSomething(){ ++x; }
  int* x;
};