5.1算数操作符
如果两个操作数都为正数,触发(/)和求模(%)操作结果也是正数;如果两个操作数都是负数,除法结果为正数,求模结果为负数;如果一个为负数,这两种操作的结果取决于机器;求模结果的符号取决于机器,而除法操作的值则是负数
当有一个操作数为负数时,求模操作结果值得符号可依据分子或分母的符号而定。如果求模的结果跟随分子的符号,则除出来的值向零一侧取整;如果求模与分母的符号匹配,则除出来的值向负无穷一侧取整
5.2关系操作符和逻辑操作符
1.逻辑与、逻辑或操作符
expr1&&expr2
expr1||expr2
仅当expr1不能确定表达式的值时,才会求解expr2:
在逻辑与表达式中,expr1的计算结果为true。如果expr1的值为false,则无论expr2的值是什么,逻辑与的值都为false。
在逻辑或表达式中,expr1的计算结果为false。如果expr1的值为false,才会计算expr2的值是否为true
只有在仅靠左操作数无法确定该逻辑表达式的结果时,才会求解其右操作数。我们称为短路求值。
5.3.2 将移位操作符用于IO
IO操作符为左结合
IO操作符具有中等优先级:其优先级比算术操作符低,但比关系操作符、赋值操作符和条件操作符优先级高。
5.4赋值操作符
赋值操作符的左操作数必须是非const的左值。
数组名是不可修改的左值:因此数组名不可做赋值操作的目标。
赋值表达式的值是其左操作数的值,其结果的类型为左操作数的类型
赋值操作符是具有右结合性的
5.8 sizeof操作符
sizeof操作符的作用是返回一个对象获类型名的长度,返回值类型为size_t,长度单位是字节
sizeof表达式的结果时编译时常量
sizeof操作符有三种语法形式:
sizeof(type name);
sizeof (expr);
sizeof expr;
将sizeof用于expr时,并没有计算出表达式expr的值。
对引用类型做sizeof操作将返回存放此引用类型对象所需的内存空间大小。
对指针做sizeof操作将返回存放指针所需内存大小
对数组左sizeof操作等效于对其元素类型左做sizeof操作的结果乘上数组元素个数
5.10复合表达式求值
5.10.1 优先级
优先级如下图
C++操作符的优先级
记住一个最高的:构造类型的元素或成员以及小括号。
记住一个最低的:逗号运算符。
剩余的是一、二、三、赋值。
意思是单目、双目、三目和赋值运算符。
在诸多运算符中,又分为:
算术、关系、逻辑。
两种位操作运算符中,移位运算符在算术运算符后边,逻辑位运算符在逻辑运算符的前面。再细分如下:
算术运算符分 *,/,%高于+,-。
关系运算符中,>,>=,<,〈=高于==,!=。
逻辑运算符中,除了逻辑求反(!)是单目外,逻辑与(&&)高于逻辑或(||)。
逻辑位运算符中,除了逻辑按位求反(~)外,按位与(&)高于按位半加(^),高于按位或(|)。
这样就将15种优先级都记住了,再将记忆方法总结如下:
去掉一个最高的,去掉一个最低的,剩下的是一、二、三、赋值。双目运算符中,顺序为算术、关系和逻辑,移位和逻辑位插入其中。
一个表达式里不要在两个或更多的子表达式中对同一对象做自增或自减操作
5.11 new和delete表达式
动态创建变量时,只需指定其数据类型,而不必为该对象命名
1.动态创建对象的初始化
int *pi=new int(1024);
string *ps=new string(10,’9’);
C++使用直接初始化语法规则初始化动态创建的对象。如果提供了初值,new表达式分配到所需的内存后,用给定初值初始化该内存空间。
2.动态创建对象的默认初始化
如果不提供显示初始化,动态创建的对象在函数内定义的变量初始化方式相同。对于类类型的对象,用该类的默认构造函数初始化;而内置类型对象则无初始化。
同样也可对动态创建的对象做值初始化
int *pi=new int();
4.撤销动态创建的对象
如果指针指向不是用new分配的内存地址,则在该指针上使用delete是不合法的
5.零值指针的删除
如果指针的值为0,则在其上做delete操作是合法的
C++保证删除0值的指针是安全的
6.在delete后,重设指针的值
删除指针后,指针变为悬垂指针。悬垂指针指向曾经存放对象的内存,但是该对象已经不在了。
7.const对象的动态分配和回收
C++允许动态创建const对象:
const int *pci=new const int(1024);
动态创建的const对象必须在创建时初始化,并且初始化之后不能改变。new返回的地址上存放着const对象,此地址只能赋给指向const的指针
对于类类型的const动态对象,如果该类提供了默认构造函数,则此对象可隐式初始化
动态内存管理容易出现的错误:
删除指向动态分配内存的指针失败造成内存泄漏
读写已经删除的对象
对同一个内存空间使用两次delete表达式
5.12 类型转换
如果两个类型之间可以相互转换则称这两个类型相关。
C++并不是把两个不同类型的值直接加在一起,而是提供了一组转换规则,以便在执行算术操作之前,将两个操作数转换为同一种数据类型。它们也被称作隐式类型转换
在赋值操作中,不可能改变左操作数对象的类型,因此左操作数的类型占主导地位。
5.12.1 何时发生隐式类型转换
在混合类型的表达式中,其操作数被转换为相同的类型:
int ival;
double dval;
ival>=dval; //ival被转换为dval
用作条件的表达式被转换为bool类型:
int ival;
if(ival)
while(cin)
用一个表达式初始化某个变量,该表达式被转换为该变量的类型;
int ival; //3.14被转换为int
int *p;
ip=0; 0被转换为null指针
5.12.2算术转换
算术转换保证在执行操作之前,将二元操作符的两个操作数转换为相同类型。并使表达式也具有相同的类型。
算术转换定义了一种类型转换层次,该层次决定按什么次序转换为表达式中最宽的类型。
最简单的转换为类型提升:对于所有比int小的整型,包括char、unsigned char、short、unsigned short,如果该类型的值都能包含在int类型内,它们会被提升为int型。否则它们将被提升为unsigned int型。
1.有符号和无符号类型之间的转换
包含short和int类型的表达式,将short转换为int。如果int足够表示所有unsigned short类型的值,则将unsigned short转换为int,否则将两个操作符转换为unsigned int。例如:如果short 用半个字表示,而int用一个字表示,则所有unsigned short的值都能用int包含在内。在这种机器上,unsigned short转换为int
long和unsigned int的转换也是一样的。只要机器上的long型足够表示unsigned int型的所有值,就将unsigned int的值转换为long型,否则将两个操作数转换为unsigned long型
5.12.3 其它隐式转换
1.指针转换
在使用数组时,大多数情况下数组都会自动转换为指向第一个元素的指针;
不讲数组转换为指针的情况有:数组用作取地址操作符操作数或sizeof操作数,或用数组对数组的引用进行初始化时不会将数组转换为指针
指向任意数据类型的的指针都可以转换为void *类型;整型数值常量0可转换为任意指针类型
5.转换为const对象
在使用非const对象初始化const对象的引用时,系统将非const对象转化为const对象。还可以将非const对象的地址转化为指向相关const对象的指针
5.12.4显式类型转换
显式转换也称作强制类型转换(cast)
5.12.6 命名的强制类型转换
命名的强制类型转换符号的一般形式如下
cast-name<type>(expression);
其中cast-name为static_cast、dynamic_cast、const_cast之一,type为转换的目标类型,而expression为被强制转换的值
1.dynamic_cast
支持运行时识别指针或引用所指向的对象
2.const_cast
转换掉表达式的const性质
3.static_cast
编译器隐式执行的任何类型转换都可以由static_cast显式完成。
强制类型转换关闭或者挂起了正常的类型检查