C++ primer 4章 表达式
第四章:表达式
表达式基础
作用于一个运算对象的运算符叫一元运算符,如
&
,*
;作用于两个运算对象的运算符叫二元运算符,如==
,*
;运算对象转换:小整数类型会被提升为较大的整数类型
重载运算符:当运算符作用在类类型的运算对象时,用户可以自行定义其含义。
左值和右值:
- C中原意:左值可以在表达式左边,右值不能。
C++
:当一个对象被用作右值的时候,用的是对象的值(内容);- 被用做左值时,用的是对象的身份(在内存中的位置)。
求值顺序:
1
int i = f1() + f2()
- 先计算
f1() + f2()
,再计算int i = f1() + f2()
。但是f1和f2的计算先后不确定 - 但是,如果f1、f2都对同一对象进行了修改,因为顺序不确定,所以会编译出错,显示未定义
- 先计算
算术运算符
溢出:当计算的结果超出该类型所能表示的范围时就会产生溢出。
bool类型不应该参与计算
1
2
3
4bool b=true;
bool b2=-b; //仍然为true
//b为true,提升为对应int=1,-b=-1
//b2=-1≠0,所以b2仍未true取余运算m%n,结果符号与m相同
逻辑运算符
- 短路求值:逻辑与运算符和逻辑或运算符都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。先左再右,其实就是要是左侧的直接不满足条件就不继续往下看了,这样运算速度更快
- 小技巧,声明为引用类型可以避免对元素的拷贝,如下,如
string
特别大时可以节省大量时间。
1 | vector<string> text; |
赋值运算符
赋值运算的返回结果是它的左侧运算对象,且是一个左值。类型也就是左侧对象的类型。
如果赋值运算的左右侧运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型。
比如
int k = 3.14
,实际上k = 3;
赋值运算符满足右结合律,这点和其他二元运算符不一样。
ival = jval = 0;
等价于ival = (jval = 0);
赋值运算优先级比较低,使用其当条件时应该加括号。
复合赋值运算符,复合运算符只求值一次,普通运算符求值两次。(对性能有一点点影响) 任意复合运算符op等价于
a = a op b;
递增递减运算符
- 前置版本
j = ++i
,先加一后赋值 - 后置版本
j = i++
,先赋值后加一
优先使用前置版本,后置多一步储存原始值。(除非需要变化前的值)
混用解引用和递增运算符
*iter++
等价于*(iter++)
,递增优先级较高
1 | auto iter = vi.begin(); |
简介是一种美德,追求简洁能降低程序出错可能性
成员访问运算符
ptr->mem
等价于(*ptr).mem
注意.
运算符优先级大于*
,所以记得加括号
条件运算符
条件运算符(
?:
)允许我们把简单的if-else
逻辑嵌入到单个表达式中去,按照如下形式:cond? expr1: expr2
可以嵌套使用,右结合律,从右向左顺序组合
- ```cpp
finalgrade = (grade > 90) ? “high pass”
//等价于: (grade < 60) ? "fail" : "pass";
finalgrade = (grade > 90) ? “high pass”: ((grade < 60) ? "fail" : "pass");
1
2
3
4
5
6
7
- 输出表达式使用条件运算符记得加括号,条件运算符优先级太低。
```cpp
string s = "word";
string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;//错误,加法的优先级高于条件运算符
string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;//正确
- ```cpp
位运算符
用于检查和设置二进制位的功能。
- 位运算符是作用于整数类型的运算对象。
- 二进制位向左移(
<<
)或者向右移(>>
),移出边界外的位就被舍弃掉了。 - 位取反(
~
)(逐位求反)、按位与(&
)、或(|
)、异或(^
)
有符号数负值可能移位后变号,所以强烈建议位运算符仅用于无符号数。
应用:
1 | unsigned long quiz1 = 0; // 每一位代表一个学生是否通过考试 |
位运算符使用较少,但是重载cout、cin用过
位运算符满足左结合律,优先级介于中间,使用时尽量加括号。
一个字面值非零就为真,否则为假;
sizeof运算符
- 返回一条表达式或一个类型名字所占的字节数。
- 返回的类型是
size_t
的常量表达式。 sizeof
并不实际计算其运算对象的值。- 两种形式:
sizeof (type)
,给出类型名sizeof expr
,给出表达式
- 可用
sizeof
返回数组的大小
1 | int ia[10]; |
常见数据类型的所占字节数
1 | bool: 1 bytes |
逗号运算符
从左向右依次求值,逗号表达式的优先级是最低的。
左侧求值结果丢弃,逗号运算符结果是右侧表达式的值。
类型转换
隐式类型转换
设计为尽可能避免损失精度,即转换为更精细类型。
- 比
int
类型小的整数值先提升为较大的整数类型。 - 条件中,非布尔转换成布尔。
- 初始化中,初始值转换成变量的类型。
- 算术运算或者关系运算的运算对象有多种类型,要转换成同一种类型。
- 函数调用时也会有转换。
算术转换
整型提升:将小整数类型转换为较大的整数类型。
- 常见的char、bool、short能存在int就会转换成int,否则提升为
unsigned int
wchar_t,char16_t,char32_t
提升为整型中int,long,long long ……
最小的,且能容纳原类型所有可能值的类型。- int转换为float,float转换为double;
其他转换
p143
显式类型转换(尽量避免)
static_cast:任何明确定义的类型转换,只要不包含底层const,都可以使用。
double slope = static_cast<double>(j);
dynamic_cast:支持运行时类型识别。
const_cast:只能改变运算对象的底层const,一般可用于去除const性质。
const char *pc; char *p = const_cast<char*>(pc)
只有其可以改变常量属性
reinterpret_cast:通常为运算对象的位模式提供低层次上的重新解释。
旧式强制类型转换
1 | type ex |