基础

详情点击查看

5

基本概念

1.一元运算符和二元运算符

1
2
3
4
5
6
7
作用于一个运算对象的运算符是一元运算符
作用于两个运算对象的运算符是二元运算符

一些符号既能作为一元运算符也能作为二元运算符,由上下文决定
如(*),

函数调用也是一种特殊的运算符

2.重载运算符

1
当运算符作用于类类型的运算对象时,用户可以自定义其含义,被称为重载运算符

3.左值和右值

1
2
3
4
5
6
当一个对象被用作右值的时候,用的是对象的值(内容);
当一个对象被用作左值的时候,用的是对象的身份(在内存中的位置)

赋值运算符需要一个左值作为左值运算对象,得到的结果仍然是一个左值
取地址符作用于一个左值运算对象,返回一个指向该运算对象的指针,是一个右值
解引用运算符和下标运算符的求值结果都是左值

优先级与结合律

移步c++primer p123,这里不做阐述

求值顺序

运算对象的求值顺序与优先级和结合律无关

算术运算符

详情点击查看
1
2
bool b = true;
bool b2 = -b;//b2是true

对于大多数运算符来说,布尔类型的运算对象将被提升为int类型。上式中b的值为真,参与运算时会被提升成1,求负后变为-1,-1的布尔值为真,所以结果为true

取余运算符%的运算对象必须是整数类型

逻辑和关系运算符

详情点击查看

逻辑与和逻辑或都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时才会计算右侧运算对象的值。这种策略称为短路求值。

利用短路求值的策略来确保右侧运算对象求值过程的正确性和安全性

赋值运算符

详情点击查看

1.与其他二元运算符不同,赋值运算符满足右结合律

1
2
int ival, jval;
ival = jval = 0;//都被赋值为0

注意:因为赋值运算符的优先级低于关系运算符的优先级,所以在条件语句中,赋值部分通常应该加上括号

2.复合运算符的性能比普通运算符好,因为复合运算符只求值一次,而普通运算符求值两次,一次是作为右边表达式的一部分求值,另一次是作为赋值运算的左侧运算对象求值

递增和递减运算符

详情点击查看

1.很多迭代器不支持算术运算,但基本都支持递增和递减运算符

2.建议:除非必须,优先使用递增递减运算符的前置版本

1
2
前置版本的递增递减运算符是将值加1后直接返回改变了的运算对象;
但后置版本需要将原始值存储下来以便返回这个未修改的内容。

3.在一条语句中混用解引用和递增运算符

1
2
3
4
5
//输出vector对象的内容直到遇到第一个负值为止
auto pbeg = v.begin();
while(pbeg != v.end() && *pbeg >= 0)
cout << *pbeg++ << endl;//输出当前值,并将pbeg向前移动一个元素
//后置运算符的优先级高于解引用运算符,所以*pbeg++等价于*(pbeg++)

成员访问运算符

详情点击查看

点运算符(.)和箭头运算符(->)都可以用于访问成员。

箭头运算符先解引用左侧对象再访问它的成员

1
2
3
string s1 = "a string", *p = &s1;
auto n = (*p).size();
n = p->size();//等价于上一句

注意:解引用的优先级低于点运算符,所以要加括号

条件运算符

详情点击查看

1.条件运算符(?:)允许我们将简单的if-else逻辑嵌入到单个表达式中

1
2
3
//判断成绩是否合格
string finalgrade = (grade < 60) ? "fail" : "pass";
//如果小于,返回fail给finalgrade,如果大于,返回pass

2.嵌套条件运算符

1
2
finalgrade = (grade > 90) ? "high pass" : (grade < 60) ? "fail" : "pass";
//条件运算符满足右结合律,所以右边的条件运算构成了左边的条件运算的分支

3.在输出表达式中使用条件运算符

1
2
3
4
5
cout << ((grade < 60) ? "fail" : "pass");//输出pass或者fail
cout << (grade < 60) ? "fail" : "pass";//输出1或者0
cout << grade < 60 : ? "fail" : "pass";//错误

//条件运算符的优先级非常低,所以当一条长表达式嵌套了条件运算表达式时,需要加上括号,不然表达式的含义会发生改变

位运算符

详情点击查看

1.位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。bitset类型也能使用位运算符

2.位运算符(左结合)

位运算符含义
~取反
<<
>>
左移
右移
&位与
^位异或
\位或

关于符号位如何处理没有明确的规定,所以建议仅将位运算符用于处理无符号类型

3.移位运算符

1
2
3
4
5
6
7
8
9
10
unsigned char bits = 0233;//0233是八进制的字面值
//转换成二进制:10011011
bits << 8;//bits提升成int类型,然后向左移动8位
//00000000|00000000|10011011|00000000
bits << 31;//向左移动31位,左边超出边界的位丢弃掉了
//10000000|00000000|00000000|00000000
bits >> 3;//向右移动3位
//00000000|00000000|00000000|00010011

//左移运算符在右侧插入值为0的二进制位,右移运算符:如果运算对象为无符号类型,那么在左侧插入值为0的二进制位,如果带符号,则无法确定

3.位取反运算符

1
2
3
4
unsigned char bits = 0227;
//10010111
~bits;
//11111111|11111111|11111111|01101000

4.位与,位或, 位异或运算符

1
2
3
4
5
& :如果两个运算对象的对应位置都为1,则运算结果中该位为1,否则为0;

|:如果两个运算对象对应位置至少有一个为1, 则运算结果中该位为1, 否则为0;

^:如果两个运算对象对应位置有且只有一个为1则运算结果中该位为1,否则为0。

5.使用位运算符

1
2
3
4
5
//假设班上有30名学生,进行了一次小测试,测试的结果分为通过与不通过。我们可以用二进制位表示学生是否通过,这样全班的测验结果可以用一个无符号整数来表示
unsigned long quiz1 = 0;//long至少为32位。将quiz1的每一位都设置为0
//学生27通过了考试
quiz1 |= 1UL << 27;//1UL表示无符号长整型字面值1
quiz1 = quiz1 | 1UL << 27;//与上一句等价

6.移位运算符(又称为IO运算符)满足左结合律

1
2
3
4
移位运算符的重载版本被用来进行IO操作。但他们的优先级和结合律都是一样的
满足左结合律

移位运算符的优先级比算术运算符低,但比关系运算符、赋值运算符和条件运算符高

sizeof运算符

详情点击查看

1.sizeof运算符返回一条表达式或一个类型名字所占的字节数

sizeof运算符满足右结合律

1
2
3
4
5
sizeof data;//data的类型的大小

int *p;
sizeof p;//指针所占的空间大小
sizeof *p;//p所指类型的空间的大小

2.sizeof 返回表达式结果类型的大小,但不实际计算运算对象的值

1
2
3
4
5
6
7
8
sizeof char		    返回1
sizeof 引用类型 返回被引用对象所占空间的大小
sizeof 指针 返回指针本身所占空间的大小
sizeof 解引用指针 返回指针所指对象所占空间的大小
sizeof 数组 返回整个数组所占空间的大小
sizeof string或vector 返回该类型固定部分的大小,不会计算对象中的元素占用多少空间
因为sizeof可以得到整个数组的大小,再除以单个元素的大小可以得到数组中元素的个数
sizeof(ia) / sizeof(*ia)

3.因为sizeof的返回值是一个常量表达式,所以可以用sizeof的返回值来声明数组的维度

逗号运算符

详情点击查看

逗号运算符含有两个运算对象,规定了运算对象求值的顺序,按从左向右的顺序依次求值

类型转换

详情点击查看

算术转换

算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型

P142-143

其他隐式类型转换

1.数组转换成指针

1
2
int ia[10];
int *p = ia;//ia转换成指向数组首元素的指针

2.指针的转换

1
2
3
1.常量整数值0或字面值nullptr能转换成任意指针类型
2.指向任意非常量的指针能转换成void*
3.指向任意对象的指针能转换成const void*

3.转换成布尔类型

4.转换成常量

显示转换

显示转换的方法称为强制类型转换

1.命名的强制类型转换

1
2
3
cast-name<type>(expression);
//type是转换的目标类型,expression是要转换的值
//cast-name 是 static_cast/dynamic_cast/const_cast/reinterpret_cast中的一种

2.static_cast

1
2
3
4
5
6
7
//任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast
int a, b;
double slope = static_cast<double>(j) / i;

//static_cast对于编译器无法自动执行的类型转换也有用。如可以用static_cast找回存在于void*指针中的值
void* p = &d;
double *dp = static_cast<double*>(p);

3.const_cast

1
2
3
//const_cast只能改变运算对象的底层const
const char *pc;
char *p = const_cast<char*>(cp);

4.reinterpret_cast

c++primer p146

运算符优先级表

详情点击查看

c++primer p147