概括
4.1. 运算对象、运算符以及表达式等基本概念;左值和右值;运算符的优先级和结合律以及括号的影响;求值顺序不确定引起的未定义行为;
4.2. 算术运算符的溢出问题;除法(/
)和求余(%
)的结果符号问题;
4.3. 逻辑运算符用在条件语句中;关系运算符涉及布尔类型时再使用ture
和false
;
4.4. 赋值运算符满足右结合律,而且优先级较低;分清楚=
(赋值运算符)和==
(相等,属于关系运算符);复合赋值运算符的含义;
4.5. 递增和递减分为前置和后置,除非必须,否则不使用后置版本;由于会改变对象的值,递增和递减可能引起未定义行为;
4.6. 成员访问的两种方式:点运算符和箭头运算符;
4.7. 条件运算符满足右结合律,可以嵌套使用但是最好不要超过两到三层;条件运算符优先级非常低,应该添加必要的括号以避免出现问题;
4.8. 位运算符作用对象为整数类型:移位运算符中涉及到的带符号问题的处理依赖于实际环境;位求反会首先对小整数类型进行提升;使用位运算符进行计算;移位运算符的重载运算符的优先级和结合律和其内置版本一致;
4.9. sizeof
运算符:返回size_t
类型;不会实际计算运算对象的值;因为sizeof
运算不会把数组转换成指针进行处理,我们可以利用其计算数组的大小(参见『C++ Primer学习笔记』Chapter 3——string对象、vector对象以及数组的不同处理方法中使用下标法处理数组的例子);
4.10. 逗号运算符的优先级和含义;
4.11. 隐式转换的各种情况;整型提升;无符号类型的转换问题;显式转换的使用;
左值和右值
当一个对象被用作右值的时候,用的是对象的值(内容);
当一个对象被用作左值的时候,用的是对象的身份(在内存中的位置);
一个重要的原则是在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。
除法(/)和求余(%)的结果符号问题
除法
如果两个运算对象的符号相同则商为正(如果不为0的话),否则商为负;C++新标准规定商一律向0取整(即直接切除小数部分)。
21 / 6 = 3; //向0取整
21 / 7 = 3;
-21 / -8 = 2;
21 / -5 = -4; //向0取整
求余
如果m%n
不等于0,那么结果的符号和m
相同:
21 % 6 = 3;
21 % 7 = 0;
-21 % -8 = -5; //结果的符号和m相同
21 % -5 = 1; //结果的符号和m相同
求值顺序确定的情况
由于优先级只规定了运算对象的组合方式,但是并没有说明运算对象按照什么顺序求值,当涉及到不同运算对象的求解顺序时,往往会产生未定义的行为:
int i = 0;
cout<<i<<" "<<++i<<endl; //未定义的
虽然很多运算符并没有指定执行顺序,但是C++中仍然有4种运算符明确规定了运算对象的求值顺序,它们分别是:逻辑与&&
、逻辑或||
、条件运算符?:
和逗号运算符,
。前两个运算符采用的是短路求值策略。
无符号类型的转换问题
在之前的博文『C++ Primer学习笔记』Chapter 2中,我们讨论了无符号类型的类型转换问题。
其中对无符号类型变量用带符号类型进行赋值的处理方式仍然为其中描述的方法;而针对包含有带符号类型和无符号类型的表达式来说,我们之前描述的只是其中一种情况。
对包含有带符号类型和无符号类型的表达式涉及到的类型转换问题,主要分为两种情况:
无符号类型不小于带符号类型,那么带符号类型转换成无符号类型,所用的方法仍然为『C++ Primer学习笔记』Chapter 2中所说的方法;
带符号类型大于无符号类型,此时依赖于机器:如果无符号类型的所有值都能存在该带符号类型中,则无符号类型转换成带符号类型;如果不能,那么带符号类型的运算对象转换成无符号类型;
2017-09-13更新说明:
实际上,由于C++只规定了内置类型的最小尺寸,因此采用符号类型比较的情况“带符号类型大于无符号类型”时,也会出现两种情况,这是因为类型大小不绝对等于类型所占尺寸大小,所以根据类型所占尺寸来分可能更好。
显式转换
在编译期间实现显示转换的类型转换函数有三种,参考其他的文献12可以将其作用概括如下:
static_cast
主要用于基本类型和具有继承关系的类型之间的转换,只要不包含底层const
,因为这种转换一般不会改变变量的内部表示方式,因此使用static_cast
转换指针没有太大意义,而且效率也不如reinterpret_cast
高。
const_cast
用于去除指针变量的常量属性,将它转换为一个对应指针类型的普通变量;反过来,也可以 将一个非常量的指针变量转换为常量指针变量。 不能将非指针的常量变量转换为普通变量,也无法将非指针的普通变量转换为常量变量。
reinterpret_cast
主要用于实现不同类型的指针之间转换,这种转换不会修改原来指针变量值,相当于把地址赋给新的指针变量,在编译期间使用新的指针变量时进行重新解释。reinterpret_cast
也可以将指针值转换为一个整型数,但是不能用于非指针类型的转换。