今天在阅读《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. Android加壳native实现

    本例仅在Android2.3模拟器跑通过,假设要适配其它机型.请自行研究,这里不过抛砖引玉. 0x00 在Android中的Apk的加固(加壳)原理解析和实现,一文中脱壳代码都写在了java层非常ea ...

  2. QQ空间g_tk、bkn加密参数算法

    g_tk是腾讯在QQ空间这一领域使用的密文,有写数据包或者url参数中需要加入你计算出的g_tk才能成功! 下面是通过浏览器抓包工具抓取 访问该js内容找出 QZONE.FrontPage.getAC ...

  3. 安卓UI适配限定符

    引言 对于程序在不同尺寸的Android机器上执行,对UI的适用性造成了额外的开销,只是限定符的出现,非常方便的攻克了这个问题.通过创建限定符相关的文件夹来解决资源的载入. 限定符用处 限定符(mdp ...

  4. python--文件操作补充

    文件操作补充 f = open('file',encoding='utf-8')其中打开时不写模式默认只读f是文件句柄,文件操作符,不能使数字相当于content = f.read()content是 ...

  5. Effective Java - [2. 创建与销毁对象]

    让对象的创建与销毁在掌控中. Item 1: 使用静态工厂方法而非使用构造函数 public static Boolean valueOf(boolean b) { return b ? Boolea ...

  6. Activiti实战

    说实话,接触Activiti已经是3年前的事情,那时候组里想做一个流程自动化的application,并且记录用户点击.做单量等.第一次听说Activiti,感觉挺好奇的,遂看了下相关的文档跟同事的代 ...

  7. 04 http协议模拟登陆发帖

    <?php require('./http.class.php'); $http = new Http('http://home.verycd.com/cp.php?ac=pm&op=s ...

  8. mongodb学习之:GridFS

    GridFS是一种在Mongodb中存储大二进制文件的机制.GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片.音频.视频等). 使用GridFS有如下几个原因: 1 利用 ...

  9. 设置port转发来訪问Virtualbox里linux中的站点

    上一篇中我们讲到怎么设置virtuabox来通过SSH登录机器. 相同.我们也能够依照上一篇内容中的介绍,设置port转发,来訪问虚拟linux系统已经搭建的站点: 1.设置port转发: water ...

  10. IE6 PNG图片不透明的解决方案-tinypng

    https://tinypng.com/ 把图片上传上去,就能处理这个问题啦. 纠正一下 再也不用把png图片一个个拖到TinyPNG进行在线压缩(和熊猫哥哥说再见了):再不用把JPG/JPEG图片拖 ...