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

  1. 在函数中,编译器会帮助将析构函数(Destructor) 安插在相应的位置。对于函数中的局部对象,会将析构函数安插在对象的每一个离开点。
    例如:
       1: void Function(int a) {
       2:   Object obj;
       3:   swithch(a) {
       4:      case 0 :....;return;
       5:     case 1 : ....;return;
       6:     case 2 : ....;return;
       7:     default : ....;return;
       8:   }
       9: }
    在上面的函数中,在switch的每个分支中,都会安插Object对象的析构函数。
    一般而言,我们把Object尽可能放置在使用它的那个程序区段的附近,这么做可以节省非必要的对象产生操作和析构操作。 (注:即可能在代码的一些分支逻辑中,根本不会走到使用对象的分支。此时最好把相应对象的创建放在对应的分支逻辑中,以避免无论使用使用都执行对象的产生和析构操作
    例如
       1: void Function(int a) {
       2:   if (a < 0) {
       3:     return;
       4:   }
       5:   Object obj;
       6:   ....
       7:   return;
       8: }
    在上面的逻辑中,在传入的参数a小于0的时候,是不需要使用Object对象的。因此如果在函数的开始就创建Object的话,就导致无论是否使用都会触发该对象的创建和析构。
  2. new 和delete 运算符
    int *pi = new int(5);
    new运算符看起来似乎是一个单一运算,实际上由两个步骤完成的:
    1. 通过适当的new运算符函数实例,配置所需要的内存: int *pi = __new(sizeof(int))
    2. 将配置得带的对象设立初值: *pi = 5
  3. 布局new操作符并不支持多态。其行为是未定义的。
  4. C++标准允许编译器对于临时对象的产生有完全的自由度
    例如:
       1: T operator+(const T&, const T&);
       2: T a, b;
       3: T c = a + b;
    对于上面这段代码可能如下:
    1. 编译器产生一个临时对象,保存a+b的结果,然后再使用T的拷贝构造函数,把该临时性对象当做c的初始值
    2. 直接以拷贝构造的方式,将a+b的值放到c中。
    3. NRV(name return value)优化,直接在上述c对象中求表达式的结果,避免执行拷贝构造函数和具名对象的destructor
  5. 而对于临时对象的摧毁,则规定如下:
    临时性对象的被摧毁,应该是对完整表达式求值过程中的最后一个步骤。该完整表达式造成临时对象的产生。
       1: ((objA > 1024) && (objB > 1024)) ? objA + objB : foo(objA, objB)
    对于上面的表达式产生的任何一个临时对象,都是在整条语句执行完之后,才可以摧毁。
    例外情况:
    1. 表达式用来初始化一个object:
         1: bool verbose;
         2: ...
         3: string progNameVersion = !verbose ? 0 : progName + progVersion;
      对于上面这种情况,由于progNameVersion需要使用该对象进行初始化,如果在初始化之前临时对象被析构,那么结果就是错误的。不是我们希望的。
      标准要求:凡持有表达式执行结果的临时性对象,应该存留到object的初始化操作完成为止
    2. 当一个临时对象被一个reference绑定时:
      标准要求:如果一个临时性对象被绑定于一个reference,对象将保留,知道被初始化值reference的生命结束,或直到临时对象的生命scope结束——视哪一种情况先到达。
原文地址:https://www.cnblogs.com/lovemdx/p/3251676.html