《深度探索C++对象模型》读书笔记第六章:执行期语意学


  • C++保证一定会在 main() 函数之前构造出全局对象,在 main() 函数结束之前把全局对象摧毁掉。这是静态的初始化操作和内存释放操作。
  • C++中所有的全局对象都放在程序的 data segment 中。如果明确给它一个值,对象将以该值为初值,否则对象所配置到的内存内容全为 0。包括 POD类型,这个C语言不一样。在C中专门有 .bss 段来存放未初始化的全局变量。

  • cfront 采用 munch 策略保证全局对象的构造与析构,有三部曲:
    1. 为所有的需要静态初始化的档案产生一个__sti() 函数,内带必要的 constructor 调用操作或 inline expansions。
    2. 在所有需要静态的内存释放操作的文件中,产生一个__std()函数(static deallocation),内带必要的 destructor 操作,或是其 inline expansions。
    3. 提供一组 runtime library “munch” 函数:一个 _main() 函数(用以调用可执行文件中所有的 __sti() 函数),以及一个 exit() 函数(以类似方式调用所有 __std() 函数。
如图:


  1. 对于对象数组,cfront 中采用 vec_new() 函数初始化数组中的对象,你可以显示初始化某几个对象,那么其他对象或许会默认构造初始化或者由于 trivial 什么也不做。
  2. 对于:point *ptr = new point3d[10]; 这种多态申请对象数组,如果直接 delete[] ptr,可能不会触发多态(亲测:gcc直接 core dump)。所以必须进行强制转换,使用delete[] static_cast<point3d*>(ptr);
  3. placement new 不分配内存,只调用构造函数。所以要提前申请好内存,防止段错误。
  4. placement new 在一个原已存在的对象上构造新对象,而该对象有一个析构函数,这个析构函数并不会调用。所以需要显示将原有对象销毁掉,但是不能直接 delete,因为直接 delete 会将内存也释放掉,所以必须显示调用析构函数(STL的 destroy 函数就是这么处理placement new 的)即可。如:p2w->~point2w(); p2w = new(arena) point2w; 

  1. C++中类似 T c = a + b,编译器优化后不会产生临时对象,但是赋值语句 c = a + b,不能够忽略临时对象,因为该表达式期待左端有一次析构操作,所以左端必须先析构,此时的 a + b 的结果会用 __temp0 来保存,左端完成析构后,再进行赋值。
  2. 在一个表达式中(比如各种与或非),临时对象的摧毁,应该是对完整表达式求值过程中的最后一个步骤。
  3. 临时对象的销毁有两个特例:
    1. 临时对象作为赋值语句的右值,必须等到左值对象初始化完成后才销毁(此时会进行拷贝构造)。
    2. 临时对象被引用绑定了,那么它的销毁会知道引用的生命结束,或者直到临时对象的生命周期结束——视哪种情况先到达而定。


附录
有关定位 new 的知识,来自知乎:
作者:蓝色
如果你是在placement new上面工作,你如果delete以后,你也把placement new上面的内存释放了,这往往不是你想要的结果。举个例子:
Point* p = new (arena) Point;
// do something
// manipulate a new object
delete p; // Wrong. You release the arena memory
p = new (arena) Point;

// correct method

p->~P();
p = new (arena) Point;
而这里,你说有没有placement delete? placement delete expression是绝对没有的东西。但是有一个placement delete function,而不是placement delete expression。而这个placement delete function是你 直接调用不到的东西。当placement new expression调用placement new function,如果构造函数 函数构造的时候发生了异常,这个时候要防止内存泄露,那么要 清理掉已分配的内存,就需要这个placement delete function。而这个东西的定义你可以打开<new>头文件看到。

但是,暴露出来的delete expression却只有两个出来: 也就是用于 非数组与数组的delete也就是用于非数组与数组的delete。

接下来举一个例子来说明一下这个构造函数发生异常的时候,调用 placement delete function的情况:
#include <cstdlib>
#include <iostream>

struct A {} ;
struct E {} ;

class T 
{
public:
    T() { throw E() ; }
} ;

void * operator new ( std::size_t, const A & )
    {
        void* nothing = 0;
        std::cout << "Placement new called." << std::endl;
        return nothing;
    }
void operator delete ( void *, const A & )
    {
        std::cout << "Placement delete called." << std::endl;
    }

int main ()
{
    A a ;
    try {
        T * p = new (a) T ;
    } catch (E exp) {std::cout << "Exception caught." << std::endl;}
    return 0 ;
}


结果如下:




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值