6.1 对象的构造与析构

Q1:对象的构造函数与析构函数的分布

• 对象在被创建时就会调用其构造函数 ,若在一个区段中有一个以上的离开点,则析构函数必须放在每个离开点前

Eg:

    {
        Point point;
        //构造函数调用

        switch(int(point.x))
        {
            case -1 : 
                //析构函数
                return;
            case 0 :
                //析构函数
                return;
            case 1 : 
                //析构函数
                return;
            default:
                //析构函数
                return;
        }
        //析构函数(尽管程序不会运行到这里,但在对象的生命结束期末尾也会调用析构函数)
    }

把对象尽可能的放置在使用它的程序区段附近,可以节省非必要的对象产生操作和销毁操作



Q2:全局对象

• 全局对象如果有构造函数与析构函数的话,称这个全局对象需要静态初始化操作和内存释放操作

• C++中所有的全局变量都放置在程序的数据段中,若显示给定一个值,则对象以该值作为初值,否则对象所配置到的内存内容为0

全局类对象在编译器被放置在数据段中且内容为0,构造函数直到程序启动时才会实施

• 全局对象的Munch策略,实现在程序开始时对全局对象进行初始化,在程序结束前调用对象的析构函数

  1. 为每一个需要静态初始化的文件产生一个_sti()函数,内含必要的构造函数调用操作或 inline expansions

  2. 在每一个需要静态的内存释放操作的文件中产生一个_std()函数,内含必要的析构函数的调用操作

  3. 提供一组运行时库”munch”函数:

    1) 一个_main()函数(用以调用可执行文件中所有的_sti()函数)
    2) 一个exit()函数(用以调用可执行文件中所有的_std()函数)

• 收集一个程序中各个对象的_sti()函数与_std()函数

  1. 分析符号表格的方法

        a) 使用nm列出对象文件的符号表格项目
    
        b) 使用 munch 程序分析符号表格中的名称,搜索以_sti或_std开头的名称
    
        c) 把这些函数名称加到一个sti()函数和std()函数的跳离表格中,并将这个更表格写到一个小的 program test 文件中 
    
        d) CC命令被激活,编译这个内含表格的文件,重新连接整个可执行文件
    
        e) _main()函数与exit()函数在各个表格上走访一遍,轮流调用每一个项目        
    
  2. 基于特定平台的C++编译器的情况下,定义不同的目的文件格式,以支持静态初始化与内存释放操作




Q3:局部静态对象

局部静态对象的构造函数与虚构函数在程序的生命周期中只会被调用一次,尽管其所在函数可能被调用多次

• 保护局部静态对象的初始化操作(确保在整个生命周期中只执行一次)

• 导入一个临时对象以保护静态局部变量的初始化操作

• 第一次处理其所在函数时,该临时对象被评估为 false,于是静态局部对象构造函数被调用,然后将临时对象改为 true,之后再处理该函数时,不会再调用静态局部对象的构造函数

• 保护局部静态对象的析构操作(确保只在程序结束前进行销毁)

• 有条件的施行静态局部对象的构造函数:仅在静态局部对象已经被构造了的情况下,才在最后施行析构操作。(可判断保护构造函数的临时对象是否为true)

• 一个问题:不可以在静态内存释放函数中存取静态局部对象(因为该对象是局部的)
• 解决办法:可以取出该局部对象的地址(这是因为该对象是静态对象,因此会被存储在放置全局对象的数据段中,因此,取出的局部对象的地址不会失效(**特别注意:当且仅当为静态对象才行))

Eg:

        const Matrix & identity()
        {
            static Matrix mat_identity;
            //…
            return mat_identity;
        }

该函数将被扩展为以下形式:(加入了构造函数保护对象)

        static struct Matrix * __0_F3 = 0;      //临时对象

        //C++中的引用在C中是以指针来代替的

        struct Matrix * identity_Fv()
        {
            static struct Matrix __lmat_identity;

            //如果临时性的保护对象已经设定,那就什么也不做;
            //否则将调用构造函数(__ct__6MatrixFv),设定保护对象,使其指向目标对象(为销毁对象做准备)

            __0_F3 ? 0 : (__ct__6MatrixFv)(&__lmat_identity),(__0_F3 = (&__lmat_identity)));

            //…
        }

该静态局部对象的析构函数将在静态内存释放函数中有条件的调用:

        char __std__stat_0_c_j()
        {
            __0_F3 ? __dt_6MatrixFv(__0_F3,2) : 0;

            //…
        }



Q4:对象数组

• 假设有以下数组定义:

Point knots[10];

• 若 Point 即没有定义一个构造函数也没有定义一个析构函数,则该操作的工作与建立一个“内建类型所组成的数组”相同,只需要分配足够内存来存储10个连续的Point元素即可

• 若 Point 定义了构造函数或析构函数,则构造函数与析构函数必须轮流施行于每一个元素之上

• cfront 中,使用一个命名为vec_new()的函数,产生出以类对象构造而成的数组(该类对象没有虚基类),用另一个命名为vec_vnew()的函数来处理内含虚基类的类对象数组

• 关于vec_new()

    • 原型:void * vec_new(
                            void * array,               //数组起始地址
                            size_t elem_size,           //每一个类对象的打下
                            int elem_coutn,                                //数组中元素的个数
                            void (*constructor)(void*),           //构造函数的指针
                            void(*destructor)(void*,char)  //析构函数的指针
                            )
    • 若array持有的不是数组的地址,就是0。若是0,则数组将通过应用程序的 new 运算符,动态配置于对中。

• Eg:上述 Point 数组的定义操作,被编译器转换为:

            Point knots[10];
            vec_new(&knots,sizeof(Point),10,&Point::Point,0);

• 当对象数组的生命结束时,其析构函数也必须实行于每个对象上,此时类似的 vec_delete()与vec_vdelete()函数来完成析构函数的调用

• 当程序员提供一个或多个明显的初值给一个由类对象组成的数组时,对于明显获得初始的元素,vec_new()不再必要,对未获得初始值的元素,仍旧调用vec_new()

Eg:

        Point knots[10] = { Point(), Point(1.0,1.0,0.5),-1};

编译器将其转换为如下:

        Point knots[10];

        //显示的初始化前三个元素
        Point::Point(&knots[0]);
        Point::Point(&knots[1],1.0,1.0,0.5);
        Point::Point(&knots[2],-1,0,0);

        //仍旧以 vec_new() 调用构造函数初始化后七个元素
        vec_new(&knots+3,sizeof(Point),7,&Point::Point,0);



Q5:默认构造函数和数组

• 不能在程序中取出构造函数的地址,对vec_new()函数而言,经由一个指针来启动构造函数,将无法存取默认参数值

• cfront的解决办法:产生一个内部的stub constructor,该构造函数没有参数。但在其内部调用由程序员提供的构造函数,并将参数值显示的指定过去

• 对于cfront而言,将有两个没有参数的构造函数,一个默认构造函数(带默认参数值),一个合成的stub constructor。当时只有当类对象数组真正被产生出来时,stub实例才会被产生并调用

Eg:

        class complex
        {
            complex(double = 0.0,double = 0.0);
            //…
        };

当程序员定义:complex c_array[10];
编译器需要调用: vec_new(&c_array,sizeof(complex),10,&complex::complex,0); //将无法获得默认参数
因此,合成以下 stub constructor

        complex::complex()
        {
            complex(0.0,0.0);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值