第四章 表达式
表达式由一个或多个运算对象组成,对表达式求值将得到一个结果。
1.基础
1.基本概念
一元运算符:作用于一个运算对象。如取址符和解引用符。
二元运算符:作用于两个运算对象。如==,<这些
函数调用也是特殊的运算符,对运算对象的数量没有限制。
重载运算符:IO库的<<,>>以及string对象,vector对象,迭代器使用的运算符都是重载的运算符。
2.求值顺序,优先级,结合律
可以利用括号改变运算顺序。
对于未指定运算顺序的运算符,如int i=f()*f1(),
我么不知道是先调用f还是f1。
这样的情况如果表达式指向并修改了同一个对象,将会产生错误。
cout<<i<<++i<<endl; //未定义的
&&,||,?:,和逗号,
运算符指定了顺序
2.算术运算符
注意,大多数运算符会把布尔类型提升为int类型。
bool b=true;
//运算时被提升为整数值1
bool b2=-b;
//-b是-1,将-1转换为布尔值作为b2的初始,初始值不为0,转换为布尔值是1,所以b2值为真。(p33类型转换,其他值转换为布尔类型,值为0则为false,否则为true)
整数相除还是整数,直接舍弃小数。
%的运算对象必须是整数。
m, n是整数且n不为0,则**(m/n)*n+m%n=m。**m%n的符号与m相同。
3.逻辑和关系运算符
返回值都是布尔类型。
&&和||都是先对左侧求值再求右侧,&&当且仅当左侧为真才求右侧,||当且仅当左侧为假才求右侧。
if(i<j<k
)其实是用i<j的布尔值和k作比较,显然不对,应该用逻辑与运算符写两个。
如果想测试一个算数运算对象或者指针对象的真值:
if(val) { } //val是任意非0值,条件为真
if(val==true) {} //如果val不是布尔值,比较前会将true转换为1,就变成了if(val==1),写法错误
4.赋值运算符
赋值运算符优先级较低,再条件语句中应该加括号。
5.递增和递减运算符
++和–不仅书写简单,还可以应用于迭代器例如*pebg++。
前置版本:++i,先把运算对象加一,将改变后的对象作为结果,将对象本身作为左值返回
后置版本:i++,加一后的值作为运算对象改变前的值的副本,将对象原始值的副本作为右值返回
int i=0,j;
j=++i; //j=1,i=1
j=i++; //j=1,i=2
尽量使用前置,因为后置需要储存原始值,对于复杂的迭代器类型消耗太大了。
6.成员访问运算符
点运算符和箭头运算符都可访问成员,ptr->mem等价于(*ptr).men。解引用优先级低于点运算符。
7.条件运算符(?:)
cond?expr1:expr2
cond为真对expr1求值并返回,否则对expr2求值
可以嵌套
a=(grade>90)?"high pass"
:(grade<60)?"fail":"pass"
输出表达式使用条件运算符,记得加最外层的括号
cout<<((grade<60)?"fail":"pass");
8.位运算符
**建议仅将位运算符用于处理无符号类型。**因为位运算符如何处理运算对象的符号位依赖于机器,而且左移操作可能改变符号位位置。
移位运算符:左移<<右移>>
求反运算符:~
位与&,位或|,异或^
9.sizeof运算符
sizeof返回一个表达式或者一个类型名字所占的字节数。所得的值是一个size_t类型的常量表达式。
**sizeof并不会实际计算其运算对象的值,**所以再sizeof运算对象中解引用一个无效指针依然安全。如sizeof *p。
sizeof(引用类型)得到被引用对象所占空间大小。
sizeof(指针)得到指针本身所占空间大小。
sizeof(解引用指针)得到指针指向的对象所占空间大小,指针不需要有效。
sizeof(数组)得到数组所占空间大小,注意不会将数组转换为指针处理。数组大小/单个元素大小=数组元素个数,constexpr size_t size=sizeof(arr)/sizeof(*arr)
sizeof(vector/string对象)只返回该类型固定部分的大小,不会计算对象中的元素占了多少空间。
10.类型转换
1.算术转换
运算符的运算对象会转换成最宽的类型。
整型提升:小整数类型转换为大整数类型。(例如bool, signed char, unsigned char, char, short,unsigned short会被提升为int)
2.其它隐式类型转换
数组自动转换成指向数组首元素的指针,数组被用作decltype关键字参数,或者取址符,sizeof,typeid等运算符的运算对象时,上述转换不会发生。同时用引用初始化数组时也不会发生。
算术类型或指针类型自动转换为布尔类型。
指向非常量的指针转换为指向相应常量类型的指针,引用也是。
3.显式转换
强制类型转换:cast_name <目标类型> (表达式)
static_cast:只要不包含底层const就可以
void* p=&d; //任何非常量对象的地址都可以存入void *
double *dp=static_cast<double*>(p); //将void* 类型转换为初始指针类型
**const_cast:**去const性质
reinterpret_cast
旧式类型转换:(目标转换类型)表达式
**尽量避免强制类型转换。