C/C++学习
第四章:表达式
0.前导:
表达式是由一个或多个运算对象构成的,分为俩部分,运算与对象,其中对象在前二章已经介绍过了,在本章重点介绍运算,在c++中定义了不同的运算符实现不同的运算。
学习路径:基础知识——各种运算符
1.基础:
运算符分类:
一元运算符:作用于一个对象的运算符
二元运算符:作用于二个对象的运算符
三元运算符:作用于三个对象的运算符(条件 ? 表达式1 : 表达式2)
特殊运算符:函数调用,涉及到多个对象
重载运算符:
一般使用运算符时,作用对象只包括内置数据类型。使用运算符运算自定义的数据类型时,需要在类中自定义(重载)这个运算符号的操作,使得这个符号能用在自定义的数据类型上。
左值与右值:
当一个对象是左值时:用的是该对象的内存地址 当一个对象是右值是:用的是该对象的内容
优先级与结合律与括号:
在复合表达式(有多个运算符)中,求复合表达式的值时运算符使用优先级与结合律对对象进行有序操作
优先级:优先级高的运算符优先进行与对象结合
结合律:相同优先级的运算符按照结合律规定的顺序与对象结合,运算符调用顺序也遵循结合律。
括号:无视优先级与结合律,先执行括号中的表达式
int a = 9 ,b=3,c=4,d=7;
int i = a + b * c / d //结合顺序为((b*c)/d)+a
int m = (a+b) * c / d //结合顺序为((a+b)*c)/d
求值顺序:
求值顺序就是运算符对俩边对象进行操作时是没有先处理哪一边的概念的。因此不能在表达式中调用某一对象和被改变值后的对象。
int a =8, b=6;
int i = a + b; //不确定是先调用a还是先调用b
int m = i + ++i; //不允许的
2.运算符
一元运算符:
递增递减运算符: 区分前置版本与后置版本:
i = ++i; //前置,对i加一,返回运算后的对象赋值给i
j = j--; //后置,对j减一,返回运算前的对象赋值给j
与解引用混用
int i = 3;
int* p = &i;
cout<< *p++ <<endl; //递增递减运算符优先级高,先执行P++再解引用
顺序求值:对于这个会影响自身值的运算符,严禁在表达式中不要多次调用一个对象和该运算符
int i = 4;
int p = i++ + ++i; //禁止,不确定调用顺序
成员运算符: . 、-> 作用:获取类对象中的成员
string s1 = "wef" , *p = s1;
auto n = s1.size();
n = (*p).size(); .表示调用对象中的成员
n = p->size(); ->表示先对对象解引用后调用对象中的成员
位运算符: ~ 、<<、>>、&、^、|
在使用位运算符时,如果被操作对象的数据类型是小整形,则会自动提升为较大的整形:类型转换。建议使用无符号整形作为位运算符的使用对象。
sizeof运算符:
sizeof(对象)、sizeof 表达式 使用sizeof运算符只返回该对象或表达式所占的内存大小,不参与数值的运算,返回的数据类型是size_t的常量表达式。
int a = 5;
int &b = a;
int *c = &a;
int d[] = {3,4,5};
string e = "ef";
vector<int> f = {3,6,3};
auto n = sizeof(a); //返回a的所占空间大小
n = sizeof(b); //引用,返回被引用对象所占空间大小
n = sizeof(c); //返回指针所占空间大小
n = sizeof *c; //返回指针所指对象所占的空间大小
n = sizeof(d); //返回数组中所有对象所占空间大小的和
n = sizeof(e); //返回string所占空间大小,即对象个数
n = sizeof(f); //返回vector所占空间大小,及对象个数
类型转换运算符:在定义数据类型进行转换
隐式转换:在这些情况会发生隐式的数据类型转换
- 表达式,小数据类型会提升为大数据类型,若有多种类型则转换为同一类型
- 条件中,非布尔值转换成布尔值
- 初始化与赋值,右值对象数据类型转换为左值对象的数据类型
- 函数调用
显示转换(强制转换):cast-name(expression)
早期c++转换语句:type(expr) 、(type) expr(c语言风格强转)
- static_cast:除了底层const,其它数据类型都可以强制转换
- const_cast:只能去除const,无法转换其它数据类型。
- reinterpret_cast:无需理会
int i = 4;
int *p = &i;
const int *j = &i;
void *t;
double u = static_cast<double> i ; //正确
double *y = static_cat<double*> p; //错误,不能将int*转换成double*
double *r = static_cast<double *> t; //正确,可以将空指针转换为其它数据类型
int *b = const_cast<int *>j; //正确,将常量指针转换成指针
二元运算符:
算术运算符:正号、负号、加、减、乘、除
赋值运算符 =:右结合,在复合赋值表达式中,从右向左赋值
逻辑和关系运算符(!、&&、||) :有求值顺序,先调用运算符左边的表达式再调用右边的对象
逗号运算符 ( , ) :有求值顺序,先调用运算符左边的表达式再调用右边的表达式,只返回右边表达式的结果
三元运算符:
条件运算符:右结合,优先级很低,在使用时最好用括号括住整个表达式
cout<< grade<60 ? "fail" : "pass" ; //<<运算符会先将cout与grade结合
cout<< (grade<60 ? "fail" : "pass") ; //正确
3.运算符优先级表
| 优先级 | 结合律 | 运算符 | 功能 | ||
|---|---|---|---|---|---|
| 0 | 左 | : : | 全局作用域 | ||
| 0 | 左 | : : | 类作用域 | ||
| 0 | 左 | : : | 命名空间作用域 | ||
| 1 | 左 | . | 成员选择 | ||
| 1 | 左 | -> | 成员选择 | ||
| 1 | 左 | [ ] | 下标 | ||
| 1 | 左 | ( ) | 函数调用 | ||
| 1 | 左 | ( ) | 类型构造 | ||
| 2 | 右 | ++、-- | 后置自增自减 | ||
| 2 | 右 | typeid | 类型ID | ||
| 2 | 右 | typeid | 运行时类型ID | ||
| 2 | 右 | cast | 类型转换 | ||
| 3 | 右 | ++、-- | 前置自增自减 | ||
| 3 | 右 | ~ | 取反 | ||
| 3 | 右 | ! | 逻辑非 | ||
| 3 | 右 | +、- | 正负号 | ||
| 3 | 右 | *,& | 解引用,取址 | ||
| 3 | 右 | () | 类型转换 | ||
| 3 | 右 | sizeof | 内存大小 | ||
| 3 | 右 | new | 新建对象 | ||
| 3 | 右 | new[ ] | 新建对象数组 | ||
| 3 | 右 | delete | 释放对象 | ||
| 3 | 右 | delete[ ] | 释放数组 | ||
| 3 | 右 | noexpect | 能否抛出异常 | ||
| 4 | 左 | -> *、. * | 指向成员选择的指针 | ||
| 5 | 左 | * / % | 乘除 | ||
| 6 | 左 | +,- | 加减 | ||
| 7 | 左 | <<、>> | 左移右移 | ||
| 8 | 左 | <,>,>=,<= | 比较符 | ||
| 9 | 左 | ==,!= | 逻辑等于不等于 | ||
| 10 | 左 | & | 与 | ||
| 11 | 左 | 异或 | |||
| 12 | 左 | 或 | |||
| 13 | 左 | && | 逻辑与 | ||
| 14 | 左 | 逻辑或 | |||
| 15 | 右 | ? : | 条件语句 | ||
| 16 | 右 | = | 赋值 | ||
| 17 | 右 | += | 复合赋值 | ||
| 18 | 右 | throw | 抛出异常 | ||
| 19 | 左 | , | 逗号 |