如果class没有定义destructor,那么只有在class内置的member object(或者class的base class)拥有destructor的情况下,编译器才会合成出一个。否则,destructor将会被视为不需要,自然也就不需要被合成(更不需要被调用)。
不定义destructor反而具备某种功效性,我们不应当认为某一个class具备constructor就也应当具备destructor,实际上,我们应当根据“需要”而非“感觉”来提供destructor。
为了决定class是否需要一个程序层面的destructor(或constructor),开发者必须明确一个class object的生命在哪里结束(或开始),需要何种操作才能保证对象的完整性,这也是constructor和destructor何时发生作用的关键。举例而言,1
2
3
4
5
6
7{
Point pt;
Point *p = new Point3d;
foo(&pt,p);
...
delete p;
}
我们看到,pt和p在作为foo()的参数之前,都必须先初始化为某些坐标值。这个时候需要有一个constructor,一般而言,class的使用者无法检验一个local变量或者heap变量是否已经被初始化。从这个角度来看,一个constructor的存在至少令开发者得以确认object必然已被初始化。
再来关注delete语句。当我们在delete掉p后,有任何程序上必须要处理的吗?答案是否定的,因为在本实例中我们无需归还任何资源,因此,我们无需一个destructor。
接着考虑Vertex class,它维护了一个由紧邻的“顶点”所形成的链表,并且当一个顶点的生命结束时,在链表上来回移动以完成删除操作,因此,Vertex destructor需要一个destructor。
当我们从Point3d和Vertex派生出Vertex3d时,如果我们不提供explicit destructor但仍然希望Vertex destructor被调用,因此,编译器必须合成一个Vertex3d destructor,其唯一任务就是调用Vertex destructor。如果我们提供一个Vertex3d destructor,编译器会扩展它,使他调用Vertex destructor。一个由开发者定义的destructor被扩展的方式类似于constructors,但是顺序相反:
- destructor函数本身首先被执行
- 如果class拥有member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。
- 如果class内带一个vptr,则现在将会被重新设定,指向适当的vtbl(当前object已经退化)。
- 如果上层nonvirtual base class拥有destructor,它们会以其声明顺序的相反顺序被调用。
- 如果任何virtual base class拥有destructor,而当前讨论的class是最尾端class(most derived),那么它们会以其原来的构造顺序的相反顺序被调用。
类似于constructor,对于destructor的最佳实现策略就是维护两份destructor实体:
- 一个complete object实体,总是设定好vptr,并且调用virtual base class destrcutors
- 一个base class subobject实体,除非在destructor函数中调用一个virtual function,否则它绝不会调用virtual base class destructors并设定vptr。
我们可以认为,每一次destructor的调用都使当前的object退化为另一个object,它们在destructor之外的任何地方都是完整的。