17、对象的构造和析构

目录

全局对象(Global Object)

局部静态对象(Local Static Object)

对象数组


全局对象(Global Object)

如果我们有以下的程序片段:

Matrix identity;
int main()
{
	//identity 必须在此处被初始化
	Matrix m1 = identity;
	...
	return 0;
}

C++保证,一定会在main函数中第一次用到identity之前,把identity构造出来,而在main()函数结束之前把identity摧毁掉。像identity这样所谓global object如果有constructor和destructor的话,我们说它需要静态的初始化操作和内存释放操作。

C++程序中所有的global objects都被放置在程序的data segment中。如果显示指定给它一个值,此object将以该值为初值。否则object所分配到的内存内容为0。因此在下面这段代码中:

int v1 = 1024;

int v2;

v1和v2都被配置于程序的data segment,v1值为1024,v2值为0(这和C略有不同,C并不自动设定初值)。在C语言中一个global object只能够被一个常量表达式(可在编译时期求值的那种)设定值。当然,constructor并不是常量表达式。虽然class object在编译时期可以被放置于data segment中并且内容为0,但是constructor一直要到程序启动(startup)时才会实施。必须对一个“放置于program data segment中的object的初始化表达式”做评估(evaluate),这正是为什么一个object需要静态初始化的原因。

当cfront还是唯一的C++编译器,而且跨平台移植性比效率的考虑更重要的时候,有一个可移植但成本颇高的静态初始化(以及内存释放)方法,我把它称作munch。下面是munch策略:

1)为每一个需要静态初始化的文件产生一个_sti()函数,内含必要的constructor调用操作。例如前面所说的identify对象在matrix.c中产生出下面的_sti()函数(译注:我想sti就是static initialization的缩写):

__sti__matrix_c__identity(){
	//C++ 伪代码
	identity.Matrix::Matrix();//译注:这就是static initialization
}

其中matrix_c是文件名编码,_identity表示文件中所定义的第一个static object。在__sti之后附加上这两个名称,可以为可执行文件提供一个独一无二的标志符号。

2)类似情况,在每一个需要静态的内存释放操作(static dealllocation)的文件中,产生一个__std()函数(译注:我想std就是static deallocation的缩写),内含必要的destructor调用操作,或是其inline expansions。在我们的例子中会有一个__std()函数被产生出来,针对identify对象调用Matrix destructor。

3)提供一组runtime library “munch”函数:一个_main()函数(用以调用可执行文件中的所有__sti()函数),以及一个exit()函数(以类似的方式调用所有的__std()函数)。

局部静态对象(Local Static Object)

假设我们有以下程序片段:

const Matrix&
identity()
{
	static Matrix mat_identify;
	//...
	return mat_identify;
}

Local static class object保证了什么样的语义?

  • mat_identify的constructor必须只能实施一次,虽然上述函数可能会被调用多次。
  • mat_identify的destructor必须只能实行一次,虽然上述函数可能会被调用多次。

编译器的策略之一就是,无条件地在程序起始(startup)时构造出对象来。然而这会导致所有的local static objects都在程序起始时初始化,即使它们所在的那个函数从不曾被调用过。因此,只在identify()被调用时才把mat_identify构造起来,是比较好的做法。我们应该怎么做呢?

首先,导入一个临时性对象以保护mat_identity的初始化操作。第一次处理identity()时候,这个临时对象被评估成false,于是constructor会被调用,然后临时对象被改成true。这样解决了构造的问题。相反的另一端,destructor也需要施加到mat_identity身上,但只有在mat_identity已经构造出来才算数。如果那个临时对象是true,就表示构造好了。下面是cfront的输出。

//被产生出来的临时对象,作为戒护作用
static struct Matrix *__0__F3 = 0;

//C++的reference在C中是以pointer来代替的
//identity() 的名称被mangled

struct Matrix*
identity__Fv()
{
	static struct Matrix __lmat_identity;
	
	//如果临时性的对象已被设立,那就什么也别做。否则
	//(a)调用constructor:__ct__6MatrixFv;
	//(b)设定保护对象,使它指向目标对象
	__0__F3
		?0
		:(__ct__6MatrixFv( &__lmat_identity),
		(__0__f3 = (&__lmat_identity)));
}

最后,destructor必须在“与text program file有关联的静态内存释放函数(static deallocation function)”中被无条件的调用:

char __std__stat_0_c_j()
{
	__0__F3
		? __dt__6MatrixFv(__0__F3, 2)
		:0;
	...
}

对象数组

假设我们有以下的数组定义:

Point knots[10];

什么东西需要完成?如果Point既没有定义constructor也没有定义一个destructor,那么我们的工作不会比建立一个“build-in类型所组成的数组”更多,也就是说我们只要配置足够内存以存储10个连续的Point元素即可。

然而Point的确定义了一个default destructor,所以这个destructor必须轮流实行于每个元素之上。一般而言这是经由一个或者多个runtime library函数达成的。在cfront中,我们使用一个被命名程vec_new()的函数,产生出以class objects构造而成的数组。比较新的编译器,包括Microsoft和Sun,则提供两个函数,一个用来处理“没有virtual base class”的class,另外一个用来处理“virtual base class”的class,后一个函数通常被称为vec_vnew()。函数原型如下:

void *
vec_new(
	void *array,                 //数组起始地址
	size_t elem_size,            //每个class object的大小
	int elem_count,              //数组中的元素个数
	void (*constructor)(void *),
	void (*destructor)(void *),
)

其中的constructor和destructor参数是这一class之default constructor和default destructor的函数指针。参数array持有的若不是具名数组(本例为knots)的地址,就是0。如果是0,那么数组将由应用程序的new运算符,被动态配置于heap中。Sun把“由class objects所组成的具名数组”和“动态配置而来的数组”的处理操作分成两个library函数:_vector_new2和_vector_con,他们各自拥有一个virtual base class函数实现。

在vec_new()中,constructor实行于elem_count个元素之上。对于支持exception handling的编译器而言,destructor的提供是必要的。下面是编译器可能针对我们10个Point元素所做的vec_new()调用操作:

Point knots[10];

vec_new( &knots, sizeof(Point), 10, &Point::Point, 0);

如果程序员提供一个或多个明显初值给一个由class objetcs组成的数组,像下面这样,会如何:

Point knots[10] = {
	Point(),
	Point(1.0,1.0,0.5),
	-1.0
};

对于那些明显获得初值的元素,vec_new()不在必要。上面的定义可能转换为下面的代码。

Point knots[10];

//C++伪代码

//显示初始化前3个元素
Point::Point(&knots[0]);
Point::Point(&knots[1],1.0,1.0,0.5);
Point::Point(&knots[2],-1.0,0,0);

//以vec_new初始化后7个元素
vec_new(&knots[0]+3, sizeof(Point), 7, &Point::Point, 0);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值