5.5 析构语意学(Semantics of Destruction)

如果class没有定义destructor,那么只有在class内含的member object(或class自己的base class)有拥有destructor时,编译器才会自动合成一个出来。否则,destructor被视为不需要,也就不需要合成(当然更不需要调用)。

例如,Point,默认情况下并没有被编译器合成一个destructor——甚至虽然它拥有一个virtual function:

class Point
{
public:
	Point(float x = 0.0,float y = 0.0,float z = 0.0);
	Point(const Point&);
	
	virtual float z();
	//...
private:
	float _x,_y;
};

类似道理,如果我们把两个Point对象组合成一个Line class:

class Line
{
public:
	Line(const Point&,const Point&);
	//...
	
	virtual draw();
	//...
protected:
	Point _begin,_end;
};

Line也不会拥有一个合成出来的destructor,因为Point并没有destructor。

当我们从Point派生出Point3d(即使是一种虚拟继承)时,如果我们没有声明一个destructor,编译器也就没不要合成一个destructor。

不论Point还是Point3d都不需要destructor,为它们提供一个destructor反而是低效率的。我们应该拒绝对称性的想法:定义了constructor,就必须定义destructor,更不要在不确定是否需要的时候提供destructor。

为了决定class是否需要一个程序层面的constructor(或destructor),就要知道一个class object的声明的开始(或结束),例如:

{
	Point pt;
	Point* p = new Point3d;
	foo(&pt,p);
	...
	delete p;
}

pt和p在作为foo()函数的参数之前,都必须先初始化某些坐标,就需要一个constructor,否则使用者必须显示提供坐标。

当我们显示的delte掉p,是否在delete之前这么做:

p->x(0);p->y(0);

没有任何理由说delete一个对象之前得将其内容清理干净,你也不需要归还任何资源。因此不需要一个destructor。

请考虑Vertex class,它维护了一个紧邻的“顶点”所形成的链表,并且当一个顶点生命结束时,在链表上来回移动完成删除操作,这就是Vertex destructor工作。

扫描二维码关注公众号,回复: 4690949 查看本文章

当我们从Point3d和Vertex派生出Vertex3d时,如果我们不提供一个explicit Vertex3d destructor,因此编译器会合成一个Vertex3d destructor,其唯一的任务就是调用Vertex destructor。我们我们提供了一个Vertex3d destructor,编译器就会扩展它,使它调用Vertex destructor(在我们提供的程序代码之后)。一个由程序员定义的destructor被扩展的方式类似constructor被扩展的方式,但顺序相反(似乎应该是2、3、1、4、5才符合constructor的相反顺序):

  1. 如果object内含一个vptr,那么首先重设相关的vtbl。
  2. destructor的函数体被执行,vptr会在程序员的代码之前被重设。
  3. 如果class拥有member class objects,而后者拥有destructors,那么它们会以其声明顺序的相反顺序被调用。
  4. 如果任何直接的(上一层)nonvirtual base classes拥有destructor,它们会以其声明顺序的相反顺序被调用。
  5. 如果有任何的virtual base classes拥有destructor,目前这个class是最尾端(most-derived)的class,那么它们以其原来的构造的相反顺序被调用。

就像constructor一样,目前对于destructor的一种最佳的实现策略就是维护两份destructor实例:

  1. 一个complete object实例,总是设定好vptrs,并调用virtual base class destructor。
  2. 一个base class subobject实例;除非在destructor函数中调用virtual function,否则它绝对不会调用virtual base class destructor并设定vptr。

一个object声明结束期destructor开始执行之时。由于每个base class destructor被轮番调用,所以derived object实际上变成了一个完整的object。例如一个PVertex对象归还内存空间前,会依次变成一个Vertex3d对象、一个Vertex对象,一个Point3d对象,最后变成一个Point对象。当我们在destructor中调用member function时,对象对象的蜕变会因为vptr的重设(在每一个destructor中,在程序员所提供的代码执行之前)而受到影响。

猜你喜欢

转载自blog.csdn.net/weixin_28712713/article/details/84605029
5.5
今日推荐