4.1 基础
- 函数调用也是一种特殊的运算符,它对运算对象的数量没有限制。
- C++ 的表达式要么是左值,要么是右值。左值可以位于赋值语句的左边,右值则不可以。
- 当一个对象被用作右值的时候,用的是对象的值;当对象被用作左值的时候,用的是对象的身份。
【mark】
- 优先级规定了运算对象的组合方式,但是没有说明运算对象的求值顺序,一般不会明确指定求值顺序。
- 对于没有指定执行顺序的运算符,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。
cout << i << ++i << endl; // 未定义的
4.2 算术运算符
- 算术运算符的运算对象和求值结果都是右值。
- 在表达式求值之前,小整数类型的运算对象被提升成较大的整数类型,所有运算对象最终会转换成同一类型。
- 一元正(负)号运算符作用于一个指针或者算数值时,返回运算对象(取负)的提升后的副本。
bool b = true;
bool b2 = -b; // b2 is true
-
m = (m / n) * n + m % n
,则 m % n
符号与 m
相同。
4.3 逻辑和关系运算符
- 逻辑和关系运算符的运算对象以及求值结果都是右值。
- 相等性测试与布尔字面值
if (val) {} // 如果 val 是任意的非 0 值
if (!val) {} // 如果 val 是 0
if (val == true) // 如果 val 等于 1
4.4 赋值运算符
int i = 0, j = 0, k = 0; // 初始化而非赋值
const int ci = i; // 初始化而非赋值
1024 = k; // 错误 字面值是右值
i + j = k; // 错误 算术表达式是右值
ci = k; // 错误 ci 是常量左值
- 赋值运算的结果是它的左侧对象,并且是一个左值,结果的类型是左侧运算对象的类型。
- 赋值运算符满足右结合律。
4.5 递增和递减运算符
- 递增和递减运算符有两种形式:前置版本
++i
和后置版本 i++
。这两种运算符必须作用于左值运算对象,前置版本将对象本身作为左值返回,后置版本将对象原始值的副本作为右值返回。
- 除非必须,否则不用递增和递减运算符的后置版本。
4.6 成员访问运算符
4.7 条件运算符
4.8 位运算符
- 位运算符的运算对象如果是“小整型”,则会被自动提升为整数类型。
- 位运算符如何处理带符号数的符号位是未定义的,因此建议仅将位运算符用于处理无符号数。
- 移位运算符(IO运算符)满足左结合律。
sizeof 运算符
-
sizeof
运算符返回一条表达式或者一个类型名字所占的字节数,返回 size_t
类型的常量表达式。
sizeof (type)
sizeof expr
-
sizeof
运算符满足右结合律。
- 在
sizeof
的运算对象中解引用一个无效指针是安全的,因为指针没有被真正使用。
- 对数组执行
sizeof
运算得到整个数组的大小,对 sting 或 vector 执行 sizeof
运算只返回该类型固定部分的大小。
4.10 逗号运算符
- 逗号运算符先对左侧的表达式求值,然后将结果丢弃掉,再对右侧的表达式求值,返回右侧的结果。
4.11 类型转换
- 算术转换:运算符的运算对象将转换成最宽的类型。
- 整型提升:转换后的类型要容纳原类型所有可能的值。
- 如果一个运算对象是无符号类型,另一个是带符号类型:
无符号类型不小于带符号类型:带符号类型转成无符号类型;
带符号类型大于无符号类型:转换结果依赖于机器;
- 指针的转换
常量整数值 0 或者字面值 nullptr 能转换成任意指针类型;
指向任意非常量的指针能转成 void;
指向任意对象的指针能转成 const void;
- 转换成布尔类型:指针或算术值为 0,结果为 false,否则为 true。
- 转换成常量:允许将指向非常量的指针(引用)转换成相应的常量指针(引用)。
- 类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一次类类型的转换。
- 强制转换
cast-name <type> (expression)
// 把一个较大的算术类型赋值给较小的类型。
double slope = static_cast<double>(j) / i;
// 找回存在于 void* 指针的值
double *p = static_cast<double*>(p);
// 改变运算对象的底层 const
const char *pc;
char *p = const_cast<char*>(pc);
// todo