アリスはvoid*がお嫌い

久々の「アリプラ」シリーズです(自分も忘れていたよ)。

void*にdeleteしてもデストラクタが呼ばれない!? – かずきのBlog@Hatena
http://d.hatena.ne.jp/okazuki/20120204/1328323854

というのを見つけて、一瞬「?」と思ったのですが、確かにそうですね。delete するときに void* を渡すと型情報が失われる…というか、delete が型情報を判別できないので、メモリとしか解放されなくてデストラクタが呼び出されません、という現象です。

配列を new したときに、「delete [] ポインタ」 で解放しないといけません。ってのと同じ話だと思います。

#include <iostream>
using namespace std;

// アリスクラス
class Alice
{
public:
	Alice() {
		cout << &quot;in constractor&quot; << endl;
	}
	~Alice() {
		cout << &quot;in destractor&quot; << endl;
	}
};

class Person
{
public:
	Person() {
		cout << &quot;in Parson::Parson&quot; << endl;
	}
	virtual ~Person() {
		cout << &quot;in Parson::~Parson&quot; << endl;
	}
};
// ロリータクラスは、Person クラスを継承する
class Lolita : public Person
{
public:
	Lolita() {
		cout << &quot;in Lolita::Lolita&quot; << endl;
	}
	virtual ~Lolita() {
		cout << &quot;in Lolita::~Lolita&quot; << endl;
	}
};

// アリスはvoid*がお嫌い
int main(int argc, char *argv[] )
{
	{
		cout << &quot;自動で解放&quot; << endl;
		Alice alice;
	}

	{
		cout << &quot;自前で解放&quot; << endl;
		Alice *alice = new Alice();
		delete alice;
	}

	{
		cout << &quot;void*で解放&quot; << endl;
		void *alice = new Alice();
		delete alice;
	}

	{
		cout << &quot;継承あり&quot; << endl;
		Lolita *lolita = new Lolita();
		delete lolita;
	}
	{
		cout << &quot;継承あり Person&quot; << endl;
		Person *lolita = new Lolita();
		delete lolita;
	}
	{
		cout << &quot;継承あり void*&quot; << endl;
		void *lolita = new Lolita();
		delete lolita;
	}
	{
		cout << &quot;継承あり void* から Lolita へキャスト&quot; << endl;
		void *lolita = new Lolita();
		delete (Lolita*)lolita;
	}
	{
		cout << &quot;継承あり void* から Person へキャスト&quot; << endl;
		void *lolita = new Lolita();
		delete (Lolita*)lolita;
	}
	return 0;
}

/* 実行結果
D:\work\blog\src\alice>alice015
自動で解放
in constractor
in destractor
自前で解放
in constractor
in destractor
void*で解放
in constractor
継承あり
in Parson::Parson
in Lolita::Lolita
in Lolita::~Lolita
in Parson::~Parson
継承あり Person
in Parson::Parson
in Lolita::Lolita
in Lolita::~Lolita
in Parson::~Parson
継承あり void*
in Parson::Parson
in Lolita::Lolita
継承あり void* から Lolita へキャスト
in Parson::Parson
in Lolita::Lolita
in Lolita::~Lolita
in Parson::~Parson
継承あり void* から Person へキャスト
in Parson::Parson
in Lolita::Lolita
in Lolita::~Lolita
in Parson::~Parson
*/

普通に void* のまま delete してしまうと、デストラクタが呼び出されませんが、delete 時に元の型(Lolita)に戻してやると、正常にデストラクタが呼び出されます。継承元の Person* に戻してもきちんと Lolita のデストラクタが呼び出されるのは、デストラクタが virtual になっているためです。

カテゴリー: C++ パーマリンク