《C++ Primer》【Chapter 4】
作者:互联网
chapter4 表达式
基础
左值和右值
左值可以位于赋值语句的左侧,而右值不可以
当一个对象被用作右值当时候,用的是对象的值(内容);当对象被用作左值当时候,用的是对象的身份。
后面写的内容没看太明白
优先级和结合律
复合表达式:指含有两个或多个运算符的表达式。
高优先级运算符的运算对象要比低优先级运算符的对象更为紧密地结合在一起。如果优先级相同,则其组合规则由结合律确定。
括号无视优先级和结合律
求值顺序
优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。
int i = f1()*f2(); //f1和f2的执行顺序无法知道
对于f() + g() * h() + j()
表达式中:
- 优先级规定,g()返回值和h()返回值相乘
- 结合律规定,f()的返回值先和g()和h()的乘积相加,所得结果再与j()的返回值相加
- 对于这些函数的调用顺序没有明确的规定
如果f(),g(),h(),j()是无关函数,它们的调用顺序并不会有影响,但如果其中某几个函数影响了同一个对象,则由于调用顺序的不确定性,会导致该表达式是错误的。
处理复合表达式的准则
-
- 拿不准的时候用括号强制让表达式复合逻辑
-
- 如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象
算术运算符
+、-、*、/、%
其中+\-
也可以作为一元运算符。一元运算符的优先级最高,解下来是乘法和除法,优先级最低的是加法和减法。优先级高的运算符比优先级低的运算符组合得更紧密。
对于+
,可以用于指针,但只会返回这个指针的一个副本,即现在有两个指针指向该地址了。
int a = -4;
int *b = &a;
int *c = +b;
*c = 10;
cout << a << endl; //10
逻辑和关系运算符
什么是短路求值?
- 对于逻辑与运算符(&&)和逻辑或运算符(||)都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧对象的值(对&&即左侧是true时才会算右边的表达式,对||即左侧是false时才会算右边的表达式)。
进行比较运算时,除非比较对象是布尔类型,否则不要使用布尔字面值true/false作为运算对象
if(val == true) //如果val不是布尔类型会将true转换为1
//推荐使用
if(val)
if(!val)
赋值运算符
赋值运算的结果是它的左侧运算对象,并且是一个左值。相应的,结果的类型就是左侧运算对象的类型。如果赋值运算符的左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型。
int k = 0;
int k = 3.1415;//结果是3
C++11新标准允许使用花括号括起来的初始值列表作为赋值语句的右侧运算对象
int k = {3.14}; //错误,窄化转换 (空间大的转空间小的)
vector<int> vi = {0,1,2,3,4,5,6,7,8,9};
无论左侧运算对象的类型是什么,初始值列表都可以为空。此时,编译器创建一个值初始化的临时量(根据类型决定,int为0)并将其赋给左侧运算对象。
复合赋值运算符
+= -= *= /= %= //算术运算符
<<= >>= &= |= ^= //位运算符
使用复合运算符好处:只求值一次!
递增和递减运算符
除非必须,否则不用后置版本的递增递减运算符
这个与平常写代码的习惯可能不一样,一定需要注意!
即推荐++i
而不是i++
原因:后置版本需要将原始值存储下来以便返回这个未修改的内容。如果我们不需要修改前的内容,那么后置版本的操作就是一种浪费。
对于整数和指针类型来说,编译器可能对这种额外的工作进行了一定的优化;但是对于相对复杂的迭代器类型,这种额外的工作就消耗巨大。所以一定要养成使用前置版本的习惯
成员访问运算符
点运算符与箭头运算符的区别
-
点运算符只能用于类类型的对象!成员所属的对象是左值,那么结果是左值,反之如果是右值,那么结果是右值。
-
箭头运算符作用于一个指针类型的运算对象,将解引用和成员访问两个操作结合在一起,即
it->mem
和(*it).mem
左值就是能出现在等号左边的值,即能够通过地址(名字)访问的对象,右值即出现在等号右边的,即是创建的临时变量。
条件运算符
cond ? expr1 : expr2
finalgrade = (grade > 90) ? "high"
: (grade < 60) ? "fail" : "pass";
条件运算符的优先级非常低,与别的混用的时候一定要加括号
条件运算符满足右结合律(从右往左结合,但是判定还是从左往右看),即有多个条件运算符嵌套时,优先计算右边的条件运算符结果,如图中的(grade < 60) ? "fail" : "pass"
的结果
位运算符
值得注意的是:二进制位或者向左移或者向右移,移出边界之外的位就被舍弃掉了
移位运算符满足左结合律
cout << "hi" << "there" << endl;
((cout<<"hi") << "there") << endl;
移位运算符比算术运算符的优先级低,比关系运算符、赋值运算符和条件运算符的优先级高
cout << 42 + 10; //true
cout << (10 < 42); //true
cout << 10 < 42; //false 因为相当于比较cout和42的大小
sizeof运算符
sizeof (type)
sizeof expr
sizeof有两种用法,返回的是size_t类型的值。
因为sizeof满足右结合律并且与*
运算符的优先级一样,所以表达式按照从右向左的顺序组合。
sizeof *p == sizeof(*p) //指针p指向类型的内容大小(注意是类型)
sizeof p //指针p的所占空间大小
需要注意的是在sizeof中,解引一个无效指针仍然是一种安全行为,因为指针实际上并没有真正被使用
这里也可以看出,sizeof指向的对象不一定是具体的对象,只需要知道类型即可,因为获取大小与具体对象是什么并无关系。
sizeof作用于数组会得到整个数组的所占空间的大小,sizeof不会把数组转换成指针来处理
sizeof的对象是vector或string时,只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间
类型转换
一个比较普遍的转换方式是,短类型转成宽类型。
标签:Chapter,优先级,运算,对象,C++,运算符,sizeof,Primer,表达式 来源: https://www.cnblogs.com/dybala21/p/16525445.html