zoukankan      html  css  js  c++  java
  • 构造函数语义学之程序转化语义学(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 会是一个不确定的值。所以下次,该怎么写你的代码了吧?^_^

        

  • 相关阅读:
    vue工程项目中配置测试环境及生产环境线上地址
    复杂表格的拖拉拽,及行列拖拽
    前端骨架屏的资料
    关于vue中的重复key的警告问题
    git 约定规范
    git 博客
    关于svg的一些事
    vue单文件组件2(webpack打包)
    vue单文件组件1(webpack打包)
    vue-resource
  • 原文地址:https://www.cnblogs.com/zhuwbox/p/3422497.html
Copyright © 2011-2022 走看看