第六章:执行期语义学:对象的构造和析构

 

 

一、全局变量

  全局变量要求在第一次执行前就被准备好,在程序结束时被销毁。因为构造函数在执行期才能实施,所以需要对一个全局对象做静态初始化。如下面代码所示,后三行的对象没法在编译器完成初始化(都是非常量表达式),所以需要静态初始化并且也需要内存静态释放操作(静态意思是看起来像在程序执行前构造或者在程序结束后释放内存,主程序开始时执行一次初始化,主程序结束时执行一次释放,主程序执行期间不执行)

extern int i;
//下面全部要求静态初始化
int j=i;
int *pi=new int(i);
double sal=compute_sal(get_employee(i));

  1.munch策略:

  ①为每一个需要静态初始化的文件产生一个_sti()函数,内含必要的构造函数调用操作或内联扩张。

  ②在每一个需要静态的内存释放操作的文件中产生一个_std()函数,内含必要的析构调用操作或是其内联扩张,

  ③提供一组运行时库的munch函数:一个_main()函数,用以调用可执行文件中的所有_sti()函数,以及一个exit()函数(用以调用所有_std()函数)

  其中_main()被作为主程序的第一个指令,exit()作为主程序的最后一个指令。如下所示

int main()
{
    _main();
    ...
    exit();
}

  2.缺点:需要静态初始化的全局变量不能放在异常处理的try语句中,因为不能够静态释放。

 

二、局部静态变量

  局部静态变量要求在第一次调用的时候构造出来并且只能调用一次构造函数,同时析构函数也只能被施行一次

  解决方法是设置一个全局的对象,在第一次处理函数时被设为false,于是局部静态变量的构造函数会被调用,然后临时对象改为true。局部静态变量的销毁则是在静态内存释放函数中判断临时对象的状态来决定是否调用局部静态变量的拷贝构造函数(现在C++标准要求局部静态变量的销毁也要与构造顺序相反,所以可能需要一个执行期链表来记录构造顺序)

 

三、对象数组

  编译器处理对象数组会合成两个函数vec_new(...)和vec_delete(...),其中new用来构造数组delete用来析构数组。有的编译器还会针对含有虚基类对象的数组产生一个另外的函数vec_vnew和vec_vdelete。

void* vec_new(
void *array,      //数组起始地址(部分元素需要调用时可以是数组起始地址加一个偏移量)
size_t elem_size,      //每一个类对象的大小
int elem_count,         //数组中元素个数
void (*constructor)(void*),       //意思是经由指针存取构造函数的地址(这显然违背了C++的语言规则,但是汇编层面确实可以做到这一点)   
void (*destructor)(void*,char)   //析构函数的地址
}

void *vec_delete(
void *array,          //数组起始地址
size_t elem_size,         //每一个类对象的大小
int elem_count,           //数组中元素的个数
void (*destructor)(void*,char)
}

  这样处理数组的构造依然还有问题,就是无法使用构造函数的默认参数值,因为通过指针调用函数当然不能得到函数的默认参数。所以编译器又设计了一个新的内部构造函数,他没有参数,会在内部调用数组元素的构造函数并传递默认参数值。而上面的vec_new的构造函数指针则指向这个内部构造函数

complex::complex(double=1.0,double=1.0);
vec_new(&c_array,sizeof(complex),10,&complex::complex,0);
complex::complex() { complex::complex(
0.0, 0.0); }
posted @ 2021-06-29 12:12  放不下的小女孩  阅读(66)  评论(0)    收藏  举报