表达式=一个运算符+一个或多个运算对象
1.基础
(1)基本概念
- 一元运算符、二元运算符、三元运算符…
- 组合运算符和运算对象
- 运算对象转换:指针不能转换成浮点数
- 重载运算符:用户自定义的作用于类类型的运算对象的运算符。其运算对象的类型和返回值的类型都由该运算符定义。
- 左值和右值:当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
在需要右值的地方可以用左值来代替,但是不能把右值当成左值使用。
需要左值的运算符:赋值运算符(左左)、取地址符&(左右)、解引用运算符*(左左)、下标运算符[] (左左)、递增递减运算符(左左)。
(2)优先级与结合律
- 复合表达式
- 括号无视优先级与结合律
(3)求值顺序
明确规定先左再右的求值顺序的运算符:逻辑与(&&)、逻辑或(||)、条件(?: )、逗号(,)。
运算对象的求值顺序与优先级和结合律无关。
建议:
①拿不准的时候最好加括号来强制使表达式的组合关系符合程序逻辑的要求。
②如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
2.算术运算符
算术运算符作用于算术类型或能转化为算术类型的类型,返回值为算术类型。运算对象和求值结果都是右值。一元正号、加法和减法运算符可作用于指针。
/和%:整数相除结果还是整数;取余运算对象必须是整数类型;整数(正负数)除法的商一律向0取整;取余遵循的原则为(m/n)*n+m%n=m。
int a = 10, b = 4;
float c = 10, d = 4, e = 3.14;
cout << a / b << endl; //2
cout << c / b << endl; //2.5
cout << a / d << endl; //2.5
cout << 10 / 4 << endl; //2
cout << 10.0 / 4 << endl; //2.5
cout << a%e << endl; //报错
cout << -34 / 7 << ' ' << -34 % 7 << endl; //-4 -6
cout << 34 / (-7) << ' ' << 34 % (-7) << endl; //-4 6
cout << (-34) / (-7) << ' ' << (-34) % (-7) << endl;//4 -6
3.逻辑和关系运算符
关系运算符作用于算术类型或指针类型,逻辑运算符作用于能转换成布尔值的类型;它们的返回值都是布尔类型。运算对象和求值结果都是右值。
4.赋值运算符 =
赋值运算符可作用于很多类型。赋值运算符的左侧运算对象必须是一个可修改的左值,运算结果是它的左侧运算对象,也是一个左值。
- 赋值运算符满足右结合律
int ival, jval;
ival = jval = 0; //正确:都被赋值为0
int iv, *pv;
iv = p = 0; //错误:不能把指针的值赋给int
- 赋值运算符优先级较低
- 复合赋值运算符
5.递增和递减运算符 ++ --
递增和递减运算符作用于算术类型,还可作用于迭代器。它们的运算对象都为左值;前置版本将对象本身作为左值返回,后置版本将对象原始值的副本作为右值返回。
int m = 0, n;
n = ++m; //m=1,n=1:前置版本得到递增之后的值
int i = 0, j;
j = i++; //i=1,j=0:后置版本得到递增之前的值
- 在一条语句中混用解引用和递增运算符
6.成员访问运算符 . ->
点运算符获取类对象的一个成员;箭头运算符ptr->mem 等价于(*ptr).mem 。箭头运算符作用于指针类型的运算对象。箭头运算符运算结果是一个左值;点运算符的运算结果根据成员所属的对象是左值还是右值来判断。
7.条件运算符 ?:
基本形式: cond ? expr1 : expr2
条件运算符运算结果根据两个表达式的左右值类型来判断。条件运算符满足右结合律。
- 嵌套条件运算符
- 在输出表达式中使用条件运算符
8.位运算符
位运算符作用于整数类型的运算对象,也能用于bitset类型。如果运算对象是“小整型”,则它的值会被自动提升为较大的整数类型。
- 移位运算符
移位运算符满足左结合律。 - 位求反运算符:对运算对象逐位求反,将1置为0、将0置为1。
- 位与、位或、位异或运算符
- 使用位运算符
见书上P137例子。
9.sizeof运算符
sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,运算结果为size_t类型的常量表达式。
两种基本形式: sizeof (type)
sizeof expr
10.逗号运算符 ,
逗号运算符先对左侧表达式求值,然后将求值结果丢弃掉,其真正结果是右侧表达式的值。其运算结果的左右值由右侧运算对象为左右值来判断。
11.类型转换
(1)算术转换(隐式转换)
- 整数提升:bool、char、signed char、unsigned char、short、unsigned short→int/unsigned int
较大的char→int、unsigned int、long、unsigned long、long long、unsigned long long中最小的类型 - 无符号类型的运算对象:
- 理解算术转换
(2)其他隐式类型转换
- 数组转换成指针
在大多数情况下,数组转换成指向数组首元素的指针。但当数组被用作decltype、取地址符&、sizeof、typeid或用引用来初始化数组时,上述转换不会发生。 - 指针的转换
常量整数值0或字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*。 - 转换成布尔类型
- 转换成常量
允许将指向非常量类型的指针(或引用)转换成指向相应的常量类型的指针(或引用)。但相反的转换不允许。
- 类类型定义的转换
(3)显式转换
- 命名的强制类型转换
基本形式:cast-name <type>(expression);
①static_cast: 任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
②dynamic_cast: 支持运行时类型识别。
③const_cast: 将常量对象转换成非常量对象,只能改变运算对象的底层const,一般用于函数重载。
④reintterpret_cast: 为运算对象的位模式提供较低层次上的重新解释。
- 旧式的强制类型转换
两种基本形式:type (expr); //函数形式的强制类型转换
(type) expr; //C语言风格的强制类型转换
12.运算符优先级表