析构函数在什么情况下应该声明为虚函数

析构函数在什么情况下应该声明为虚函数

作者:BlogUpdater |  时间:2021-02-21 |  浏览:1310 |  评论已关闭 条评论

弄懂虚函数是人生一大乐事
如标题所言,一个C++对象的析构函数,在什么情况下,应该被声明为一个虚函数呢?
(看到这一标题,我兴趣大增。)

首先,虚析构函数是什么意思呢?Well,你想想,一个类的虚方法是什么意思,就能明白虚析构函数是怎么一回事儿了。

如果一个方法被声明为虚方法,则当调用这个方法时,会调用具体实现此方法的对象方法,也称动态绑定。
如果这个方法不是虚方法,则会调用在编译期指针确定的对象方法,这种称之为:静态绑定。

我们看看下面的例子:

对p->f()的调用会导致对Sample::f的调用,因为指针的类型是指向Sample对象的指针。虽然指针实际指向的Derivedd对象,但因为指针的类型是Sample指针,所以对于非虚方法f来说,会调用基类Sample的f方法。

另一方面,对p->vf()的调用会导致对Derived::vf的调用,因为vf为虚方法,属于会调用派生类的f。

所以,现在应该理解什么是虚方法了吧?

虚析构函数也和上面的原理一样,只是通常你不会主动调用它。实际上它是在对象离开作用域或者你删除对象时自动被执行的。如下图所示:

因为Sample并没有声明它的析构函数为虚函数,所以delete pp将调用指针本身的类型对应的对象的析构函数(Sample::~Sample),而不是派生类的析构函数(Derived::~Derived)。如你所看到的,这个行为在上面的代码场景中,不是我们想要的。

通过上面的实例,你应该可以回答标题中提出的问题。
当符合如下两个条件时,一个类必须声明它的析构函数为虚函数。
1) 你对这个类对象执行了delete。
2) 类指针可能指向一个派生类对象。

有些人可能会说了:应该这样说,只要一个对象有声明虚方法,则它就必须有一个虚析构函数。
这种说法不正确。

下面我来句一个反例:下面的类没有定义任何虚方法,但是它依然需要一个虚析构函数。

delete p将会调用Sample::~Sample,而不是Derived::~Derived,导致了智能指针指向的stream对象的泄露。

下面是另外一个例子:它定义了一个虚方法,但是它并不需要一个虚析构函数。

由于对象删除是从与实际对象类型匹配的指针类型发生的,因此将调用正确的析构函数。这种模式通常发生在COM对象中,COM对象公开了与其接口相对应的几种虚拟方法,但是对象本身是由其自己的实现而不是由基类实现的。(请注意,没有任何COM接口包含虚拟析构函数。)

知道何时使析构函数虚拟化的问题是,你必须知道人们将如何使用你的类。
如果C++有一个”sealed”关键字,则规则将更简单:如果执行”delete p”(其中p是指向未密封的类的指针),则该类需要一个虚拟的析构函数。 (假想的”sealed”关键字是用来告诉编译器:这个类可能作为另一个类的基类)

总结
无脑下一步:一般在存在继承场景的对象层次中,请记得将基类的析构函数声明为虚函数。

最后
Raymond Chen的《The Old New Thing》是我非常喜欢的博客之一,里面有很多关于Windows的小知识,对于广大Windows平台开发者来说,确实十分有帮助。
本文来自:《When should your destructor be virtual?》

标签:

评论已关闭。