表达式
运算符可以作用于内置类型的运算对象,如果对象是类类型,也允许用户指定运算符的含义。
本章主要介绍了内置类型运算对象的运算符。也简单介绍几种标准库定义的运算符。
什么是表达式?
表达式是由一个或多个运算对象组成,对表达式求值得到一个结果。
一个运算符和一个或多个运算对象组合起来可以生成较复杂的表达式。
字面值和变量是最简单的表达式,结果就是字面值和变量的值。
1.基础
一元运算符、二元运算符、三元运算符……
函数调用也是一种运算符,它对运算对象的数量没有限制。
复合表达式是指含有两个或多个运算符的表达式。
优先级和结合律决定了运算对象组合的方式。
括号无视优先级和结合律。
求值顺序
int f = f1() * f2(); //不确定先调用 f1 还是 f2
int i = 0;
cout << i << ++i << endl; //输出结果不确定,可能是1 1, 0 1,等等
//因为不知道先求 i 还是先求 ++i
建议:处理复合表达式
1 拿不准优先级时用括号。
2 在表达式中改变了某个对象的值,在表达式的其他地方就不要再使用这个对象。
2.算术运算符
+ //一元正号
- //一元负号
* //乘法
/ //除法
% //求余
+ //加法
- //减法
布尔值不应参与运算
bool b = true; //b = 1
bool b2 = -b; //b2是true! b2 = -1,还是true
3.逻辑与关系运算符
关系运算符和逻辑运算符的返回值都是布尔类型。
! 逻辑非
< 小于
<= 小于等于
> 大于
>= 大于等于
== 相等
!= 不相等
&& 逻辑与
|| 逻辑或
4.赋值运算符
赋值运算符的左侧对象必须是一个可修改的左值。
int i = 0, j = 0, k = 0; //初始化而非赋值
const int ci = i; //初始化而非赋值
ci = k; //错误,ci 是常量
C++11新标准
k = {3.14};
vector<int> vi;
vi = {1, 2, 3, 4};
左侧运算对象是内置类型,初始值列表只能包含一个值。
左侧运算对象是类类型,赋值运算的细节由类本身决定。
赋值运算满足右结合律
int ival, jval;
ival = jval = 0;
jval = 0作为靠左赋值运算符的右侧对象。
赋值运算符优先级较低。
while ((i = get()) != 42)
i = get() 要加括号。
切勿混淆相等运算符和赋值运算符
if (i = j)
if (i == j)
编译器不好发现这种错误。
复合赋值运算符
+= -= *= /= %=
<<= >>= &= ^= |=
5.递增和递减运算符
递增和递减运算符还可以用于迭代器。
有两个版本:前置版本和后置版本。
建议:除非必须,否则不用递增递减的后置版本
前置版本把值加1后直接返回改变了的运算对象。
后置版本先将原始值存储下来以便于返回这个未修改的值。
6.成员访问运算符
点运算符(.)和箭头运算符(–>)
string s1 = "a string", *p = &s1;
auto n = s1.size();
n = (*p).size();
n = p->size();
7.条件运算符
条件运算符(?:)
cond? expr1 : expr2;
先求cond的值,如果条件为真对expr1求值并返回该值,否则对expr2求值并返回该值。
条件运算符可以嵌套。
条件运算符的优先级非常低,在长表达式中,通常需要在它两端加上括号。
8.位运算符
~ 位求反
<< 左移
>> 右移
& 位与
^ 位异或
| 位或
9.sizeof运算符
sizeof (type)
sizeof expr
10.逗号运算符
含有两个运算对象,按照从左到右的顺序依次求值。
11.类型转换
隐式转换
int ival = 3.541 + 3;
//3先转换成double类型,然后执行加法,得到结果是double类型。
//被初始化的对象的类型无法改变,由double转向int,忽略小数部分。
何时发生隐式转换?
- 比int类型小的整型首先提升为较大的整数类型
- 非布尔类型转换成布尔类型
- 初始化过程中,初始值转换成变量的类型;赋值语句中,右侧运算对象转换成左侧运算对象的类型。
- 算术运算或关系运算有多种类型,需要转换成同一种类型。
- 函数调用也会发生类型转换。
显式转换
形式如下:
cast-name<type>(expression);
cast-name 是 static_cast、 dynamic_cast、 const_cast、 reinterpret_cast中的一种
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
const_cast只能改变运算对象的底层const。就是去掉const属性,可以通过指针修改指向的值。
避免强制类型转换!!!