« _CrtSetDbgFlag | トップページ | 「Windows Update エラー」となってアップデートできない。 »

2003年11月24日 (月)

メモリリークに関する考察。

new/deleteにまつわるメモリリークを検出しようと考えた。
new/deleteに関するメモリは全てスマートポインタであるPointerクラスで管理しているので、
Pointerクラスの機能としてメモリリークチェッカーを作ることにした。

newされたメモリは即座にPointerクラスのSetメソッドに渡される。これはPointerクラスの仕様。
そしてdeleteは全てPointerクラス内で行われる。

つまりSetメソッドに入ってきたアドレスとdelete直前のアドレスのログをとればメモリリークがあるかどうかチェックできる。
たとえばSetメソッドに入ってきたアドレスがdeleteされていなかったらメモリリークとなる。




が。C++には多重継承があるのでnewで返ったアドレスと、deleteに渡すアドレスがたとえ同じオブジェクトでも同一とは限らない。
このケースは確かに特殊で、多重継承していて、なおかつ基底クラスが仮想関数を持っていることが条件となる。
が、なんにせよアドレスが違ってしまうので単にnewのアドレスとdeleteのアドレスを==で比較して違っていたらメモリリーク、とは出来ない。


class	A
{
public:
	virtual ~A(){}
};
class	C
{
public:
	virtual ~C(){}
};
class	B :
	public A,
	public C
{};

int __stdcall	WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
{
	B*	pB = new B;
	A*	pA = pB;
	C*	pC = pB;

	//pAとpCはアドレスが異なる!!

	size_t b = sizeof(B);
	size_t c = sizeof(C);

	delete	pA;
}
このコードでは実際にそれを検証している。

以下がdelete文の直前でのウオッチウインドウである。







+ pA 0x00372f88 A *
+ pB 0x00372f88 B *
+ pC 0x00372f8c C *
b 8 unsigned int
c 4 unsigned int


pAとpBは等しいが、pBとpCが異なるのがわかると思う。

ここで注目したいのがbとcである。
B型とC型ではサイズが異なる。B型のほうが大きい。


|372f88-------------B:8-----------------|

+----+----+----+----+----+----+----+----+

			|372f8c----C:4------|
このようにBはCを含んでいる。

よくよく考えてみれば、Bは基底クラスであるAとCの情報を含んでいなければならない。そうでなければAとCに安全にキャストできない。
つまり「派生クラスの使用領域は基底クラスの使用領域を含む」と推論できる。多分そう外れてないと思う。




newされたオブジェクトをさらに派生した型に安全にアップキャストすることは出来ない。
つまりnewされたときの型より派生されることはない。=newされたときのオブジェクトの使用領域が最大である、といえる。
deleteするときにはそれと同じか、それより少ない領域となっている。また、その領域はnew時のものに含まれる。

これを利用すればメモリリークは検出できるだろう。…と思う。

|

« _CrtSetDbgFlag | トップページ | 「Windows Update エラー」となってアップデートできない。 »

コメント

コメントを書く



(ウェブ上には掲載しません)


コメントは記事投稿者が公開するまで表示されません。



トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/61222/972571

この記事へのトラックバック一覧です: メモリリークに関する考察。:

« _CrtSetDbgFlag | トップページ | 「Windows Update エラー」となってアップデートできない。 »