表达式 expression 由一个或者多个运算对象operand组成
4.1 基础
重载运算符 overloaded operator 当运算符作用于类类型的运算对象时,用户可以自定义其含义,自定义的过程相当于为已存在的运算符赋予了另外一种含义,所以成为重载运算符。
表达式分为左值和右值,右值不能在赋值语句中的左侧。
c++中,对象被用作左值的时候用的是他的位置,用作右值的时候是用的内容(值)。右值不能替代左值。
要注意,如果在改变了某个运算对象的值,在表达式的其他地方不要使用这个运算对象。
4.2 算数运算符
m%n// 求余数 参与运算的必须是整数,且其符号与m相同
42 % 3.12;//错误!
//m%(-n)等于m%n
//(-m)%n等于-(m%n)
//除法运算中一律向0取整!!!也就是直接切除小数部分
算数运算符的运算对象和求值结果都是右值
但是要注意布尔值不参与运算
bool b=true;
bool b2=-b; //b2是true!!!因为首先运算时会被提升为1,1取负值后的结果是-1,所以得到的结果依然是true
4.3逻辑和关系运算符
逻辑与或者逻辑或运算符只有当左侧不能确定结果时才会计算右侧的运算结果,所以有时左侧运算是为了确保右侧的运算对象求值过程中的正确性和安全性
4.4 赋值运算
注意初始化不是赋值,c++11中允许使用初始值列表作为赋值语句的运算对象,但是初始化列表会窄化转换,更加严格
int k=0;
k=3.14;//right k=3
k={3.14};// 错误! 窄化转换
//赋值运算满足右结合律
int ival,*pval;
ival=pval=0;//ival不能被赋于指针的值(int*不能被转换为int),尽管0可以被赋值给任何对象但是这里仍然是非法的
另外,赋值运算的优先级较低,一般先加括号
4.5 递增或者递减运算
int i=0,j;
j=++i;//j=1.i=1
j=i++;//j=1;i=2 注意哦!所以建议,除非必须,不要使用后置版本
这里插播一条广告
while (beg!=s.end() && !isspace(*beg)
*beg = toupper(*beg++);// 错误, 该赋值语句未定义,两端都用到了beg,右侧运算对象还改变了beg的值!注意运算对象可按任意顺序求值
4.6 成员访问运算符
箭头运算符与点运算符都可以访问成员
ptr->men等价于(*ptr).men//注意加括号哦 箭头作用于一个指针类型的运算对象,返回的是一个左值左值左值左值左值,但是点运算取决于成员的属性
4.7 条件运算符
?:
cond?expr1:expr2;//首先如果cond为真,求expr1否则expr2
//也可以嵌套,嵌套的时候书写顺序不需要用括号,但是可读性受到了影响
finalgrade=(grade>90)?"high pass"
:(grade>60)?"fail":"pase";
4.8 位运算
<<在之前io库中的重载版本已经看过了,这里时位移运算符
另外还有 位与 位或 位异或 (^两个对象的对应位置只有一个为1,则为1)
注意区别
& &&
| ||
~ !
使用位运算符时要善于使用&=及|=
1UL<<27 //注意这里UL表示无符号长整型,有32位,位移之后得到的值的第27位为1
4.9 sizeof
sizeof (type)
sizeof expr
返回表达式或者一个类型名字所占的字节数,返回类型时size_t
sizeof(ia)/size(*ia)//返回数组ia的数量
4.11 类型转换
尽量不要使用强制转换
4.11.1 算数转换 arithmetic conversion
整型提升 intergral promotion
首先进行整型提升,比如char转换为int
然后如果无符号类型不小于有符号类型就会转化为无符号类型,并且会有副作用的可能。
如果无符号的小于有符号的,且无符号的可以储存再带符号中那么就会转换成有符号类型,如果不能就转化为无符号类型、
4.11.2 其他隐式类型
int ia[10];
int * ip=ia;//ia 转换成指向首元素的指针
while (*cp){}//如果*cp不是空字符,为真
if (cp)//如果cp不是0,为真
4.11.3 显式转换
int i,j;
double slope =i/j; 也叫做cast 强制类型转换
这种方法本质上是十分危险的
另外还有命名的强制类型转换 cast-name<type>(expression)