转:前置自增(自减)和后置自增(自减)
第一篇:转自 http://pppboy.blog.163.com/blog/static/30203796201041014447359/
http://pppboy.blog.163.com/blog/static/302037962011112311822421/
一、基本认识
前置递增效率更高,后置式递增要拷贝一个临时对象。
1 int b = i++; 2 //可以分解为一下过程: 3 int temp = i; 4 b = temp; 5 i = i + 1;
n++要开辟一个变量来保存n的值并返回,然后让n这个变量中的值加 1。而++n直接把1加到n这个变量的空间中去,并返回n这个空间中的值,没有开辟任何临时空间,性能更高。
n+=1与n=n+1在结果上是等价的。但使用复合赋值操作符+=时,直接把1加到变量n的空间中去,左操作数只计算了一次加法;而使用长表达式 n=n+1时,先要去n这个变量空间中去取值(要寻址),与1做一次加法计算,然后做一次赋值计算把结果存入到n这个空间中,这样左操作数n计算了两次。因此,n+=1的性能更高。事实上,这也是复合赋值操作符存在一个基本原因。很多人刚开始八成会困惑,为什么会存在+=,-=,*=这样的操作符,能提高程序的性能就是其中的一个原因。
1.VC编译器优化?能前置,就别后置。(这一条应该视情形而定)
2.养成使用前置操作符的习惯
3.养成使用复合赋值操作符的习惯
二、重载和句法区别
C++规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数。
一个例子可以说明一切
1 #include "stdafx.h" 2 #include <iostream> 3 4 class NumUpper 5 { 6 public: 7 8 //前缀,返回引用 9 NumUpper& operator++() 10 { 11 std::cout << "++ prefix\n"; 12 13 *this += 1; //operator+= 14 return *this; 15 } 16 17 //后缀 返回const对象,int参数 18 const NumUpper operator++(int) 19 { 20 std::cout << "postfix ++ \n"; 21 22 NumUpper preNum = *this; 23 ++(*this); 24 return preNum; 25 } 26 27 NumUpper& operator+=(int num) 28 { 29 mNum += num; 30 return *this; 31 } 32 33 private: 34 int mNum; 35 }; 36 37 //main 38 int main(int argc, char* argv[]) 39 { 40 41 NumUpper upper; 42 43 //前置 44 std::cout << "-----------------------\n"; 45 ++upper; 46 47 //后置 48 std::cout << "-----------------------\n"; 49 upper++; 50 51 //前置 52 std::cout << "-----------------------\n"; 53 upper.operator++(); 54 55 //后置 56 std::cout << "-----------------------\n"; 57 upper.operator++(0); 58 59 std::cout << "-----------------------\n"; 60 system("pause"); 61 return 0; 62 } 63
//output:
-----------------------
++ prefix
-----------------------
postfix ++
++ prefix
-----------------------
++ prefix
-----------------------
postfix ++
++ prefix
-----------------------
请按任意键继续. . .
从上面的例子可以看到:
前缀形式返回一个引用
后缀形式返回一个const类型对象
三、其它联想
1.const的使用
比如不能用 upper++++这样,因为返回的是const,正好这个const就是为了防止这么连加用的。
2.对自减也是一样
3.还是效率问题
(1)如编程规范所说,不要进行不成熟的劣化:
<1>在使用前置++操作很合适的场合,却偏偏使用了后置++
<2>可以传引用的时候,去偏偏用了传值
<3> 构造函数中可以使用初始化列表,却偏偏使用赋值
(2)大气使用,该用后置的时候还是要用后置,正确远比高效重要
四、引用参考:
《More Effective C++》
《C++ Standard Library》
第二篇:转自 http://www.cnblogs.com/maxwellp/archive/2012/02/11/2346844.html
《C++ Primer》确实给我带来了很多惊喜。
作为一本专业的具有较高门槛的"入门书",它有很多地方实在是国内的书不可及的,这不仅在于内容上的严谨,更表现在优秀的习题所带给学习者的启迪。
习题5.16
你认为为什么C++不叫做++C?
看到这个习题,顿感眼前一亮(不禁想起某些国内书籍的题目,真是平淡如水),这对于理解前自增操作与后自增操作而言着实是个好的题目。
想弄明白这个问题,就需要先理解一些基础的概念。什么是自增操作呢?
++操作符,即自增操作符。自增操作符有两种形式:前置操作和后置操作。前自增操作生成左值,在给操作数加1后返回改变后的操作数值。后自增操作生成右值,给操作数加1但返回未改变的操作数原值。
*事实上,因为C++具有操作符重载的功能,自增操作不仅仅可以表示加1
前置操作返回的是加1后的值,返回的是对象本身,所以这是左值。
后置操作返回的是加1前的值,其返回值可以近似的理解为与原操作数值相等的常量,所以是一个右值。
具体例子:
int i = 0, j;
j = ++i; // j = 1 , i = 1:prefix yields incremented value
j = i++; // j = 1 , i = 2:postfix yields unincremented value
附左值与右值的概念:
左值:可以出现在赋值操作左边的值。非const左值可读可写。
右值:可用于赋值操作的右边但不能用于左边的值。右值只能读不能写。
左值可以出现在赋值操作右端,但右值不可以出现在赋值操作左端,将后自增操作置于赋值操作左端将会出现编译错误。
另外需要注意:
由于后置操作符要返回未加1前的值作为操作的结果,所以必须要保存操作数原来的值(即上一篇说的临时对象),对于比较复杂的类型,这种额外工作可能会花费更大的代价。
建议:只有在必要时才使用后置操作符。
最后,让我们用全新的角度去审视C++的命名原因
C++之名是Rick Mascitti在1983年夏天定名的(参见The C++ Programming Language(Special Edition)1.4节),C说明它本质上是从C语言演化而来的,“++”是C语言的自增操作符。C++语言是C语言的超集,是在C语言基础上进行的扩展(引入了new、delete等C语言中没有的操作符,增加了面向对象程序设计的直接支持,等等),是先有C语言,再进行++。根据自增操作符前、后置形式的差别,C++表示对C语言进行扩展之后,还可以使用C语言的内容;而写成++C则表示无法再使用C的原始值了,也就是说C++不能向下兼容C了,这与实际情况不符。
如果以后有人问你这个问题,你会回答了吗?囧。