分享
 
 
 

深度探索C++对象模型(9)

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

介绍

当编译一个C++程序时,计算机的内存被分成了4个区域,一个包括程序的代码,一个包括所有的全局变量,一个是堆栈,还有一个是堆(heap),我们称堆是自由的内存区域,我们可以通过new和delete把对象放在这个区域。你可以在任何地方分配和释放自由存储区。但是要注重因为分配在堆中的对象没有作用域的限制,因此一旦new了它,必须delete它,否则程序将崩溃,这便是内存泄漏。(C#已经通过内存托管解决了这一令人头疼的问题)。C++通过new来分配内存,new的参数是一个表达式,该表达式返回需要分配的内存字节数,这是我以前把握的关于new的知识,下面看看通过这本书,使我们能够更进一步的了解到些什么。

这一章主要是说Runtime Semantics执行期语义学。

这是我们平时写的程序片段:

Matrix identity; //一个全局对象

Main()

{

Matrix m1=identity;

……

return 0;

}

很常见的一个代码片段,雷神从来没有考虑过identity如何被构造,或者如何被销毁。因为它肯定在Matrix m1=identity之前就被构造出来了,并且在main函数结束前被销毁了。我们不用考虑这些问题,好象C++就应该这样。但这本书是研究C++底层机制的。既然我们在看这本书,说明我们希望了解C++的编译器又做了那些大量的工作,使得我们可以这样使用对象。

在C++程序中所有的全局对象都被放在data segment中,假如明确赋值,则对象以该值为初值,否则所配置到内存内容为0。也就是说,假如我们有以下定义

Int v1=1024;

Int v2;

则v1和v2都被配置于data segment,v1值为1024,v2值为0。(雷神在VC6环境用MFC编程时中发现假如int v2;v2的值不为0,而是-8,不知为什么?编译器造成的?)。

假如有一个全局对象,并且这个对象有构造函数和析构函数的话,它需要静态的初始化操作和内存释放工作,C++是一种跨平台的编程语言,因此它的编译器需要一种可以移植的静态初始化和内存释放的方法。下面便是它的策略。

1、为每一个需要静态初始化的档案产生一个_sit()函数,内带构造函数或内联的扩展。

2、为每一个需要静态的内存释放操作的文件中,产生一个_std()函数,内带析构函数或内联的扩展。

3、提供一个_main()函数,用来调用所有的_sti()函数,还有一个exit()函数调用所有的_std()函数。

侯先生说:

Sit可以理解成static initialization的缩写。

Std可以理解成static deallocation的缩写。

那么main函数会被编译器变成这样:

Matrix identity; //一个全局对象

Main()

{

_main();//对所有的全局对象做static initialization动作。

Matrix m1=identity;

……

exit();//对所有的全局对象做static deallocation动作。

}

其中_main()会有一个对identity对象的静态初始化的_sti函数,象下面伪码这样:

// matrix_c是文件名编码_identity表示静态对象,这样能够保证向执行文件提供唯一的识别符号

_sti__matrix_c_identity()

{

identity.Matrix:: Matrix(); //这就是静态初始化

}

相应的在exit()函数也会有一个_std_matrix_c_identity(),来进行static deallocation动作。

但是被静态初始化的对象有一些缺点,在使用异常时,对象不能被放置在try区段内。还有对象的相依顺序引出的复杂度,因此不建议使用需要静态初始化的全局对象。

局部静态对象在C++底层机制是如何构造和在内存中销毁的呢?

1、导入一个临时对象用来保护局部静态对象的初始化操作。

2、第一次处理时,临时对象为false,于是构造函数被调用,然后临时对象被改为true.

3、临时对象的true或者false便成为了判定对象是否被构造的标准。

4、根据判定的结果决定对象的析构函数是否执行。

假如一个类定义了构造函数或者析构函数,则当你定义了一个对象数组时,编译器会通过运行库将你的定义进行加工,例如:

point knots[10]; //我们的定义

vec_new(&knots,sizeof(point),10,&point::point,0); //编译器调用vec_new()操作。

下面给出vec_new()原型,不同的编译器会有差别。

void * vec_new(

void *array, //数组的起始地址

size_t elem_size, //每个对象的大小

int elem_count, //数组元素个数

void(*constrUCtor)(void*),

void(*destructor)(void* ,char)

)

对于明显获得初值的元素,vec_new()不再有必要,例如:

point knots[10]={

Point(), //knots[0]

Point(1.0,1.0,0.5), //knots[1]

-1.0 //knots[2]

};

会被编译器转换成:

//C++伪码

Point::Point(&knots[0]);

Point::Point(&knots[1],1.0,1.0,0.5);

Point::Point(&knots[2],-1.0,0.0,0.0);

vec_new(&knots,sizeof(point),10,&point::point,0); //剩下的元素,编译器调用vec_new()操作。

怎么样,很神奇吧。

当编译一个C++程序时,计算机的内存被分成了4个区域,一个包括程序的代码,一个包括所有的全局变量,一个是堆栈,还有一个是堆(heap),我们称堆是自由的内存区域,我们可以通过new和delete把对象放在这个区域。你可以在任何地方分配和释放自由存储区。但是要注重因为分配在堆中的对象没有作用域的限制,因此一旦new了它,必须delete它,否则程序将崩溃,这便是内存泄漏。(C#已经通过内存托管解决了这一令人头疼的问题)。C++通过new来分配内存,new的参数是一个表达式,该表达式返回需要分配的内存字节数,这是我以前把握的关于new的知识,下面看看通过这本书,使我们能够更进一步的了解到些什么。

Point3d *origin=new Point3d; //我们new 了一个Point3d对象

编译器开始工作,上面的一行代码被转换成为下面的伪码:

Point3d * origin;

If(origin=_new(sizeof(Point3d)))

{

try{

origin=Point3d::Point3d(origin);

}

catch(…){

_delete(origin);

throw;

}

}

而delete origin;

会被转换成(雷神将书上的代码改为exception handling情况):

if(origin!=0){

try{

Point3d::~Point3d(origin);

_delete(origin);

catch(…){

_delete(origin); //不知对否?

throw;

}

}

一般来说对于new的操作都直截了当,但语言要求每一次对new的调用都必须传回一个唯一的指针,解决这个问题的办法是,传回一个指针指向一个默认为size=1的内存区块,实际上是以标准的C的malloc()来完成。同样delete也是由标准C的free()来完成。原来如此。

最后这篇笔记再说说临时对象的问题。

T operator+(const T&,const T&); //假如我们有一个函数

T a,b,c; //以及三个对象:

c=a+b;

//可能会导致临时对象产生。用来放置a+b的返回值。然后再由 T的copy constructor把临时对象当作c的初值。也有可能直接由拷贝构造将a+b的值放到c中,这时便不需要临时对象。另外还有一种可能通过操作符的重载定义,经named return value优化也可以获得c对象。这三种方法结果一样,区别在于初始化的成本。对临时对象书上有很好的总结:

在某些环境下,有processor产生的临时对象是有必要的,也是比较方便的,这样的临时对象由编译器决定。

临时对象的销毁应该是对完整表达式求值过程的最后一个步骤。

因为临时对象是根据执行期语义有条件的产生,因此它的生命规则就显得很复杂。C++标准要求凡含有表达式执行结果的临时对象,应该保留到对象的初始化操作完成为止。当然这样也会有例外,当一个临时对象被一个引用绑定时,对象将残留,直到被初始化的引用的生命结束,或者超出临时对象的作用域。

好了今天很有收获,马上就会结束这本书的学习了。下一章的标题 站在对象模型的尖端 我有些迫不及待了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有