表达式的副作用
C++语言中的表达式可分为算术表达式,关系表达式和逻辑表达式等多种类型,但从表达式中变量所起的作用角度来看可分为两类:有副作用的表达式和无副作用的表达式.
1 有关表达式的副作用的概念一般说计算一个表达式的值需要引用一些变量,在表达式求值过程中,需要提取这些变量的值,但并不改变这些变量的值,这样的表达式称为无副作用的表达式.从传统意义上讲,表达式的作用就是计算,它除了产生一个计算结果外,不应该改变参与计算过程的任何变量的值或产生其它的效应.换句话说,传统意义上的表达式是不应该有副作用的.因此,绝大多数的高级语言中的表达式都是无副作用的表达式.C++语言兼有高级语言和低级语言的特点,是一种典型的全面支持面向对象特性的语言,为了运行效率的提高,引用了具有副作用的表达式.一个表达式在求值过程中,对使用的变量不但引用,对它们的值还加以改变,这样的表达式称为有副作用的表达式,或者称这个表达式是有副作用的.
2 对表达式的副作用的分析与理解,有如下四个表达式:1、5*X;2、X+Y;3、X++;4、Y-=18*2.其中表达式1和表达式2是无副作用的表达式,因为在表达式求值过程中,所引用的变量X和Y的值均未发生变化,例如:已知X=5,Y=10,表达式1的值为25,表达式2的值为15,但X的值仍为5,Y的值仍为10,而表达式3和表达式4是有副作用的表达式, 因为在求表达式值的过程中,所引用的变量X和Y的值均发生了变化.例如:x和Y的值初始状态下分别是5和10 经计算后,表达式3的值为5,X的值变为6,表达式4的值为一26,Y的值变为一26.
对于给出的表达式,怎样才能正确地判断出它是有副作用的表达式还是无副作用的表达式?主要原因取决于表达式中出现的操作符.C++语言中的操作符可分为两类:无副作用的操作符和有副作用的操作符.如果一个表达式中引用了具有副作用的操作符,该表达式就是有副作用的表达式.C++语言引入的具有副作用的操作及相应的操作符包括: (1)赋值(=). (2)复合赋值(+=,一:, *=,/=,%=,《=,》=,!=,&=,一). (3)前增1,前减1(++, 一一).(4)后增1,后减1(++, 一一).这些操作所作用的对象必须是变量,因此这些操作也可称作用于变量的操作.对于作用于变量的操作,除后增1和后减1以,还有这样一个特点:作为操作结果的表达式的值就是所作用的变量获得的值.例如执行语句序列:int i=5;count<< (i+=3);count<<i,后两个输出结果均为8.也就是说表达式的值就存放在所作用的变量中.而对于后增1和后减1操作符,运算后尽管变量的值和表达式的值不同(表达式的值不能通过变量来表示),但是通过计算表达式的值,确使变量的值发生了变化,例如执行语句序列:int i=5;cout<<i++;count<<i;后两个输出结果分别是5和6,因此从有副作用的表达式的概念出发,后增1和后减1操作符仍是有副作用的操作符. .区别一个表达式是否是有副作用的表达式还可以从计算顺序上进行比较.不带副作用的几个表达式的值,不会因计算顺序不同而不同,例如:设X=3,Y=2,则3*x与表达式X+Y 的值一定是3*2=6与3+2=5;但是带副作用的表达式与其它表达式之间计算的顺序将非常重要,顺序不同结果也不同,例如:X=3,Y=2,则(++X)+Y与X+Y++依次计算后,表达式的值分别为6与6,而把两个表达式改变计算次序,先计算,X+Y++,后计算(++X) +Y,则表达式的值分别为5和7.因此在对多个有副作用的表达式进行计算时,一定要注意表达式的计算次序.
3 指针表达式的副作用有的指针表达式也有副作用,而产生副作用的原因也是因为使用了具有副作用的操作符.在与指针有关的操作中,++ (前增1)、一一(前减1)、+=、-=以及=这5种操作符用于变量并改变它的值,变量所获得的新值也就是指针表达式的值,因而表达式的值就间接地以变量形式提供.也就是说,这5种操作符的操作结果(即指针表达式的值)视同变量,可继续对之进行只能对变量才能实施的操作(包括上述5种操作以及取地址、后增1、后减1),因此象++ (++pk),pk+=&k这样的表达式都是合法的(其中pk是指针变量).对于++ (后增1)和一一(后减1)操作,虽然也作用于变量而且改变它的值,但指针表达式的值与变量所获得的新值不相同,因而表达式的值就不可能以变量形式提供,而只能以数值的这种更直接的形式提供.也就是说,后增1(或后减1)的操作结果视同常量的数值形式的地址值,因此象(pk++) ++,pk一一十=&k这样的表达式都是非法的(pk是指针变量).* (间接访问)操作结果视同变量,可继续对之进行只能对变量才能实施的操作;而& (取地址)作用于变量,但不改变变量的值,其操作的结果视同常量的数值形式的地址值,不可对之进行只能对变量才能实施的操作.对于这两种操作符,都是不具有副作用的操作符.总之,与数值表达式的副作用有关的结论,对于指针表达式同样适用.在指针表达式中具有副作用的操作符与数值表达式中的具有副作用的操作符相同.
4 表达式副作用的应用
4.1 逗号表达式 由逗号操作符(即,)将各个表达式连接起来就构成了逗号表达式,即表达式1,表达式2,? ? ,表达式n,逗号表达式的求值顺序是:先计算表达式1的值,然后计算表达2的值,依此类推,最后计算表达式n的值,最后计算出的表达式n的值就作为整个逗号表达式的值.例如:w--,j+=3,k++,P=w+j+k是一个逗号表达式,若初始状态下w=5,j=6,k=7,那么该逗号表达的值为21(5+9+7).对于逗号表达式中的每个表达式,除最后一个外,必须是具有副作用的表达式才有意义,如上例.而像X=5,6+18,72*9,Y=10这个逗号表达式中的表达式6+18和72*9是无副作用的表达式,计算时毫无意义.
4.2 表达式语句 在C++中,任何一个表达式都可以作为一个语句来使用,称为表达式语句,方法是在表达式后加上语句结束符(即;).C++表达式之所以可以作为语句使用,是因为C++拓展了表达式的概念,把赋值等具有副作用的操作也归入表达式的范畴.一切输入、输出操作都通过函数实现;并无专门的输入、输出语句,而函数调用也是表达式的一种形式.因此,除了流程控制语句外,其它如赋值、输入、输出等都只能通过表达式实现.表达式作为语句使用时,它的值被舍弃不用,其语句功能通过它的副作用体现, 因而把无副作用的表达式作为语句使用是毫无意义的,如表达式语句K+19;表达式的值(K与19之和)被舍弃不用,它是无意义的语句;只有把具有副作用的表达式作为语句使用才有意义,像表达式语句x+=5;表达式的值被舍弃不用,但由于+=是有副作用的操作符,经计算后表达式的值实际已保存在变量X中,因此该表达式语句是有意义的.总之,C++语言中表达式的副作用并不一定是坏东西,理解好它的概念,掌握有副作用的操作符,这样才能处理好它,给自己设计的程序带来较高的运行效率.