文章目录
写在前面
前面一天我们学习了如何使用C++内置的数组,以及标准库中的vector和string今天我们来学习一下C++提供的一套丰富的运算符,和使用这些运算符作用于内置类型的运算对象所执行的操作。
表达式由一个或多个运算对象组成,对表达式求值将得到一个结果
字面值和变量是最简单的表达式,其结果就是他们的值,当我们使用一个运算符和一个或多个对象结合在一起,就形成了复杂的表达式。
这个笔记拖欠了一个月,这一个月经历了很多事情。主要是期中考还有自己懈怠了。所以以后要更加努力把这一个月缺失的弥补回来。
4.1 基础
4.1.1 基本概念
C++定义了一元运算符和二元运算符,一元运算符可以理解为对一个对象进行操作,如&a,*a。二元运算符可以理解为对两个对象进行操作如a + b。有一些运算符既可以作为一元运算符,又可以作为二元运算符。如 *。*a,a * b。这种符号两种用法互不相干,相互不形成干扰
左值和右值
当一个对象被用作右值的时候,用的是对象的值(内容)。当一个对象被用作左值的时候,用的是对象的身份(内容所在的地址)。
取地址符生成的值是右值
decltype用于左值的时候,他生成的是一个引用类型,用于右值的时候他生成的是一个右值类型。
4.1.2 优先级和结合律
在C++中,优先级基本来说和我们常理的相同,由于记得部分特别多,所以这里提供一个技巧,那就是让你想先计算的表达式用括号进行包围。括号优先级是最高的,这样我们就能人为的控制复合表达式的运算顺序。
4.1.3 求值顺序
在求值顺序中比较麻烦的是复合表达式的求值顺序,我们在处理复合表达式中有两个比较简单的方法
- 1、拿不准的时候使用括号(括号真的是无敌啊)
- 2、如果改变了某个运算对象的值,在同一句表达式中尽量就不要再使用他了
4.2 算术运算符
算术运算符的运算对象都是右值
且bool类型不参与算术运算
bool a = true;
bool b2 = -a;
cout << b2 << endl;//true
cout << -a << endl;//-1
参与取余运算的对象必须要是整型,在学习这一章时差点遇到了一个数学思维在计算机思维上面的一个坑
int a = 10;
int b = 3;
//cout << a % b << endl;
cout << (a / b) * b + a % b << endl;//10而非11
在取余运算中遵循一个规则这个规则就是m%(-n)等价于m%n,(-m)%n等价于-(m%n)
逻辑与和逻辑运算符
短路求值 短路求值就是对于逻辑与(&&)和逻辑或(||)而言 先求左侧运算符对象,当左侧运算符执行完毕不能够得到表达式的确定值才会进行后一段也就是右侧运算符。
关系运算符
一条语句中,关系运算符应当只出现一个,如果出现多个则可能会导致歧义
if(i > k > j)
因为关系运算符是采用左结合律,如果i>k成立,则上述式子会变成1>j可能会不符合我们原先写程序的本意。
4.4赋值运算符
赋值运算符左侧应为左值,左侧对象必须是可以被赋值的对象
在C++11中出现了一种花括号赋值的方法
- 如果花括号赋值左侧是内置类型,则右值不能够超过左值的内存大小如
int k = 0;
k = {3.14};
就是错误的。
- 而花括号左侧不是内置类型,而是类的话,则不需要考虑第一种情况
vector<int> i;
i = {1,2,3,4,5};
上面的代码是正确的。
赋值运算符符合右结合律
可以这样理解,赋值运算符是将右值赋给左值。如
i = 0;//将0的值赋给i
又如
i = j = 0; //将0赋给j ,然后j再把值赋给i
当然如果多赋值运算符在同一个式子当中,应当符合右值能够转换成左值,或者是类型相同。如下面这个例子就是错误的
int *p = nullptr;
int i = 0;
p = i = 1;//错误的因为int类型不能转换成int*类型
赋值运算符的优先级比关系运算符的低,所以我们在用赋值运算符在条件语句的时候,应该使用括号
4.5 递增和递减运算符
前置版本作为左值对象进行返回,后置版本作为原始版本的右值进行返回。
因为这两种情况存在一些差异,所以我们在使用递增或者递减运算符的时候应该使用前置版本,因为后置版本做多了一些操作。(留一个地方保留之前未修改的版本)当然具体使用视情况而定。例如我们想使用原始版本之后又将原始版本加一,就可以使用后置版本。当然这里有更好的写法(使用前置版本)。
4.6 成员访问运算符
在C++中成员访问运算符有两种
- 点运算符 (*p).solve()
- 箭头运算符 p->solve()
4.7条件运算符
条件运算符(?:)的用法
cond?expr1:expr2;
条件运算符当两个表达式的值都是左值或者能转换成同一类型左值结果才是左值,否则结果是右值。
在输出表达式中使用条件运算符
条件运算符的优先级比较小,甚至小过>> 所以在输出表达式中,我们应该使用括号来包括住条件运算符相关式子。
4.8 位运算符
使用位运算符尽量保证,被使用对象是无符号数。
移位运算符的优先级处于中间位置,比算数运算符低,比关系运算符、赋值运算符、条件运算符要高
4.9 sizeof运算符
sizeof()返回的是占用内存空间字节数,一般使用方法是
sizeof(a)/sizeof(int)
对于string和vector而言sizeof返回的是他占得固定空间
对于数组而言,他不是计算首地址的大小而是计算的是数组在内存中占的字节数
sizeof有一点很神奇,他并不实际求运算对象的值,所以sizeof一个无效指针也是安全的。
4.10逗号运算符
逗号运算符常常用于for循环里面,这里需要注意的一点是逗号运算符是按照从左到右进行求值。
4.11 类型转换
隐式类型转换
隐式转换是自动执行的,一般而言
- 在条件中,非布尔类型转成布尔类型
- 向上转换,double = int
- 在初始化中,初始值转换成变量的类型,在赋值中,右值转成左值的类型。
4.11.1 算数转换
算数转换中存在着一个整形提升,他的意思就是把小类型的数提升为大类型的数。
4.11.2 其他类型的转换
数组进入函数的时候默认会转化成指针
4.11.3显示类型转换
我们在写程序的时候应该避免显示类型转换,想想有没有其他方式也可以实现。
const char *pc;
char *p = const_cast<char*>(pc)//将底层const * 转换成char*