- C++提供了一套运算符并且定义了它们作用于内资类型时的操作
- 运算符作用于类之间时,允许重载运算符
表达式由一个或者多个运算符/运算对象组成,对表达式求值返回结果。把运算符和运算对象组合可以得到复杂的表达式。
基础
- 当对象被用作右值时,用的是对象的值(内容),当对象被用作左值时,用的是对象的身份(在内存中的位置)。
- 除右值引用外,需要右值的地方都能用左值代替,反之不可。当左值被作为右值使用时,用的是它的值。
- 运算符的左右值要求:
- 赋值符
=需要非常量左值作为左侧对象,返回结果也是左值 - 取地址符
&作用于左值对象,返回指向该对象的指针,该指针是右值 - 内置解引用
*、内置下标[]、迭代器解引用*、string和vector的下标[],它们返回的结果都是左值 - 内置和迭代器的递增
++递减--作用于左值对象,其前置版本返回左值 - 若表达式求值结果是左值,则
decltype的结果是引用类型。例如int *p;求decltype(*p)的结果是int &
- 赋值符
先验知识: 左值 右值
- 定义:
lvalue(locator value)代表一个在内存中占有确定位置的对象(换句话说就是有一个地址)。rvalue通过排他性来定义,每个表达式不是lvalue就是rvalue。因此从上面的lvalue的定义,rvalue是在不在内存中占有确定位置的表达式。
例子:
int var;
var = 4; // corret
4 = var; // error! ‘4’是代码中临时中间变量,仅仅是寄存器中的临时存储,不具有确定locate
(var + 1) = 4; // error ! 同上
//返回引用类型的函数需要特殊注意
int a = 1;
int& foo() {return a;}
int main()
{
foo() = 10; // a的引用被修改为10,地址存储值被修改
return 0;
}
复制代码
算术运算符

- 算术表达式求值前,小整型都会被提升。所有对象最终都转换成同一种类型
- 一元正号、加减都可用于指针。一元正号作用于指针或算术值时,返回(提升后的)副本,一元负号对对象的值取负后,返回(提升后的)副本。
- 除法运算
/中,C++的早期版本允许结果为负时向上或向下取整,C++11之后规定商一律向0取整,即直接切除小数部分。 - 取余运算
%中,若m和n是整数且n非0,则表达式(m/n)*n+m%n的结果为m。即,若m%n不为0,则其符号与m相同。C++的早期版本允许m%n的符号匹配n的符号且商向负无穷侧取整,但在C++11中被禁止了。除了-m导致溢出的特殊情况,(-m)/n和m/(-n)都等于-(m/n),m%(-n)等于m%n,(-m)%n等于-(m%n)
逻辑和关系运算符

- 逻辑与
&&、逻辑或||都是短路求值,先求左侧,仅由左侧无法确定表达式结果时再求右侧。- 逻辑与
&&仅当左侧为真时才求右侧 - 逻辑或
||仅当左侧为假时才求右侧
- 逻辑与
- 与Python不同,C++中的
int a == true只有当a = 1时为真
赋值运算符
- 如果左右类型不匹配,将右侧转为左侧类型。
- C++允许赋值运算的结果作为条件,所以
=和==要分清。赋值运算的返回值为左侧对象。 - 复合运算符更快:复合运算符
a+=b仅求值一次,普通运算符a=a+b求值两次,一次加法一次赋值。
递增和递减运算符
- 递增和递减符都作用于左值对象。
++i被称为前置版本,i++被称为后置版本。- 前置版本和后置版本的区别:
- 前置版本将对象
+1或-1后,将对象本身作为左值返回 - 后置版本将对象
+1或-1后,将对象原始值的副本作为右值返回
- 前置版本将对象
- 应尽量使用前置版本,因为比后置版本少一次拷贝,避免了不必要的工作。
- 混用解引用和递增可实现简洁性:
cout<<*iter++;等价于cout<<*iter; ++iter;
成员访问运算符
- 点运算符
.获取类对象的一个成员,箭头运算符->由解引用*和点运算符.组成,即ptr->mem等价于(*ptr).mem,括号不可省略,因为点优先级比解引用高。 - 箭头运算符作用于指针,得到对象的成员,结果是左值。(因为解引用得到的一定是引用,引用是左值)
- 点运算符取决于对象:对象是左值就返回左值,对象是右值就返回右值。
条件运算符
- 条件运算符满足右结合律,即
a?b:c?d:e应解读为a?b:(c?d:e)
位运算符
- 位运算符遇到小整型(bool、char、short等)时,先对其自动提升再进行运算。
- 如果位运算符的运算对象是signed且值为负,则处理符号位的方式取决于机器。左移右移会改变符号位,是未定义行为。
- 移位运算符
<<和>>:首先令左侧运算对象按照右侧运算对象的要求移动指定位数,然后将移动后的(可能被提升了)左侧对象的拷贝作为结果。其中右侧对象一定不能为负,且值必须严格小于左侧位数。移出边界的位被舍弃。 - 移位运算符满足左结合律
- 左移运算符向右侧插入
0,右移运算符向左侧插入的数值取决于类型和机器:unsigned类型插入0,signed类型取决于机器和编码方式。

sizeof运算符
sizeof运算符返回一条表达式或一个类型所占的字节数,满足右结合律,返回值是ize_t型的常量表达式。- 两种形式:
sizeof(type)、sizeof expr sizeof运算不会计算运算对象的值,所以可以:- 在
sizeof对无效指针进行解引用,因为解引用的操作不会真实发生。 - 在
sizeof里直接用域运算符::来获取类成员的大小而不需要对象和成员。
- 在
- sizeof运算符在对不同对象进行运算时的差异:
- char型表达式返回1
引用做sizeof返回被引用对象所占空间大小指针做sizeof返回指针本身所占空间大小解引用指针做sizeof返回指向对象所占空间大小数组做sizeof得到整个数组所占空间大小,(attention:sizeof不会把数组当指针处理)string或vector求sizeof只返回固定部分的大小,不会计算对象实际占用空间
常见使用:sizeof(ia) / sizeof(*ia)用于计算数组的长度。
int arr[10];
constexpr int len = sizeof(arr) / sizeof(*arr); //存储固定数组长度,后续可以用于声明新数组
cout << len << endl;
constexpr int INPUT_H = 608;
constexpr int INPUT_W = 1088;
复制代码
类型转换
类型转换分为隐式转换和显式转换,隐式转换发生的情形有:
- 整型提升
- 条件中非bool转bool
- 初始化时初始值转为变量类型,赋值时右侧对象转为左侧类型
- 算术运算或关系运算中有多种类型,最终会统一
- 函数调用时也会有类型转换
算术转换
算术转换:把运算对象(算术类型)转为最宽的类型,同时有整型和浮点时将整型转浮点。整型提升:把小整型转为大整型。- 小整型(bool、char、signed char、unsigned char、short、unsigned short等),只要值能放进int就转为int,放不进int就放进unsigned int
- 宽字符(wchar_t、char16_t、char32_t)提升为int、unsigned int、long、unsigned long、long long、unsigned long long中能装进去的最小者
signed和unsigned的转换:- 若unsigned类型不小于signed类型,直接将signed转为unsigned
- 若unsigned类型小于signed类型,且该unsigned类型的值都能装进该signed类型,则unsigned转为signed
- 若unsigned类型小于signed类型,且该unsigned类型的值不都能装进该signed类型,则signed转为unsigned
情形示例:
bool flag; char cval;
short sval; unsigned short usval;
int ival; unsigned int uival;
long lval; unsigned long ulval;
float fval; double dval;
3.14159L+'a'; //'a'提升为int,再转为long double
dval+ival; //ival转double
dval+fval; //fval转double
ival=dval; //dval切除小数部分转int
flag=dval; //dval是0则false,否则true
cval+fval; //cval提升为int,再转float
sval+cval; //sval和cval都提升为int
cval+lval; //cval转long
ival+ulval; //ival转unsigned long
usval+ival; //未定义,根据unsigned short和int所占空间大小做转换
uival+lval; //未定义,根据unsigned int和long所占空间大小做转换
复制代码
其他隐式类型转换
- 数组转指针:
- 大多数用到数组的表达式中,数组自动转为指向首元素的指针
- 在表达式中使用函数类型也会转为函数指针
- 例外:
decltype、取地址&、sizeof、typeid运算符不会将数组转指针 - 例外:用
引用初始化数组时也不会转指针
- 指针的转换:
0或nullptr可转为任意指针类型- 指向任意
非常量的指针能转为void * - 指向任意
对象的指针能转为const void *
- 转换为常量:允许将指向非常量的指针或引用转为指向常量的指针或引用。但反之不可,底层const不可删除。
- 类类型的转换:类类型可定义转换,但编译器只能执行一步转换。
题外话: 温习 底层、顶层const
- 指向常量的指针(底层cosnt):不能改变其指向内容的指针。声明时const可以放在类型名前后都可,拿int类型来说,声明时:const int和int const 是等价的。声明指向常量的指针也就是底层const。
- 注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符
*)来改变它所指向的内容。仍可通过对指向对象直接修改实现指针指向的值。
- 注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符
int num_a = 1;
int const *p_a = &num_a; //底层const
//*p_a = 2; //错误,指向“常量”的指针不能改变所指的对象
cout << *p_a << endl;
num_a = 2; //修改地址的值
cout << "after modify : " << *p_a << endl; // 已经修改为了2
复制代码
- 指针常量:代表指针本身是常量,声明时必须
初始化,之后它存储的地址值就不能再改变。声明时const必须放在指针符号*后面,即:*const。声明常量指针就是顶层const
int num_b = 2;
int *const p_b = &num_b; //顶层const
//p_b = &num_a; //错误,常量指针不能改变存储的地址值
复制代码
- 区分作用:举例说明
const int a = 1;
//int * pi = &a; //错误,&a是底层const,不能赋值给非底层const
const int * pi = &a; //正确,&a是底层const,可以赋值给底层const
const int *const *const ppi = &pi //即是底层const,也是顶层const
const int *const *const *pppi = &ppi; //底层const
复制代码
显式转换
强制类型转换:手动指定要转换的变量和要转换为的类型,经常是很危险的。- 显式转换形式为
cast-name<type>(expression),若type是引用类型,则返回左值。cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast中的一种。 static_cast:只要不包含底层const,都可使用。例如将大算术类型转为小算术类型、浮点转整型、编译器无法自动执行的类型转换- 可用
static_cast找回存在于void *指针中的值,将其强制转换为原来的类型。 - 例子:
static_cast找回void *指针中的值
double d=3.14;
void *p=&d; //任何非常量对象的地址都能放进void *指针
double *dp=stataic_cast<double *>(p); //将指针转回指向double型
复制代码
const_cast:只能改变对象的底层const。(attention: 用const_cast去掉底层const之后不可对其写入,这是未定义行为)- 如果对象是常量,用const_cast去掉常量后执行写操作是未定义行为。
- const_cast常用于有函数重载的上下文,其他情况下不可使用。
reinterpret_cast:为运算对象的位模式提供较低层次上的重新解释,即内存中的bits不变,改变解读方式。它依赖于机器,非常危险。- 建议避免使用强制类型转换,尤其是reinterpret_cast
- 早期C++中,显式类型转换的形式为
type(expr)和(type)expr。在某处使用旧式强制转换时,若换为const_cast和static_cast也合法,就当作const_cast和static_cast,否则当作reinterpret_cast。因为指代不明,故建议不使用。
运算优先级

© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


















![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)
![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
