今天继续学习,c++ primer第五版第四章:表达式。
其实这部分东西大家应该都很熟悉,毕竟大家应该都学过c,那么学习起来就比较快了,只需要注意下c++11下有没有什么新的东西。
不过既然是总结整理,我们还是老老实实地一字一字地看过去吧。
表达式是若干个运算对象组成的。字面值和变量是简单的表达式。
运算符:一元,二元,三元(?:)。结合律也是一个重点。
运算符的重载:重新赋予运算符新的含义,可以改变运算对象的类型和返回值的类型,不能改变运算对象的个数,运算符的优先级,结合律。
左值和右值
c语言中指:可以写在赋值号左边(左值)和不可以在左边的值(右值)。C++:左值的结果是一个对象或函数,是使用对象的身份(内存的位置);右值的结果它的值而不是对象本身。表达式的非常量左值可用在赋值的左侧。
取地址符作用于左值对象,返回一个指针,这个指针是右值;解引用,下标,迭代器解引用,string和vector的解引用求值结果都是左值;递增递减符(++,--)得到结果也是左值。
decltype作用于一个表达式,若其结果是左值,返回一个引用类型,如:int *p;decltype(*p)->int &,decltype(p)->int *,decltype(&p)->int **。
优先级
具体优先级情况见于书147页,建议背过,因为碰到这种东西还查书感觉很浪费时间,而且,也很重要。
Tip,遇到不确定的就加个括号,方便下次读写。
求值顺序
大多数情况运算符并没有明确的求值顺序,比如输出符<<,如:cout<并不知道是先求++i还是i的值,比如vs2013下就是先求++i的值。
Tip,改变过运算对象的值后,建议表达式的其他地方不要使用此运算对象,除非此运算对象是一个子表达式,如*++iter,是一个子表达式。
1.算术运算符
普通运算符优先级:+-(正负)>*/%(乘除求模)>+-(加减)。对整数有几个有趣的运算规则:(m/n)*n+m%n=m;(-m)/n=m/(-n)=-(m/n),m%(-n)=m%n,(-m)%n=-(m%n)。
2.逻辑,关系运算符
&&,||,都是有明确求值顺序的,即先求左边的表达式再求右边的,而且若左边成立(&&:0,||:1)则不求右边的。一个补充:在上一章讲范围for语句的时候常对字符串使用引用,如:for(auto &s:line){...},之所以使用引用是因为字符串可能非常长,不使用拷贝可以避免对内存的过度占用。
逻辑非!返回的是布尔值。关系运算符都返回布尔值。
C++中不能连续比较大小,如x
3.赋值运算符
没什么要讲的,注意一点:判断的时候一般是使用等号(==)不要错用为赋值号(=)。
4.递增递减符
本身没有什么可讲的,直接说tip。Tip,本书建议除了必须使用后置递增(递减)外,都使用前置,因为后置需要存储未改变的值和已经改变的值,在迭代器中消耗很大。
5.成员访问运算符
点和箭头,箭头运算符:1.对象是一个类(如vector),2.自身是一个指针。
6.条件运算符
左侧为满足条件,右侧为不满足。满足右结合律。意义:嵌套使用时右侧构成一个分支,如:(grade>90)?”high pass”:(grade<60)?”fail”:”pass”这个表达式,因为满足右结合,所以(grade<60)?”fail”:”pass”才可以成为一个有效分支。
7.位运算符
有:~,<<,>>,&,^,|。其中^为按位异或。
移位运算最好对unsigned执行以免产生不理想的结果。移位运算符应该严格小于一个类型的位数。如int为32位,则<<,>>最多只能移31位。否则未定义(不过csapp中说是使用此位数对最大位求模取余,而且vs2013也支持这个说法)。
位运算会将小整型提升为int整形,即位数补全后计算。
类型转换
本人认为这是比较重要的一小节。因为类型,尤其是复杂类型的问题经常导致程序的崩溃和编译不通过。
隐式转换。
1.类型提升。一个表达式需要对几个不同类型的数据做运算时一般都会提升至最高一级,而且是逐级提升,如:double+float*int,先int->float,再float->double。
2.整形提升。小整型做运算时一般都提升至整形,包括bool,char,signed char,unsigned short,short只要int存的下都会转为int,否则转化为unsigned int。大整形如wchar_t, char16_t, char32_t,会转为int至unsigned long long中最小的。
3.有符号和无符号一起运算会转化为无符号,所以负数会转化为不理想的值,其按照规则“求模取余”进行。
4.其他,如数组名转化为指针等。
显示转换
本人认为是c++中很有意义的东西。不同于c的直接转化(type)expr,c++分为4种:
1.static_cast:一般转换,但不能对底层const使用。
2.const_cast:专门对底层const使用的转换,使const变为变量。回忆一下底层const:一般使用于复杂类型,如指针和引用。如:const *int p;p可以被改变,但必须有同样的const资格,举个例子,const int x=1;p=&x;是对的,但是int y=1;p=&y;是错的。不过顶层const是可以的。
3.reinterpret_cast:为运算对象的位模式提供较低层次的重新解释。