首页 > 代码库 > 执行期语意学
执行期语意学
执行期语义主要从一下两个方面展开:
1 执行期发生的一些转换。
2 临时性对象。
执行期发生的转换
一 对象的构造和解构(构造和析构必须调用时)
1 一般而言,constructor和destructor的安插都如你锁预期。对象定义时构造函数被调用,初始化该对象;区段结束(离开点)时,destructor被调用。
2 如果一个区段(以{}括起来的区域)或函数中有一个以上的离开点,destructor必须被放在每一个离开点。
3 一般而言,我们应该把object尽可能放置在使用它的那个程序区段附近,这样做可以节省不必要的对象产生操作和摧毁操作。
全局对象
我们有如下程序片段:
Matrix identity;
main()
{
Matrix m1=identity;
return 0;
}
main()
{
Matrix m1=identity;
return 0;
}
1 c++保证,一定会在main()函数中第一次调用identity之前,把identity构造出来,而在main()结束之前把identity摧毁掉。 我们说全局identity需要静态的初始化操作和内存释放操作。
2 静态初始化的原因?
在c语言中一个全局对象只能被一个常量表达式(可在编译时期求其值的那种)设定初值。而constructor并不是常量表达式。
因此,虽然class object在编译时期可以放置在data segment并且内容为0(c++会这样做,而c这不处理),但constructor一直到程序激活(startup)时才会实施,必须对一个“放置在program data segment 中的object的初始化表达式”做评估。
3 cfront的实现策略是munch策略。会产生_sti()和_std()函数,以及一组运行时库,一个_main()函数,一个_exit()函数。
局部静态对象
我们有如下程序片段
const Matrix & identity(){
static Matrix mat_identity;
return mat_identity;
}
static Matrix mat_identity;
return mat_identity;
}
local static class object保证了如下意义:
1 mat_identity的constructor必须只能执行一次,虽然上述函数可能会被调用多次。
2 mat_identity的destructor必须只能执行一次,虽然上述函数可能会被调用多次。
3 实现方法:1> 无条件地在程序起始(startup)时构造出对象来。
2> 导入一个临时对象保护mat_identity的初始化操作。第一次处理时该对象评估为false。然后constructor会调用,然后被改为true。
对象数组
我们有下列数组定义:
Point knots[10];
1 如果Point既没有顶一个constructor也没有定义一个destructor,我们只需配置足够的内存以存储10个连续的Point元素。
2 如果Point的确定义了一个default constructor,所以这个constructor必须轮流实施于每个元素之上。一般这是经由一个或多个运行时库达成的。如cfront中的vec_new();
3 如果Point也定义了一个destructor,当knots的声明结束时,该destructor也必须实施于那10个元素身上。运行时库可能是vec_delete()。
default constructor和数组
1 vec_new()取一个default constructor的地址,激活constructor,然而这样将无法(不能允许)存取default argument values。
2 cfront所采用的方法是产生一个内部的sub construtor,没有参数。在其函数内调用由程序员提供的constructor,并将default 参数值明确地指定过去。
new和delete运算符
int *pi=new int(5);
1 new调用其实有两个步骤来完成的
1> int *pi=__new(sizeof(int)); //通过适当的new运算符实体配置内存;
2> *pi=5;//然后设置初值。
注意:初始化操作应该在内存配置成功后才能执行。
2 delete的情况类似
delete pi;//可能转化成一下步骤
if(pi!=0)
__delete(pi);
以constructor来配置一个class object,情况类似。例如:
Point3d *origin=new Point3d;//被转化为
3 Point3d *origin;
if(origin=__new(sizeof(Point3d)))
Point3d::Point3d(origin);
4 destructor的应用类似
delete origin;//被转化为
if(orgin!=0){
Point3d::~Point3d(origin);
__delete(orgin);
}
针对数组的new语意
1 int *p_array=new int[5];
vec_new()不会调用,因为它的主要功能是把default constructor施行于class object所组成数组的每一个元素身上。不过new运算符函数会被调用。
2 //struct simple{int i1,i2;};
simple *p_aggr=new simple_aggr[5];
vec_new也不会被调用。因为:simple并没有定义一个constructor和destructor,所以配置数组以及清除p_aggr数组的操作,只是单纯地获取内存和释放内存而已。
3 如果class定义有一个default constructor,某些版本的vec_new()就会被调用,配置并构造class objectes所组成的数组。
Point3d *p_array=new Point3d[10];//通常会被编译为
Point3d *p_array;
p_array=vec_new(0,sizeof(Point3d),10,&Point3d::Point3d,&Point3d::~Point3d);
临时性对象
理论上,c++标准允许编译器厂商对是否产生临时性对象有完全的自由度。但实际上,由于市场竞争,几乎保证任何表达式如果有这种形式:
1 T c=a+b;
加法定义为:T operator+(const T &,const T &); 或T T::operator(const T&);
实现根本不会产生一个临时对象。
注:1> 直接以拷贝构造的方式,将a+b的值放到c中。
2> 视operator的定义而定,NRV优化也可能实施起来,这将导致直接在上述c对象中求表达式结果,避免执行copy constructor和具名对象的构造和析构。
2 然而,意义相当的赋值叙述句:
2 然而,意义相当的赋值叙述句:
c=a+b;
不能忽略临时对象,相反,他会导致下面的结果:
//c++伪码
T temp;
temp.operator+(a,b);
c.operator=(temp);
temp.T::~T();
3 没有出现目标对象:a+b;
这时有必要产生一个临时对象,以放置运算后的结果。然后其析构有点复杂:
C++标准上这么规定:
临时性对象的被摧毁,应该是对完整表达式求值过程中的最后一个步骤。该完整表达式造成临时性对象的产生。完整表达式就是被涵括的表达式最外围那个。
4 临时性对象声明规则有两个例外:
1> 表达式被用来初始化一个object时:凡含有表达式执行结果的临时对象,应该存留到object的初始化操作完成为止。
2> 如果一个临时对象被绑定与一个reference,对象将残留,直到被初始化之reference的生命结束,或直到临时对象的声明范围结束(视哪一种情况先到而定)。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。