C++对象模型:执行期语意学

目录

执行期发生的转换

一 、对象的构造和解构(构造和析构必须调用时)

全局对象

局部静态对象

default constructor和数组

二、new和delete运算符

针对数组的new语意

Placement Operator new 语意

三、临时性对象


执行期语义主要从一下两个方面展开:

  1.  执行期发生的一些转换。
  2.  临时性对象。

执行期发生的转换

一 、对象的构造和解构(构造和析构必须调用时)

  1. 一般而言,constructor和destructor的安插都如你所预期。对象定义时构造函数被调用,初始化该对象;区段结束(离开点)时,destructor被调用。
  2. 如果一个区段(以{}括起来的区域)或函数中有一个以上的离开点destructor 必须被放在 对象被生成后每一个离开点前
  3. 一般而言,我们应该把object尽可能放置在使用它的那个程序区段附近,这样做可以节省不必要的对象产生操作和摧毁操作。

全局对象

我们有如下程序片段:

Matrix identity;
main()
{
    Matrix m1=identity;
    return 0;
}
  1. c++保证,一定会在main()函数中第一次调用identity之前,把identity构造出来,而在main()结束之前把identity摧毁掉。 如果这种 global object 定义了构造与析构函数,我们说它需要静态的初始化操作和内存释放操作
  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;
}

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必须轮流实施于每个元素之上。一般这是经由一个或多个 runtime library 函数达成的。如cfront中的 vec_new();
  3. 如果Point也定义了一个destructor,当knots的声明结束时,该destructor也必须实施于那10个元素身上。runtime library 可能是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;//被转化为

Point3d *origin;

  if(origin=__new(sizeof(Point3d)))

       Point3d::Point3d(origin);

//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);

为什么vec_new中会有 destructor?在个别元素的构造过程中,如果发生 exception ,destructor 就会被传递给 vec_new,vec_new有责任在 exception 发生时将那些内存释放掉。

Placement Operator new 语意

有一个预先定义好的重载的 new 运算符,称为 Placement Operator new。它需要一个 void* 指针作为参数,指示出要生成的对象的存放的内存地址。调用如下:

Point2w *ptw = new (arena) Point2w;

arena 指向内存中的一个区块,用以放置新生成的对象。

//C++伪码
Point2w *ptw = ( Point2w* ) arena;    //指定地址
if( ptw != 0 )
    ptw->Point2w::Point2w();          //调用构造函数

析构后希望再次使用 arena 的正确方法:

p2w->~Point2w;
p2w = new ( arena ) Point2w;

如果直接使用 delete 对象被析构的同时指向的内存也会被释放,之后不能再使用了。

一般而言,placement new operator 并不支持多态,被交给 new的指针,应当适当的指向一块预先配置好的内存。

三、临时性对象

    理论上,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 然而,意义相当的赋值叙述句:

 c=a+b;

不能忽略临时对象,相反,他会导致下面的结果:

//c++伪码
T temp;
temp.operator+(a,b); 
c.operator=(temp);
temp.T::~T();

3 没有出现目标对象:a+b; 

    这时有必要产生一个临时对象,以放置运算后的结果。然后其析构有点复杂:

C++标准上这么规定:

    临时性对象的被摧毁,应该是对完整表达式求值过程中的最后一个步骤。该完整表达式造成临时性对象的产生。完整表达式就是被涵括的表达式最外围那个。例如

((objA > 1024) && (objB > 1024))
    ? objA + objB : foo(objA ,objB)

一共有五个子算式,内含在一个“?:完整表达式”中。任何一个子式所产生的任何一个临时对象,都应该在完整表达式被求值完成后,才可以销毁。

4 临时性对象声明规则有两个例外:

1> 完整表达式被用来初始化一个 object 时:凡含有表达式执行结果的临时对象,应该存留到object的初始化操作完成为止。

2> 如果一个临时对象被绑定与一个reference,对象将残留,直到被初始化之reference的生命结束,或直到临时对象的声明范围结束(视哪一种情况先到而定)。

转载自:botou 的博客园

http://www.cnblogs.com/botou0405/p/3764685.html

猜你喜欢

转载自blog.csdn.net/SimonxxSun/article/details/84996148
今日推荐