[C++ Primer] 表达式
-
左值和右值
当一个对象被用作右值时,用的是对象的值(内容);当对象被用作左值时,用的是对象的身份(在内存中的位置)。
几种熟悉的运算符要用到左值:
- 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果也是左值。
- 取地址符作用于一个左值运算对象,返回一个指向该运算对象的指针,该指针是右值。
- 内置解引用运算符*、下标运算符[ ]、迭代器解引用运算符、string和vector的下标运算符[ ]的求值结果都是左值。
- 内置类型和迭代器的递增递减运算符作用于左值运算对象,所得结果也是左值。
-
4种运算符明确规定了运算对象的求值顺序:
- 逻辑与运算符&&
- 逻辑或运算符||
- 条件运算符 ?:
- 逗号运算符(,)
-
赋值运算符的左侧运算对象必须是一个可修改的左值。
-
递增递减运算符
-
前置版本++i :首先将运算对象加1,然后将改变后的对象作为求值结果。
-
后置版本i++ :将运算对象加1,求值结果是运算对象改变之前那个值的副本。
int i = 0, j; j = ++i; // j = 1, i = 1 :前置版本得到递增之后的值 j = i++; // j = 1, i = 2 :后置版本得到递增之前的值
这两种运算符必须作用于左值运算对象。前置将对象本身作为左值返回,后置将对象原始值的副本作为右值返回。
建议:尽量使用前置++i,除非必须,否则不用后置版本 -
-
箭头运算符作用于一个指针类型的运算对象,结果是一个左值。点运算符分两种情况:若成员所属的对象是左值,结果为左值,若成员所属的对象是右值,结果为右值。
-
sizeof
运算符sizeof
运算符返回一条表达式或一个类型名字所占的字节数。满足右结合律,所得的值是一个size_t类型的常量表达式。运算符的运算对象有两种形式:sizeof (type) sizeof expr
第二种形式中,返回的是表达式结果类型的大小,并不计算其运算对象的值。
-
逗号运算符
含有两个运算对象,按照从左到右顺序依次求值,首先对左侧表达式求值,然后将求值结果丢弃掉,逗号运算符真正的结果是右侧表达式的值。若右侧运算对象是左值,则最终求值结果也是左值。
-
隐式转换
数组转换为指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:
int ia[10]; int *ip = ia; // ia转换成指向数组首元素的指针
不会发生转换的情况:当数组被用作
decltype
关键字的参数,或者作为取地址符&
、sizeof
及typeid
等运算符的运算对象时,用一个引用来初始化数组时。 -
命名的强制类型转换
形式:
cast-name<type>(expression);
type
时转换的目标类型,expression是要转换的值。若type
是引用类型,则结果是左值。cast-name
是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一种。-
static_cast
任何具有明确定义的类型转换,只要不包含底层const,就可以使用static_cast。 -
const_cast
const_cast只能改变运算对象的底层const。const char *pc; char *p = const_cast<char*>(pc); // 正确:但是通过p写值是未定义的行为
-
reinterpret_cast
通常为运算对象的位模式提供较低层次上的重新解释。 -
dynamic_cast
支持运行时类型识别
-
-
旧式的强制类型转换
type(expr); // 函数形式的强制类型转换 (type)expr; // C语言风格的强制类型转换
与命名的强制类型转换相比,旧式的从表现形式上来说不那么清晰明了,容易被看漏,一旦转换过程出现问题,追踪起来困难。
-
C++运算符优先级表