4.1 基础
4.1 基本概念
- 重载运算符
重载运算符,可重载运算的类型和返回的类型。运算对象的个数、运算符优先级、结合律都无法改变 - 左值和右值
- 简单理解,左值可以被赋值,右值不能被赋值。
- 一个对象被用作左值,表示用的是对象的身份(在内存中的位置);被当作右值使用的时候,用的对象的值(内容)。
- 需要右值的地方可以用左值代替,需要左值的地方不能用右值代替。
- 作用于左值的符号:赋值运算符、取地址运算符、解引用运算符、下标运算符
- decltype作用于表达式得到引用类
int *p;
decltype(*p) //int&,对p解引用 -》引用
decltype(&p) //int**,对p取地址得到的是p的地址-》int指针的地址
4.1.2 优先级结合律
理解左右结合律。
定义符号¥为二元运算符,计算1¥2¥3
,若符合左结合律,求值顺序为(1¥2)¥3
;若符合右结合律,求值顺序为1¥(2¥3)
;
4.1.3 求值顺序
<<
没有明确规定何时、如何对运算对象求值。
int i=0;
//未定义,可能输出0,1或者1,1
cout<<i<<" "<<++i<<endl;
- 有四种运算符规定了运算顺序:"
&&
“,”||
", “?:
”,",
",从左到右计算。 f()+g()*h()+j()
- 优先级要求g和h先相乘
- 结合律要求f+乘积,再加j
- 对函数的调用顺序没有要求,若函数内部影响同一对象,很可能出错。
4.2 算数运算符
- 算数运算符的运算对象和结果都是右值
- 所有运算对象都会转为同一类型,再进行运算
- %:取余操作,
- 参与取余操作的数必须是整数
- m%n计算方法:将m、n的绝对值进行取余运算得数值部分,结果的正负性和m相同。
4.3 逻辑和关系运算符
&&
和||
都是短路求值。从左往右计算时,对&&
,一个表达式为假,停止计算,表达式为假;对||
,一个表达式为真,停止计算,表达式为真。- 在循环或传值时,比较大的对象应该声明成引用类型,避免拷贝。不需要写操作时,应该声明成常量引用。
i<j<k
的写法,若k大于1,则一定为真。‘’val==true
仅当val值为1,返回值才是true;
4.4 赋值运算符
- c++允许花括号括起来的初始值列表作为赋值语句的右侧赋值对象。
- 赋值满足右结合律。
- 赋值运算符优先级较低。运算符可查看本页最后的表。
4.5 递增和递减运算符
- 除非必要,否则不用递增递减的后置版本
- 掌握对迭代器的常用方法
*pbeg++
:返回当前值,并向后移动。 - 运算符可以按任意顺序求值。
//赋值和递增递减符同时使用行为未定义
*beg = toupper(*beg++)
*beg = toupper(*beg)//若先求左侧值
*(beg+1) = toupper(*beg)//若先求右侧值
4.6 成员访问运算符
ptr->mem
等同于(*ptr).mem
。由于.
运算符登记高,所以必须加括号。
4.7 条件运算符
- 条件运算符满足右结合律
- 在输出表达式中使用条件运算符,因为条件运算符优先级很低,要将条件运算符括起来,输出才正确。
cout<<(grade<60?"fail":"pass")<<endl;//正确
cout<<grade<60?"fail":"pass"<<endl;//错误,输出grade后,尝试比较cout和60
4.8 位运算符
- 位运算符作用于整数类型和
bitset
类 - 运算对象是小整型,值会被自动提升为int
- 负数进行左移操作时,位运算符如何处理符号位依赖于机器;强烈建议位运算符只作用于无符号类型
- IO运算符是移位运算符的重载,优先级比算术运算符低,比关系运算符、赋值运算符、条件运算符高。
4.9 sizeof运算符
sizeof(a)
返回a所占字节数- 有两种使用方式,括号内可使用type或者expr。
int a = 0;
auto sz_a1 = sizeof(a);
auto sz_a2 = sizeof(int);
auto sz_a3 = sizeof a;
// auto sz_a4 = sizeof int; 错误声明
- sizeof的返回
- char或char表达式:1
- 引用类型:被引用类型所占的字节数
- 指针:指针本身所占的字节数
- 解引用指针:指针所指所占的字节数,指针不需要有效
- 数组:整个数组所占空间的字节数,sizeof不会把数组转换成指针处理
- string或vector:只返回固定部分的大小,不返回元素所占的空间。
4.10 逗号运算符
- 对于左侧表达式,计算后将值丢掉,返回右侧表达式结果
int x=1,y=2;
a = ++x,++y;
//x=2,y=3,a=3
4.11 类型转换
- 隐式转换发生在:
- 多数表达式中,比int小的整型,提升为较大的整数类型
- 条件中,非布尔表达式转换为布尔表达式
- 初始化过程中,初始值转换为变量类型;赋值中,右侧运算对象转换为左侧运算对象。
- 算术运算、关系运算中多个类型需统一为一个类型
- 函数调用时,实参转换为形参类型
4.11.1 算数转换
- 整型提升
bool、char、signed char、unsigned char、short 、unsigned short
提升为int- 较大的char类型,如
w_char、char16_t、char32_t
转换到int、unsigned int、long、unsigned long、long long、unsigned long long
中能容纳的最小类型
- 当运算对象类型不一致,或含有无符号类型
1.若运算对象都是符号数、或都是无符号数。则小类型提升为大类型。结束。
2.若运算对象含有符号数和无符号数
- 无符号数类型不小于带符号类型:带符号数转换为无符号数。int + unsigned int 都转为 unsigned int
- 带符号数大于无符号数类型,转换结果依赖于机器。如果无符号类型的所有值都能存在该带符号类型中,则无符号类型转为带符号类型。如果不能,那么带符号类型的运算对象转换成无符号类型。如int和long的大小相同,long + unsigned int,long->unsigned int
;如果long类型占用的空间比int更多,则unsigned int->long
4.11.2 其他隐式转换
- 在取地址符&、sizeof、decltype、typeid等运算符下,数组不会被转为指针。
- 任意非常量指针都可以转为void*,任意指针都可以转换为const void*
- 条件中非0值为真,否则为假
4.11.3 显式转换
- 强制转换是非常危险的
- 强制转换格式:
cast-name<type>(expression)
- cast-name有四种类型
- dynamic_cast 在19.2节中介绍
- static-cast:
1.在明确转换成什么类型,不含底层const时使用
2.将较大类型赋值给较小类型时,关闭了警告
3.可用于找回void*指针的类型
void* p=&d;
//若类型不符合,会产生未定义的后果
double *dp =static_cast<double*>(p);
- const_cast
- 在取消底层const时使用
- 不能使用const_cast来改变为其他类型
- 如果对一个常量使用并写值,会出现未定义的后果
const char* pc;
//正确,但是通过p写值是未定义的行为
char *p =const_cast<char*>pc;
- reinterpret_cast
- 通常使用在为运算对象的位模式提供较低层次上的重新解释
- static_cast只是可用在将大类型改为小类型,reinterpret_cast将数据类型直接改变。
- reinterpret_cast本质依赖于机器,安全地使用他需要对编译器实现的转换过程非常了解。
int *ip;
char *pc = reinterpret_cast<char*>(ip);
-
避免强制类型转换,即使必须使用也要记录假定,限制类型转换值作用域。
-
旧式强制转换格式如下。替换合法,执行功能和static_cast、const_cast一致,替换不合法就和reinterpret_cast功能一致。
type(expr)
(type)expr
char *pc = (char*)ip;