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

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

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

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

  1. int a = 0;
  2. ++ a;   //前置++
  3. a++;    //后置++

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

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

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

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

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

  1. class Age
  2. {
  3. public:
  4. Age& operator++() //前置++
  5. {
  6. ++i;
  7. return *this;
  8. }
  9. const Age operator++(int) //后置++
  10. {
  11. Age tmp = *this;
  12. ++(*this);  //利用前置++
  13. return tmp;
  14. }
  15. Age& operator=(int i) //赋值操作
  16. {
  17. this->i = i;
  18. return *this;
  19. }
  20. private:
  21. int i;
  22. };

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

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

返回值类型的区别

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

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

  1. int main()
  2. {
  3. Age a;
  4. (a++)++;  //编译错误
  5. ++(a++);  //编译错误
  6. a++ = 1;   //编译错误
  7. (++a)++;  //OK
  8. ++(++a);  //OK
  9. ++a = 1;   //OK
  10. }

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

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

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

有两个原因:

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

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

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

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

形参的区别

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

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

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

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

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

代码实现的区别

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

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

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

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

效率的区别

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

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

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

  1. const Age operator++(int) //后置++
  2. {
  3. Age tmp = *this;
  4. ++(*this);  //利用前置++
  5. return tmp;
  6. }

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

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

C++之运算符重载(前置++和后置++)的更多相关文章

  1. C++前置++与后置++的区别与重载

    ++属于单目运算符,前置与后置的实现代码不一样,下面以整数自增为例: // 前置++,返回自增后的值,且返回的是一个左值 int& operator++(){ *this += 1; retu ...

  2. c++ 前置++与后置++的区别

    用C++编程的都知道,C++提供了一个非常强大的操作符重载机制,利用操作符重载,我们可以为我们自定义的类增加更多非常有用的功能.不过,C++也有限制,就是当我们为自定义的类重载操作符时,重载操作符的含 ...

  3. # c++运算符重载之 前置++, 后置++, 负号运算符, 类型转换函数, 以及输入输出运算符

    c++运算符重载之 前置++, 后置++, 负号运算符, 类型转换函数, 以及输入输出运算符 标签(空格分隔): c++ 前言 我在c++学习的过程中, 对这几个不太常见的运算符重载不太会写.出现了很 ...

  4. JavaScript运算符:递增递减运算符前置和后置的区别

    从两段代码说起 var num1 = 2; var num2 = 20; var num3 = --num1 + num2; var num4 = num1 + num2; console.log(n ...

  5. C++的前置++、后置++和前置--、后置--

    一.C++的前置++和后置++ 在C++中,运算符重载是你必须要掌握的重点,而前置++和后置++有什么区别呢?其实前置++和后置++是有关于 影响效率的问题,前置++比后置++的效率要高,原因是因为前 ...

  6. android Camera 如何判断当前使用的摄像头是前置还是后置

    现在 android 平台的智能手机一般都标配有两颗摄像头.在 Camera 中都存在摄像头切换的功能. 并且有一些功能前后置摄像头上会有所不同.譬如人脸检测,人脸识别,自动对焦,闪光灯等功能, 如果 ...

  7. ThinkPHP - 前置操作+后置操作

    前置操作和后置操作   系统会检测当前操作(不仅仅是index操作,其他操作一样可以使用)是否具有前置和后置操作,如果存在就会按照顺序执行,前置和后置操作的方法名是在要执行的方法前面加 _before ...

  8. PHP通过__call实现简单的AOP(主事务后的其他操作)比如前置通知,后置通知

    /** * person class */ class Person { /** * person class -> function say */ public static function ...

  9. 20_AOP_Advice增强1(前置、后置、环绕)

    [增强的类型] 1.前置增强:org.springframework.aop.BeforeAdvice. 由于Spring只支持方法级别的增强,所以MethodBeforeAdvice是目前可用的前置 ...

随机推荐

  1. 小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载一(PhoneGap中的API)

    之前本博连载过<构建跨平台APP:jQuery Mobile移动应用实战>一书.深受移动开发入门人员的喜爱. 从如今開始,连载它的孪生姐妹书phoneGap移动应用实战一书,希望以前是小白 ...

  2. php.ini的载入位置

    php.ini文件找不到,载入WINDOS下的,但找不到,后来强制-c查找是OK的.思考,为什么载入window下的ini文件.1.可能是有一个默认路径.2.可能没有路径.默认载入. 问题解决:htt ...

  3. hdoj 2188 悼念512汶川大地震遇难同胞——选拔志愿者 【巴什博弈】

    题意:. . . 策略:最简单的典型的巴什博弈. 代码: #include<stdio.h> int main() { int n, m; int t; scanf("%d&qu ...

  4. UVA 11578 - Situp Benches(dp)

    题目链接:11578 - Situp Benches 题意:健♂身♂房有两个仰卧起坐坐垫,每次调整角度要花费10元/10度,每次使用要花费15,如今给定n个人的时间顺序,和所希望的角度,求最少花费 思 ...

  5. 实习日记 laravel怎么删除磁盘上的文件

    Storage 里面有 delete的方法 具体使用是 Storage::disk('uploads')->delete($fileName); 其中'uploads'是filesystem里面 ...

  6. caffe2--ubuntu16.04--14.04--install

    Install Welcome to Caffe2! Get started with deep learning today by following the step by step guide ...

  7. Spring的常见问题及答案

    目录 Spring 概述 依赖注入 Spring beans Spring注解 Spring数据访问 Spring面向切面编程(AOP) Spring MVC Spring 概述 1. 什么是spri ...

  8. BUAAOO P13-P14 UML Interaction

  9. sql server t-sql脚本转成oracle plsql

    将一份SQL SERVER数据库生成的T-SQL脚本,转成ORACLE的PL/SQL,其复杂繁琐程度,远远出乎我的意料. 这份SQL SERVER脚本,里面有表,有视图,还有存储过程,以及一些自定义函 ...

  10. 九度OJ 1125:大整数的因子 (大数运算)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:974 解决:494 题目描述: 已知正整数k满足2<=k<=9,现给出长度最大为30位的十进制非负整数c,求所有能整除c的k. ...