概述
C++ Primer 第四章 表达式(一)
- 4.1 基本概念
- 4.2 算术运算符
- 4.3 逻辑和关系运算符
- 4.4 赋值运算符
- 复合赋值运算符
- 4.5 递增和递减运算符
- 4.6 成员访问运算符
- 条件运算符
- 嵌套条件运算符
- 在输出表达式中使用条件运算符
- 位运算符
- 移位运算符
- 位求反运算符
- 位与(`&`)、位或(`|`)、位异或(`^`)运算符
4.1 基本概念
左值与右值:左值能够位于赋值语句的左侧,右值则不能。
4.2 算术运算符
- 除法运算符
/
整数相除的结果还是整数,也就是说,如果商含有小数部分,直接弃除。C++11新标准规定,商一律向0取整,即直接切除小数部分。在除法运算中,如果两个运算对象符号相同则商为正(如果不为0的话),否则商为负。 - 取余(取模)运算符
%
根据取余运算的定义,如果m和n是整数且n非0,则表达式(m/n)*n + (m%n)
的求值结果与m相等。隐含的意思是,如果m % n
不等于0,则它的符号与m相同。 - 溢出
当计算结果超出该类型所能表示的范围时就会发生溢出。
4.3 逻辑和关系运算符
- 短路求值(short-circuit evaluation)策略
逻辑与运算符&&
和逻辑或运算符||
,都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的求值结果时才会计算右侧运算对象的值。 - 一个使用逻辑或运算符的例子
假定有一个存储着若干string对象的vector对象,要求输出string对象的内容并且遇到空字符串或者以句号结束的字符串时进行换行。使用基于范围的for循环处理string对象的每个元素:
//s是对常量的引用;元素没有被拷贝也不会被改变
for(const auto &s : text){
//对于text的每个元素
cout << s;
//输出当前元素
//遇到空字符串或者以句号结束的字符串进行换行
if(s.empty() || s[s.size() - 1] == '.')
cout << endl;
else
cout << " ";
//否则用空格隔开
}
值得注意的是,s被声明成了对常量的引用。因为text的元素是string对象,可能非常大,所以将s声明成引用可以避免对元素的拷贝;又因为不需要对string对象做写操作,所以s声明成对常量的引用。
4.4 赋值运算符
赋值运算符的左侧对象必须是一个可修改的左值。
- 初始化和赋值不要搞混了。
如果赋值运算符左右两个运算对象的类型不同,则右侧运算对象将转换成左侧运算对象的类型。
C++11新标准允许使用花括号括起来的初始值列表作为赋值语句的右侧运算对象。
- 赋值语句满足右结合律,这一点与其他的二元运算符不太一样。
- 赋值运算符的优先级较低
因为赋值运算符的优先级低于关系运算符的优先级,所以在条件语句中,赋值部分通常应该加上括号。
复合赋值运算符
我们经常需要对对象施以某种运算,然后再把计算的结果赋值给该对象。
运算符的复合赋值形式:
- 算术运算符的复合赋值形式:
+=
,-=
,*=
,/=
,%=
- 位运算符的复合赋值形式:
<<=
,>>=
,&=
,^=
,|=
任意一种复合赋值运算符都完全等价于:a = a op b
唯一的区别是左侧运算对象的求值次数:使用复合运算符只求值一次,使用普通运算符则求值两次。这两次包括:一次作为右边子表达式的一部分求值,另一次是作为赋值运算的左侧对象求值。其实在很多地方,这种区别除了对程序的性能有些许影响外几乎可以忽略不计。
4.5 递增和递减运算符
递增和递减运算符有两种形式:前置版本和后置版本。
- 前置版本:运算符首先将运算对象加1(或减1),然后将改变后的对象作为求值结果。
- 后置版本:后置版本也会将运算对象加1(或减1),但是求值结果是运算对象改变之前的那个值的副本。
这两种运算符必须作用于左值对象。前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。
建议:除非必须,否则不要使用递增运算符的后置版本
前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。
对于整数和指针类型来说,编译器可能对这种额外的工作进行一定的优化;但是对于相对复杂的迭代器类型,这种额外的工作就消耗巨大了。建议养成使用前置版本的习惯,这样不仅不需要担心性能的问题,而且更重要的是写出的代码会更符合编程的初衷。
如果想在一条复合表达式中既将变量加1或减1又能使用它原来的值,这时就可以使用递增和递减运算符的后置版本。
例子:可以使用后置版本的递增运算符来控制循环输出一个vector对象内容直至遇到(但不包括)第一个负值为止:
auto pbeg = v.begin();
//输出元素直至遇到第一个负值为止
while(pbeg != v.end() && *pbeg >= 0)
cout << *pbeg++ << endl; //输出当前值并将pbeg向前移动一个元素
后置递增运算符的优先级高于解引用运算符,因此*pbeg++
等价于*(pbeg++)
。pbeg++
把pbeg的值加1,然后返回pbeg的初始值的副本作为其求值结果,此时解引用运算符的运算对象是pbeg未增加之前的值。最终,这条语句输出pbeg开始时指向的那个元素,并将指针向前移动一个位置。
建议:简洁可以成为一种美德
形如*pbeg++
的表达式一开始可能不太容易理解,但其实这是一种被广泛使用的、有效的写法。当对这种形式熟悉之后,书写
cout << *iter++ << endl;
要比书写下面的语句更简洁、也更少出错
cout << *iter << endl;
++iter;
不断研究这样的例子,直到对它们的含义一目了然。大多数C++程序追求简洁,摒弃冗长,因此C++程序员应该习惯于这种写法。而且,一旦掌握了这种写法后,程序出错的可能性也会降低。
4.6 成员访问运算符
点运算符和箭头运算符都可以用于访问成员,其中,点运算符获取类对象的一个成员;箭头运算符与点运算符有关,表达式ptr -> mem
等价于(*ptr).men
。
解引用运算符的优先级低于点运算符,所以要加括号。
箭头运算符作用于一个指针类型的运算对象,结果是一个左值。点运算符分成两种情况:如果如果成员所属的对象是左值,那么结果是左值;反之,如果成员所属的对象是右值,那么结果是右值。
条件运算符
条件运算符(? :
),允许我们把简单的if-else逻辑嵌入到单个表达式中,条件运算符按照如下形式使用:
cond ? expr1 : expr2;
其中cond
是判断条件的表达式,而expr1
和expr2
是两个相同类型或可能转换为某个公共类型的表达式。条件运算符的执行过程是:首先求cond
的值,如果条件为真对expr1
求值并返回该值。否则对expr2
求值并返回该值。条件运算符只对expr1
和expr2
中的一个求值。
当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果是左值;否则是右值。
嵌套条件运算符
条件运算符可以作为另一个条件运算符的cond
或expr
。
例子:使用一对嵌套的条件运算符可以将成绩分为三档:优秀(high pass)、合格(pass)和不合格(fail):
finalgrade = (grade > 90) ? "high pass"
: (grade < 60) ? "fail" :"pass";
随着条件运算嵌套层数的增加,代码的可读性急剧下降。因此,条件运算符的嵌套最好别超过两到三层。
在输出表达式中使用条件运算符
条件运算符的优先级非常低,因此当一条长表达式中嵌套了条件运算子表达式时,通常需要在它的两端加上括号。
位运算符
位运算符作用于整数类型的运算对象,并把运算对象看成二进制位的集合。位运算符提供检查和设置二进制位的功能。
位运算符(左结合律)优先级排序:
- 位求反
~
- 左移
<<
,右移>>
- 位与
&
- 位异或
^
- 位或
|
一般来说,如果运算对象是“小整型”,则它的值会被自动提升成较大的整数类型。运算对象可以是带符号的,也可以是无符号的。如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖于机器。而且,此时的左移操作可能会改变符号位的值,因此是一种未定义的行为。
关于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型。
移位运算符
对其运算对象执行基于二进制位的移动操作,首先令左侧运算对象的内容按照右侧运算对象的要求移动指定位数,然后经过移动的左侧运算对象的拷贝作为求值结果。其中,右侧的运算对象一定不能为负,而且值必须严格小于结果的位数,否则就会产生未定义的行为。
二进制位或者向左移(<<
)或者向右移(>>
),移出边界之外的位就被舍弃掉了。
左移运算符(<<
)在右侧插入值为0的二进制位。
右移运算符(>>
)的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位;如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定。
位求反运算符
位求反运算符(~
)将运算对象逐位求反后生成一个新值,将1置为0,将0置为1。
位与(&
)、位或(|
)、位异或(^
)运算符
运算符在两个运算对象上逐位执行相应的逻辑操作:
- 与:全1为1,否则为0.
- 或:有1即1,全0为0.
- 异或:不同为1,相同为0.
最后
以上就是单薄鸡翅为你收集整理的C++ Primer 学习笔记 第四章 表达式的全部内容,希望文章能够帮你解决C++ Primer 学习笔记 第四章 表达式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复