关于C++:在”删除此”时缺少虚析构函数

Lack of virtual destructor when doing “delete this”

C++FAQ Lite的第16.15节讨论EDCOX1×0,然后提到:

Naturally the usual caveats apply in cases where your this pointer is
a pointer to a base class when you don't have a virtual destructor.

为什么是这样?考虑此代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ISuicidal {
public:
    virtual void suicide() = 0;
};

class MyKlass : public ISuicidal {
public:
    MyKlass() {
        cerr <<"MyKlass constructor
"
;
    }

    ~MyKlass() {
        cerr <<"MyKlass destructor
"
;
    }

    void suicide() {
        delete this;
    }
};

因此使用:

1
2
3
4
5
6
int main()
{
    ISuicidal* p = new MyKlass;
    p->suicide();
    return 0;
}

在调用p->suicide()中,尽管ISuicidal没有虚拟析构函数,但仍按预期调用MyKlass的析构函数。

对我来说这是有意义的,因为在MyKlass::suicide中,this的静态类型已知为MyKlass*,所以调用了正确的析构函数。这很容易通过将typeid调用放在suicide中进行验证。

那么,常见问题条目是不准确,还是我误解了?


在你的功能中,suicide(),。你用的是delete this;。这里,this指针对应于myklass类,因为函数是在myklass中定义的,而不是isoudicial,因此调用myklass的析构函数。

如果您已经在isoidicial中定义了函数,那么它不会调用myklass的析构函数,除非您在isoidical中声明了一个虚拟析构函数。


你是误会。在isoidicial中实现自杀函数(即delete this),您会发现当此指针是调用delete的基类时,它不会调用派生类的析构函数。


class Child : public MyKlass {
~Child () {}
};

ISuicidal* p = new Child;

p->suicide(); // ~Child() not called !


如果在层次结构中引入从myClass派生的另一个实类(例如myClass2),则会出现问题。

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
class ISuicidal {
public:
    virtual void suicide() = 0;
};

class MyKlass : public ISuicidal {
public:
    MyKlass() {
       cerr <<"MyKlass constructor
"
;
    }

    ~MyKlass() {
        cerr <<"MyKlass destructor
"
;
    }

    void suicide() {
        delete this;
    }
};

class MyKlass2 : public MyKlass {
public:
    MyKlass2() {
        cerr <<"MyKlass2 ctr"<<std::endl;
    }

    ~MyKlass2() {
        cerr <<"MyKlass2 dtr"<<std::endl;
    }
}

int main()
{
    MyKlass* p = new MyKlass2;
    delete p; //destructor of base class called, not the destructor of MyKlass2 because
              //the destructor is not virtual
    return 0;
}

我想你误解了。当您在基类中调用delete this时,即当此指针具有指向该基类的指针类型时,就会发生此问题。

Naturally the usual caveats apply in cases where your this pointer is
a pointer to a base class when you don't have a virtual destructor.

在您的示例中,这不是指向基类的指针,而是指向派生类的指针。


只要您调用实例的确切析构函数(例如,不要调用基的析构函数),它是安全的。

因此,您可以通过为每个子类正确地实现suicide(),或者通过创建一个可由this访问的外部删除程序函数(或者管理this的生存期的任何函数)来安全地完成这一点。