构造函数语义学之程序转化语义学(2)

  在 构造函数语义学之程序转化语义学(1) 中编译器做了一些优化,有时他还会给你的程序更多的优化:

  (1) 在使用者层面做优化

  如果程序员顶一个计算用的 constructor:

    X bar( const T &y, const T &z )

    {

      X xx;

      // ...以 y 和 z 来处理 xx

      return xx;

    }

  有的编译器开发人员会另外定义一个 constructor:

    X bar( const T &y, const T&z )

    {

      return X( y,z );

    }

  定义被转化之后,再做如下的优化:

    void ( X &__result , const T &y, const T &z) //我觉得侯捷大师说得对哈

     {

      __result.X::X( y, z );

      return;

     }

  这样就不用__result就可以被直接计算出来而不用经由 copy constructor 拷贝而得。不过这种解决方法也受到了某种批评,怕那些特殊用途的 constructor 可能会大量扩散。在这个层面上,class 的设计是以效率考虑居多,而不是以“支持抽象化”为优先!

  (2) 在编译器层面做优化

    X bar()

    {

      X xx;

      // ...处理 xx

      return xx;

    }

  编译器把其中的 xx 用 __result取代

    void bar( X &__result)

    {

      // default constructor 被调用

      // C++伪码

      __result.X::X();

      // 直接处理 __result;

      return ;

     }

  这样的编译器优化操作,有时候被称为 Named Return Value 优化!NRV优化如今已被思维标准 C++ 编译器的一个义不容辞的优化操作,但 NRV 优化在每个编译器上的规则可能不一样。

  为什么以上1,2被分为 在用户层面( User-level ) 和 在编译器层面( Compiler-level )做优化呢?大家看看哈,在(1)中是在用户代码中另加了一个 constructor ,故属于用户层面。而 (2) 呢,是在将用户的代码在编译的时候进行优化,故被称为编译器层面优化!

   大家再来看下面这个例子:

    class A                        class A

    {                           {

      public:                         public:

        A()                            A():name(0),num(0)

        {                             {

          name = 0;                          

          num = 0;                         num = 0;

        }                             }

      private:                         private:      

        string name;                      string name;

        int num;                         int num;

    }                              }

  请大家对比一下上面 class A 的构造函数的不同,并说出哪种代码在执行时会更快?

  答案当然是第二种,我相信很多人都知道这一结果,那这是为什么呢?这要从编译器的优化说起了

  上面的第一种写法会被编译器转化成如下代码:

    A::A()

    {

      // 调用 string 的 default constructor

      name.string::string();

      //产生暂时性对象

      string temp = string( 0 );

      // "memberwise" 地拷贝 name

      name.string::operator = ( temp );

      temp.string::~string();

      num = 0;

    }

  而第二种写法会被编译器转化成如下代码:

    A::A()

    {

      //调用 string ( int )constructor

      name.string::string( 0 );

      num = 0;

    }

  从转化后的代码可看出成员初始化列表的方式明显快的多,所以大家以后遇到以上情况时,尽量写成成员初始化列表的形式以使你的程序跑的更快。不过一下四种情况可由不得你了,你必须用成员初始化列表的形式也就是上面第二种写法,而不能采用第一种写法。

  (1). 当初始化一个 reference member 时。

  (2). 当初始化一个 const member 时;

  (3). 当调用一个 base class 的constructor ,而它拥有一组参数时。

  (4). 但调用一个 member class 的 constructor,而它拥有一组参数时。

  继续往下看如下代码

    class X 

    {

        int i;

        int j;

      public:

        X(int val):j(val),i(j)

        {}

    }

  咋看上去上面的初始化代码没错,先把 val 值赋给 j,再将 j 的值赋给 i。但实际上是这样吗?来看看编译转化后的代码:

    X::X(int val)

    {

      // 调用 int 的 constructor 来初始化 i 和 j

      // 伪C++代码

      i = j;

      j = val;

    }

  许多人看完这个转化后的吗之后肯定会一目了然,知道上面代码是有问题的了,并没有达到先赋值给 j 然后再赋值给 i 的目的!读者肯定要问为什么 i = j 会在前面呢?那是因为 class X 在定义 date member 时先定义 i,编译器在优化时会在 constructor 中将先声明的 data member 先初始化。故我们那些写是不对的啦!这样 i 会是一个不确定的值。所以下次,该怎么写你的代码了吧?^_^

    

原文地址:https://www.cnblogs.com/zhuwbox/p/3422497.html