自增、自减运算符的前缀和后缀

试卷中有这么一道题目:

1

2

inta = 4;

(++a) += i;

求a的数值,正确答案是10。

如果你认为这道题重点只是考察运算符优先级,可能很容易得到正确的答案。

但是,考虑过为什么下面的代码无法编译么?

自己在笔试时,考虑到了关于表达式作为赋值运算符左值的问题,但是自己确实又对重载“++”操作符的实现机制和函数原型不很了解,就误认为“a++”和"++a"这两种写法都不能作为赋值运算符左值,从而以为这道题出错了,或者故意考察这一点,就直接写了个“无法编译”……

我回来后问了两个小伙伴,他们都能得到“(++a) += a;”的正确结果即 a = (a+1)+(a+1),但是无法解释为什么“(a++) += a;”无法编译,而且居然一致认为“++a”是“先执行自增后返回值,因此表达式是自增后的值”,而“a++”则是“先返回自增前的值,在这一条语句执行完之后a才自增”!

上述关于前自增运算符的认识基本是对的,但是关于后自增运算符的认识却大大的错了!在此鄙视一下这两个小伙伴,难道你们的C语言是体育老师教的么?居然会认为一个自增运算符能先执行一部分,在整条语句句执行后再执行另一部分……(哪种语言有这功能?)

今天上午花时间研究了一下这方面的内容,才恍然大悟,对自增/自减运算符的两种形式又加深了不少理解。不敢独享,特总结成文如下,也顺便纠正一下小伙伴们的错误认识@shasha,哼。

.

一、自增、自减运算符前缀和后缀形式的区别

我们都知道C/C++中大名鼎鼎的自增运算符(++操作符)具有两种形式:前置操作和后置操作。

从运算符的实现上来看,a++与++a的差别如下:

(1)前递增运算a++,在执行过程中,先将对象进行递增修改,而后返回该对象的引用。

(2)后递增运算++a,在运算符重载函数中采用值返回的方式编写,重载函数的内部创建一个用于临时存储原有对象值的对象,在执行完对a的自增后,函数返回该临时对象的值。

简单地讲,就是:

前自增操作生成左值,在给操作数加1后返回改变后的操作数值;而后自增操作生成右值,给操作数加1但返回未改变的操作数原值。

附左值与右值的概念:

左值:可以出现在赋值操作左边的值。非const左值可读可写。

右值:可用于赋值操作的右边但不能用于左边的值。右值只能读不能写。

因此,左值可以出现在赋值操作右端,但右值不可以出现在赋值操作左端,,将后自增操作置于赋值操作左端将会出现编译错误。

现在来分析为什么"(a++) += a;"无法编译的问题:对于“ (a++) += a; ”,先运算(a++),但(a++)返回的不是引用,而是原有a值的一个拷贝,而此时的拷贝不再是一个变量,还是一个常量,故不能当作左值继续参与运算。

同理,这也可以解释如下的几个表达式是否能够编译:

(1) ++++a;

(2) a++++;

(3) ++a++;

我们知道:自增运算符++是结合方向是自右自左(VC++6.0),所以++++a也写成++(++a),显然是正确的。而a++++写作(a++)++显然是错误的,这会导致发生编译错误:

error C2105: ‘++’ needs l-value.

至于++a++,依据结合性,写作++(a++),也是错误的,但是需要注意的是,(++a)++却是正确的写法。

.

二、关于++运算符的重载

很久以前(八十年代),没有办法区分++和–操作符的前缀与后缀调用。这个问题遭到程序员的报怨,于是C++语言得到了扩展,允许重载increment 和 decrement操作符的两种形式。

然而有一个句法上的问题,重载函数间的区别决定于它们的参数类型上的差异,但是不论是increment或decrement的前缀还是后缀都只有一个参数。为了解决这个语言问题,C++规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

classUPInt { // "unlimited precision int"

public:

UPInt& operator++();// ++ 前缀

constUPInt operator++(int);// ++ 后缀

UPInt& operator–();// — 前缀

constUPInt operator–(int);// — 后缀

UPInt& operator+=(int);// += 操作符,UPInts 与 ints 相运算

};

UPInt i;

++i;// 调用 i.operator++();

i++;// 调用 i.operator++(0);

–i;// 调用 i.operator–();

i–;// 调用 i.operator–(0);

这个规范有一些古怪,不过你会习惯的。而尤其要注意的是这些操作符前缀与后缀形式返回值类型是不同的。前缀形式返回一个引用,后缀形式返回一个const类型。下面我们将讨论++操作符的前缀与后缀形式,这些说明也同样使用与–操作符。

但是从你开始做C程序员那天开始,你就应该记住increment的前缀形式有时叫做“增加然后取回”,后缀形式叫做“取回然后增加”。这两句话非常重要,因为它们是increment前缀与后缀的形式上的规范。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// prefix: increment and then fetch

UPInt& UPInt::operator++()

{

*this+= 1; // 增加

return*this;// 取回值

}

// postfix form: fetch the origin and increment

constUPInt UPInt::operator++(int)

{

UPInt oldValue = *this;// 取回值

++(*this);// 增加

returnoldValue; // 返回被取回的值

}

生活不要太劳累,弄得自己很疲惫,快乐幸福多体会,

自增、自减运算符的前缀和后缀

相关文章:

你感兴趣的文章:

标签云: