« std::copy | トップページ | ファイルサイズの取得 »

2005年1月12日 (水)

クラスのデストラクタについて

基本的にクラスのデストラクタは仮想にしてthrow()をつける。

class A
{
public:
	virtual ~A() throw(){}
};

仮想にするのは派生クラスのデストラクタを確実に呼ぶため。

class	A{ public: ~A(){ assert(0); } };
class	B : public A{};

{
	B	b;
}

上記の場合、Aのデストラクタは呼ばれない。

派生しないクラスの場合仮想にすると仮想関数テーブルの分だけクラスのサイズが増えるし、書かなくても自動でデストラクタが作られるが、上記が問題となるバグはvirtualの付け忘れに起因することが多い。
そしてたいていこのバグは非常に見つかりにくく、3時間くらいは時間を無駄にしてしまう。
だからたとえ派生させるつもりがないクラスでも習慣的に仮想デストラクタを書く癖をつけたほうがいいと思う。

throw()をつけるのはデストラクタから例外を呼ばないことを保障するため。
class	A{ public: virtual	~A(){ throw 0; } };

try
{
	A	a5;
	{
		A	a4;
		{
			A	a3;
			{
				A	a2;
				{
					A	a1;
				}	//1
			}
		}
	}
}
catch( int& i )
{
	//2
}

上記のコードを追っていくと1のところでa1がスコープアウトしてAのデストラクタが呼ばれ、throw 0;によりスコープの巻き戻しが始まる。
次にa2が解体され、a2のデストラクタで再び例外が投げられる。
このとき例外処理中に例外が起こったことでterminateが呼ばれてプログラムは終了してしまう。
つまりa3~a5は解体されないし、2のところまで実行されることもない。

このためデストラクタでは例外は呼ばないほうがいい。
たとえ例外がネストしないような設計で組んでいても、ふと疲れたときやコードの複雑化で間違えてネストさせてしまうとまたしても解決に時間のかかるバグとなる。

|

« std::copy | トップページ | ファイルサイズの取得 »

コメント

コメントを書く



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


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



« std::copy | トップページ | ファイルサイズの取得 »