《深度探索C++对象模型》(二)C++,new,delete,构造/析构,临时对象
来源:互联网 发布:花生壳 80端口 编辑:程序博客网 时间:2024/06/06 13:15
欢迎查看系列博客:
《深度探索C++对象模型》(一)对象模型、存储形式;默认构造函数一定会构造么
《深度探索C++对象模型》(二)C++,new,delete,构造/析构,临时对象(本篇)
《深度探索C++对象模型》(三)构造函数、拷贝构造和初始化列表
--------------------------------------------------------------------------------------------------------------
即使父类的析构函数设置为虚函数,那么当父类指针指向子类对象的时候,也有不能够正确析构的情况。比如
Point* ptr = new Point3d[10];
六、执行期语意学
本章三个知识点比较重要:
1. 对象的构造和析构
2. new和delete运算符
3. 临时变量。
第一、构造和析构函数
---》C++支持栈上的对象,所以栈上的变量的constructor和destructor的安插就要注意了,比如swith-case语句,在每个case的后面都要进行constructor或者destructor
---》全局变量,局部静态变量和对象数组。(p250)
对象数组的初始化。例如后面的声明 Point knots[10];如果Point没有constructor和destructor,我们只需要在内存开辟10*Point个大小的空间即可,如果Point有constructor或者destructor那么,这个cosntructor和destructor必须轮流实施与每一个元素之上,这个工作是由runtime library函数完成。cfront中使用vec_new()函数产生出以class object构造而成的数组,下面是vec_new的原型:
void* vec_new ( void *array, //数组起始位置 size_t elem_size, //class object的大小 int elem_count, //数组中元素个数 void (*constructor)(void*), //构造函数指针 void (*destructor)(void*,char) //析构函数指针 )
那么,针对下面代码
Point knots[10]
的调用如下
vec_new(&knots, sizeof(Point), 10, &Point::Point,0)下面的定义,带有一部分初始化,结果就是 前三个元素使用Point的构造函数,后面7个仍然使用ver_new
Point knots[10]={Point (),Point(1.0,1.0,0.5),-1.0}---》如果一个class的构造函数带有默认参数,怎么办。p(252)
例如:class complex{ complex(double=0.0,double =0.0) } ,怎么生命 complex的对象数组。
这种情况下,编译器的做法是:最终还是要调用vec_new这个函数,但是编译器会生成一个sub constructor来调用带参数的构造函数。
第二、new 和 delete 运算符。
---》都是从c语言的malloc和free为标准完成的。p(254)
---》如果int* p_array = new int [ 5 ];vec_new函数不会被调用,反而是new运算符会被调用:p(257)
int* p_array = (int*) __new(5*sizeof(int));如果下面的代码:
struct simple_aggr{float f1,f2;};simple_aggr *p_aggr = new simple_aggr[5]
第一种情况:vec_new()也不会被调用,因为simple_aggr没有定义一个构造函数和析构函数,所以这个操作只是单纯地获得内存和释放内存而已。让new运算符来完成就足够了。
第二种情况:如果simple_aggr定义了构造函数vec_new还是会被调用,但是这次vec_new是返回堆上的空间,vec_new根据第一个参数来判断是从栈上分配还是从堆上分配。
总结:程序员使用了new运算符,可能底层根本没用到__new运算符,而是vec_new,也许这就是new存在的意义吧。
---》delete[ ]运算符p(259)
[ ]存在的意义就是告诉编译器,去寻找数组的维度。编译器如何去计算维度?解决之道就是在vec_new()所传回的每一个内存区块配置一个额外的word,然后把元素数目藏在这个word之中。
---》下面列出vec_new的源文件p(261)
PV __vec_new(PV ptr_array,int elem_count, int size, PV constructor){ //如果ptr_array是0,从堆中配置数组 //如果不是0,表示程序员写的是 T array[count] 或new (ptr_array)T[10]; int alloc = 0;//我们要在vec_new中配置吗 int array_sz = elem_count*size; if(alloc = ptr_array ==0) ptr_array = PV(new char[array_sz]);//全局运算符new //在exception handling之下,将丢出exception bad_alloc if(ptr_array == 0) return 0; //把数组元素数目放到cache中 int status = __insert_new_array(ptr_array,elem_count); if(status == -1) { //在exception handling 之下将丢出exception,将丢出exception bad_alloc if(alloc) delete ptr_array; return 0; } if(construct) { register char* elem = (char*)ptr_array; register char* lim = elem+array_sz; //PF是一个typedef,掉膘一个函数指针 register PF fp = PF(constructor); while(elem<lim) { //通过fp调用constructor作用域“this”元素上(由elem指出) (*fp)((void*)elem); //前进下一个元素 elem += size; } } return PV(ptr_array);}--->vec_delete()的操作差不多,但是行为并不总是C++程序员所预期或需求的。例如下面的情况p(262)
class Point{ class Point3d:public Point {public: public: Point(); Point3d(); virtual ~Point(); virtual ~Point3d();} }如果执行下面的程序。
//这完全不是个好主意。Point* ptr = new Point3d[10];//这并不是我们想要的,只有Point::~Point会被调用。delete [] ptr;那么语言层面很难解决,只能程序员自己通过遍历解决了
for( int ix =0;ix<elem_count;++ix){ Point3d* p = &((Point3d*)ptr)[ix]; delete p;}---》Placement Opertator new
有一个预选定义好的new的重载placement Operator new,调用方法如下:
Point2w *ptw = new(arena) Point2w;
其中arena是指向内存中的一个区块,用于放置新产生出来的Point2w。具体详细还是看书吧,很难懂。
第三、临时对象
---》临时性对象,是神秘的,诡异的。而且临时性对象的产生与否部分情况取决于编译器的优化。
(0) T a, b, d;
(1) T c = a+b; //可能不会产生临时性对象
(2) d = a+b; //可能产生
(3) a+b; //必须有临时对象产生。
---》临时性对象的生命周期
比如:a和b都是string类型变量,printf("%s\n",a+b);这句话要产生一个string 类型的临时变量。那么临时对象的生命期是什么呢?在Standard C++之前临时对象的生命期并没有明确的指定,而是有编译厂商自行决定的。
C++ standard之后规定:临时对象在完整表达式尚未评估完全之前不得摧毁。
---》临时对象的生命周期意外情况
1.表达式被用来初始化一个object,例如: progName + progVersion都是String类型
bool verbose;String progNameVersion = !verbose ? 0 : progName+progVersion;
假如verbose =ture ,三目运算法结束后,将临时对象(temp)销毁,那么progNameVersion将得不到正确的值,所以这种情况必须等到object初始化完成之后再销毁temp。
即便是这样,有些情况还是不能保证程序的正确:
const char *NameVersionChar = progName + progVersion;//经过编译器之后(C++ pseudo code)String temp;operator+(temp, progName, progVersion);NameVersionChar = temp.String::opertor char*();temp.String::~String();
此时NameVersionChar指向定义的堆内存
</pre><p></p><p>2.当一个临时性对象被一个reference绑定。例如:</p><p><pre name="code" class="cpp">const String &space = " ";//那么编译器产生下面的代码(C++ pseudo Code)String temp;temp.String::String(" ");const String &space = temp;
很明显,如果这时候temp被析构,那么reerence 将无用了,所以:如果一个临时对象被绑定与一个reference,对象将残留,直到被初始化之reference的生命结束或者知道临时对象的生命范畴(scope)结束在析构,销毁。
---》临时对象的产生和销毁是否影响效率
(笔者还没有完全理解原书中的意思,还需要时间来理解)
- 《深度探索C++对象模型》(二)C++,new,delete,构造/析构,临时对象
- 【C++】深度探索C++对象模型之构造、析构、拷贝语意学
- 深度探索C++对象模型
- 《深度探索C++对象模型》:构造函数
- 关于new和delete的编译器的内部实现---来自深度探索C++对象模型
- 深度探索c++对象模型之new和delete运算符介绍
- 深度探索c++对象模型之临时对象的传说
- 【C++】深度探索C++对象模型之类存储
- 【C++】深度探索C++对象模型之Function语意学
- 《深度探索C++对象模型》读书笔记[二]
- 【C++】深度探索C++对象模型之站在对象模型的顶端
- 深入探索C++对象象模型--拷贝构造函数、对象模型
- C++对象构造,析构,new,delete分析
- 深度探索C++对象模型第二章 构造函数语义学
- 深度探索C++对象模型 之 构造函数语意学
- 深度探索C++对象模型 2构造函数语意学
- 《深度探索C++对象模型》--2 构造函数语意学
- 深度探索C++对象模型--构造函数语义学
- 优先队列(UVAL-3135)
- fedora 16 ftp ntf配置
- windows下编译配置x264
- 对话框 QDialog
- Activity与Service通信的方式有三种:
- 《深度探索C++对象模型》(二)C++,new,delete,构造/析构,临时对象
- linux下使用vim+ctags+taglist替代windows下的sourceinsight
- 从排序的旋转数组中找原开始元素
- solaris挂载块文件为文件系统
- 搜索的未来 谷歌胜算几何
- Android Activity和Service之间的通信
- win7下安装linux双系统故障排除
- <<<<<<<<<用来存代码哒!!!!>>>>>>>>>>>>
- B(UVA-11997)k个最小和