アリスはvoid*がお嫌い

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
using namespace std;
 
// アリスクラス
class Alice
{
public:
    Alice() {
        cout << "in constractor" << endl;
    }
    ~Alice() {
        cout << "in destractor" << endl;
    }
};
 
class Person
{
public:
    Person() {
        cout << "in Parson::Parson" << endl;
    }
    virtual ~Person() {
        cout << "in Parson::~Parson" << endl;
    }
};
// ロリータクラスは、Person クラスを継承する
class Lolita : public Person
{
public:
    Lolita() {
        cout << "in Lolita::Lolita" << endl;
    }
    virtual ~Lolita() {
        cout << "in Lolita::~Lolita" << endl;
    }
};
 
// アリスはvoid*がお嫌い
int main(int argc, char *argv[] )
{
    {
        cout << "自動で解放" << endl;
        Alice alice;
    }
 
    {
        cout << "自前で解放" << endl;
        Alice *alice = new Alice();
        delete alice;
    }
 
    {
        cout << "void*で解放" << endl;
        void *alice = new Alice();
        delete alice;
    }
 
    {
        cout << "継承あり" << endl;
        Lolita *lolita = new Lolita();
        delete lolita;
    }
    {
        cout << "継承あり Person" << endl;
        Person *lolita = new Lolita();
        delete lolita;
    }
    {
        cout << "継承あり void*" << endl;
        void *lolita = new Lolita();
        delete lolita;
    }
    {
        cout << "継承あり void* から Lolita へキャスト" << endl;
        void *lolita = new Lolita();
        delete (Lolita*)lolita;
    }
    {
        cout << "継承あり void* から Person へキャスト" << 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++ パーマリンク