进步始于交流
收货源于分享

《深度探索C++对象模型》笔记(6)

将object尽可能放置在使用它的那个程序区段附近,这样可以节省非必要的对象的构造和析构成本。少用点return,也可以减少编译后的代码量,看了这张更加深刻的理解了一些编程规范里一些要求的深意。

刚知道还能重载new操作符。。。真的c++太能玩了

第六章 执行期语意

对象的构造与析构

全局对象

已经初始化全局对象均存储在data segment(数据段),未初始化的全局变量存储在BSS(block started by symbol),C++中所有全局变量均会进行静态初始化和内存释放操作,对于显示的给定一个初始值时静态初始化过程会将其作为初值,否则会将内存内容置为0.

cfront提供一个成本较高的静态初始化方法,munch方法:(用nm命令获取所有的object file,并搜索__sti和__std来找到需要被调用的文件用于第三步)

  1. 为每一个需要静态初始化的文件产生一个__sti()函数(static initialization),内含必要的constructor调用操作或者inline expansions。
  2. 为每一个需要静态的内存释放操作产生__std()函数(static deallocation),内含必要的constructor调用操作或者inline expansions。
  3. 提供一组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的生命结束,或者知道临时对象的生命范畴结束,视哪种情况先到达而定。

赞(0) 打赏
未经允许不得转载:Techie亮博客 » 《深度探索C++对象模型》笔记(6)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏