将object尽可能放置在使用它的那个程序区段附近,这样可以节省非必要的对象的构造和析构成本。少用点return,也可以减少编译后的代码量,看了这张更加深刻的理解了一些编程规范里一些要求的深意。
刚知道还能重载new操作符。。。真的c++太能玩了
第六章 执行期语意
对象的构造与析构
全局对象
已经初始化全局对象均存储在data segment(数据段),未初始化的全局变量存储在BSS(block started by symbol),C++中所有全局变量均会进行静态初始化和内存释放操作,对于显示的给定一个初始值时静态初始化过程会将其作为初值,否则会将内存内容置为0.
cfront提供一个成本较高的静态初始化方法,munch方法:(用nm命令获取所有的object file,并搜索__sti和__std来找到需要被调用的文件用于第三步)
- 为每一个需要静态初始化的文件产生一个__sti()函数(static initialization),内含必要的constructor调用操作或者inline expansions。
- 为每一个需要静态的内存释放操作产生__std()函数(static deallocation),内含必要的constructor调用操作或者inline expansions。
- 提供一组running library”munch”函数:_main()函数调用所有的__sti(),一个exit()函数调用__std()函数。
局部静态变量
局部的也要保证一次构造、一次释放,还要在使用时第一次构造而不是启动程序是全部构造,所以增加了标识符,bool类型来指定是否已经构造过。
对象数组
void vec_new( void* array, //数组的起始地址 size_t elem_size, //数组中每一个元素的大小 int elem_count, //数组中元素的个数 void (*constructor)(void*), //形参个数为0的默认构造函数指针 void (*destructor)(void*, char) //析构函数指针 ) void vec_delete( void* array, //数组的起始地址 size_t elem_size, //数组中每一个元素的大小 int elem_count, //数组中元素的个数 void (*destructor)(void*, char) //析构函数指针 }
编译器通过运行时库函数进行对数组中每个对象进行初始化、释放操作。
对于提供了部分初始化列表的对象数组:
Point a[10]={Point(),Point(1.0,1.0),-1.0}; Point::Point (&a[0]); Point::Point (&a[1],1.0,1.0); Point::Point (&a[2],1.0,0.0); vec_new(&a+3,sizeof(Point),7,Point::Point,0);
?为什么vec_new要传入析构函数的指针?难道是vec_new与vec_delete内部共同维护了一个链表,在new的时候会吧涉及到(具有)析构函数的,将函数指针存起来,等最后的时候delete直接调用这个链表中的所有的指针?
后面提到了vec_new有责任在个别数组元素构造过程中发生exception时将内存释放掉。
默认构造函数和数组
当自己定义的构造函数有参数时,构建对象数组:
Pointx b[10];此时因为vec_new只能自动调用无参的构造函数(默认构造函数),因此需要做改变,建立默认构造函数,并在初始化列表中调用自定义构造函数:
Pointx::Pointx() : Pointx(0.0,0.0) {}
此步骤会被编译器自动生成,对应的参数内容都会以内存位0的默认值作为参数。这个stub constructor是内部的,只有在objects数组真正被产生出来时,此函数实例才会被产生并使用,不会影响无初始化列表的默认构造函数。
new/delete
new通过malloc完成,delete通过free实现
对于内置类型,先创建内存后赋值:
int *pi = new int(5); //int *pi; //if(pi = __new(sizeof(int))) //??? *pi = 5; delete pi; //if(pi != 0) __delete(pi)
对于class
Point3d *origin = new Point3d; if(origin = __new(sizeof(Point3d))) { try { origin->Point3d::Point3d(origin); } catch(...) { __delete(origin) throw; } }//如果构造过程发生异常,会先释放内存然后上抛异常 delete origin; if(origin != 0) { Point3d::~Point3d(origin); __delete(origin); }
对于数组new
对于内置类型或无构造函数和析构函数的类型,用__new构建,大小为n*sizeof(type)方式构建内存。如果有构造会调用vec_new
对于delete只需要输入delete [] xx;数组大小会自动计算不用填充[],若不写[]会只释放第一个。
Placement Operator new
void* operator new(size_t, void* p) { return p; }
调用:Point2w* ptw2 = new(arena) Point2w;与Point2w* ptw1 = (Point2w*) arena; 区别
前者等于:
Point2w* ptw2 = (Point2w*) arena; ptw2->~Point2w(); if(ptw2 != 0) ptw2->Point2w::Point2w();
一般不要重载new用于多态,避免派生类大小不可控
临时性对象Temporary objectes
T c=a+b,编译器可能在a+b后将结果赋予临时对象,后再将结果复制到c(拷贝构造、赋值操作符重载)
临时对象的被摧毁,应该是对完整表达式(full-expression)求值过程中的最后一个步骤。
凡是有表达式执行结果的临时性对象,应该保存到object的初始化操作完成为止。
string proNameVersion = !verbose ? 0 : proName + progVersion;proNameVersionobjecrt初始化需要用到该临时对象,因此应该保存到object的初始化操作完成为止。
如果一个临时性对象绑定于一个reference,对象将残留,直到被初始化之reference的生命结束,或者知道临时对象的生命范畴结束,视哪种情况先到达而定。
最新评论