C++Primer第4章 表达式——难点

C++Primer第4章 表达式——难点

1、左值和右值
(1)表达式的求值结果只有左值和右值两种。
(2)当一个对象被用作右值的时候,用的是对象的值(内容)。右值是使用对象的值。
(3)当对象被用作左值的时候,用的是对象的身份(在内存中的位置或地址)。左值是使用对象的存储空间
难点 对象的值和对象的地址

特性 对象的值 对象的地址
含义 存储在对象中的值,例如整型的1,字符型的a,某个对象的指针(地址)等 存储对象的值的存储空间
值类型 右值,不允许右值替代左值 左值,允许左值替代右值,这时左值使用的是对象的值

(4)其他运算符的左值和右值

运算符 所需或作用的运算对象的值类型 表达式的求值类型
赋值运算符 需要一个左侧的非常量左值运算对象 结果是左值
取地址符 作用一个左值运算对象 返回的是一个对象的指针的值,是右值
内置解引用符、下标运算符、迭代器解引用运算符、string和vector的下标运算符 作用的是右值运算对象 结果是左值
内置类型和迭代器的递增递减运算符 作用于左值运算对象 结果是左值

(5)使用关键字decltype
①根据表达式的求值结果的不同,decltype返回的类型也不同。

表达式的求值结果的值类型 decltype的返回的类型
左值 引用类型
右值 根据表达式的类型推断出decltype返回的类型

注意:对象的引用使用的是对象的存储空间,而不是对象的值;所以左值对应的是引用类型。当使用对象的引用去初始化或赋值另外一个对象时,使用的左值实际上用的是对象的值,即左值可转换成右值。

2、求值顺序
(1)求值顺序:当表达式中有多个运算对象时,大多数的运算符并没有规定运算对象的求值顺序,我们无法知道编译器对哪个运算对象先求值。
如:int a=2,b=3,f1()返回值的是1;a+b+f1();这个表达式中共有三个运算对象,编译器对它们的求值顺序是随机的,可能是先把a替换成2,也可能是先把b换成3,也可能是先调用f1()函数返回1。

(2) 运算对象的求值顺序与优先级、结合律无关。
举个例子来分别表示,比如2+3*4+5.
①优先级的体现:2+(3x4)+5;优先级体现的是运算对象的组合方式,比如这个例子因为乘法运算符优先级高于加法运算符,所以3和4相乘,而非是3和2相加。即不是(2+3)x(4+5)这样的形式。

②结合律的体现:如果运算符的优先级相同,则运算对象和运算符的组合由结合律决定。左结合律的形式:(2+(3x4))+5。另一个例子:a=b=c=1;,三个=运算符的优先级是相同的,因为赋值运算遵从右结合律,所以组合方式是:(a=(b=(c=1)));

③求值顺序的体现:求值顺序与优先级和结合律无关。
比如上面的组合:(2+(3x4))+5;虽然3x4是最高优先级,但不代表它是最先计算的;虽然算数运算符满足左结合律,并不代表2是最先计算的。编译器的计算次序是随机的,未定义的。
计算次序可能是:
a、2->3->4->3x4->2+(3x4)->2+3x4+5
b、4->3->3x4->2->2+(3x4)->5>2+3x4+5
c、5->2->3->4->3x4->2+(3x4)->2+3x4+5

注意:左结合律的作用是确定运算对象的组合顺序,而非确定运算对象求值顺序,比如说不能确定计算次序:2->3->4->3x4->2+3x4->5->2+3x4+5,这样的顺序是不正确的。

④大多数的运算符没有明确规定运算对象的求值顺序,但有4种运算符明确规定了运算对象的求值顺序。
逻辑与运算符(&&),逻辑或运算符(||),条件运算符(?:),逗号运算符(,)。

(3)使用规则(C++PrimerP123的一句话):对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。
比如<<运算符没有明确规定运算对象何时以及如何求值,下面的这条语句是未定义的。
int i=0;
cout<<i<<" "<<++i<<endl;
解释:因为编译器对运算对象的计算次序是随机的,所以编译器的计算次序可能是:
①++i->i,那么输出结果就是1,1。
②i->++i,那么输出结果就是0,1.
这样上述语句就会产生错误。

上面这句话通俗来讲就是,如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。

但有个重要例外:当改变运算对象的子表达式就是另外一个子表达式的运算对象时该规则无效。
例如*++iter。就是一个表达式内不可以出现两个相同的运算对象(包括值改变后的运算对象)。

猜你喜欢

转载自blog.csdn.net/qq_47466050/article/details/106675668
今日推荐