zoukankan      html  css  js  c++  java
  • (转)前置++和后置++的区别

     今天在阅读《google c++ 编程风格》的文档的时候,5.10. 前置自增和自减:有一句话引起了我的注意:

    对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.,理由是 前置自增 (++i) 通常要比后置自增 (i++) 效率更高。于是我查了查前置++和后置++的区别

    注意:《more effective c++》条款8也专门叙述了问题。后来我发现,下面的文章基本就是它的翻版,哈哈

    前置++和后置++的区别

     

    《C专家编程》中有如下描述(P276,人民邮电出版社):

    ++a表示取a的地址,增加它的内容,然后把值放在寄存器中;

    a++表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;(也就是说操作的时候用到的都是寄存器里面的值,即自增前的值)

     

    另外,网上找了篇文章,通过从运算符重载的角度来探讨他们的不同,如下:

     

    假设有一个类Age,描述年龄。该类重载了前置++和后置++两个操作符,以实现对年龄的自增。

     

       class Age   
        {   
        public:   
          
            Age& operator++() //前置++   
            {   
                ++i;   
                return *this;   
            }   
          
            const Age operator++(int) //后置++   
          {   
                Age tmp = *this;   
                ++(*this);  //利用前置++   
                return tmp;   
            }   
          
            Age& operator=(int i) //赋值操作   
            {   
                this->i = i;   
                return *this;   
            }   
          
        private:   
            int i;   
        };

    从上述代码,我们可以看出前置++和后置++,有3点不同:

    1. 返回类型不同
    2. 形参不同
    3. 代码不同
    4. 效率不同

    返回值类型的区别

    前置++的返回类型是Age&,后置++的返回类型const Age。这意味着,前置++返回的是左值,后置++返回的是右值。(关于左值和右值的讨论很多,见本文下面)

    左值和右值,决定了前置++和后置++的用法。

        int main()   
        {   
            Age a;   
          
            (a++)++;  //编译错误   
            ++(a++);  //编译错误   
            a++ = 1;   //编译错误   
            (++a)++;  //OK   
            ++(++a);  //OK   
            ++a = 1;   //OK   
        }  

    ++的类型是const Age,自然不能对它进行前置++、后置++、赋值等操作。

    ++a的类型是Age&,当然可以对它进行前置++、后置++、赋值等操作

    注:我们只要重载++时,只要认清前置++是没有参数的,后置++是有一个int型参数的就行了,实际调用的区分是编译器的事!

     

    a++的返回类型为什么要是const对象呢?

    有两个原因:

    1. 如果不是const对象,a(++)++这样的表达式就可以通过编译。但是,其效果却违反了我们的直觉 。a其实只增加了1,因为第二次自增作用在一个临时对象上。
    2. 另外,对于内置类型,(i++)++这样的表达式是不能通过编译的。自定义类型的操作符重载,应该与内置类型保持行为一致 。

    a++的返回类型如果改成非const对象,肯定能通过编译,但是我们最好不要这样做。

     

    ++a的返回类型为什么是引用呢?

    这样做的原因应该就是:与内置类型的行为保持一致。前置++返回的总是被自增的对象本身。因此,++(++a)的效果就是a被自增两次。

     

    形参的区别

    前置++没有形参,而后置++有一个int形参,但是该形参也没有被用到。很奇怪,难道有什么特殊的用意?

    其实也没有特殊的用意,只是为了绕过语法的限制

     

    前置++与后置++的操作符重载函数,函数原型必须不同。否则就违反了“重载函数必须拥有不同的函数原型”的语法规定。

    虽然前置++与后置++的返回类型不同,但是返回类型不属于函数原型。为了绕过语法限制,只好给后置++增加了一个int形参。

     

    原因就是这么简单,真的没其他特殊用意。其实,给前置++增加形参也可以;增加一个double形参而不是int形参,也可以。只是,当时就这么决定了。

     

    代码实现的区别

    前置++的实现比较简单,自增之后,将*this返回即可。需要注意的是,一定要返回*this。

    后置++的实现稍微麻烦一些。因为要返回自增之前的对象,所以先将对象拷贝一份,再进行自增,最后返回那个拷贝。

     

    在Age的代码中,后置++利用了前置++来实现自增。这样做是为了避免“自增的代码”重复。

    在本例中,自增的代码很简单,就是一行++i,没有必要这样做。但是在其它自增逻辑复杂的例子中,这么做还是很有必要的。

     

    效率的区别

    如果不需要返回自增之前的值,那么前置++和后置++的计算效果都一样。但是,我们仍然应该优先使用前置++,尤其是对于用户自定义类型的自增操作。

    前置++的效率更高,理由是:后置++会生成临时对象。

     

    从Age的后置++的代码实现也可以看出这一点。    

        const Age operator++(int) //后置++   
        {   
            Age tmp = *this;   
            ++(*this);  //利用前置++   
            return tmp;   
        }  

    很明显,tmp是一个临时对象,会造成一次构造函数和一次析构函数的额外开销。虽然,编译器在某些情况下可以优化掉这些开销。但是,我们最好不要依赖编译器的行为。


    所以,在非内置类型的时候,尽量使用前置++,因为效率高(后置自增,效率低)

    原文链接:http://blog.csdn.net/randyjiawenjie/article/details/6747720

  • 相关阅读:
    react 常用问题总结
    vue 给element-UI库添加按需加载时启动项目时 babel-preset-es2015 报错
    前端使用 jquery.base64.js 进行加密、解密十分方便
    CommonJS规范与AMD/CMD规范总结
    彻底搞懂Websocket原理
    调用图灵API V2 遇到的坑
    前端html页面,手机查看
    关于原生js的节点兼容性
    数据类型检测方法
    一位大佬关于js去重问题的研究
  • 原文地址:https://www.cnblogs.com/balingybj/p/4736195.html
Copyright © 2011-2022 走看看