表达式

文章目录
  1. 1. 基础
  2. 2. 算术运算符
  3. 3. 逻辑和关系运算符
  4. 4. 赋值运算符
  5. 5. 递增递减运算符
  6. 6. 成员访问运算符
  7. 7. 条件运算符
  8. 8. 移位运算符
  9. 9. sizeof 运算符
    1. 9.1. 逗号运算符
  10. 10. 类型转换
    1. 10.1. 算术转换
    2. 10.2. 其他隐式类型转换
  11. 11. 运算符优先级

主要内容:

  1. 基础
  2. 算术运算符
  3. 逻辑和关系运算符
  4. 赋值运算符
  5. 递增和递减运算符
  6. 成员访问运算符
  7. 条件运算符
  8. 位运算符
  9. sizeof运算符
  10. 逗号运算符
  11. 类型转换运算符
  12. 运算符优先级

运算符优先级大概层级:

  1. 作用域运算符
  2. 成员选择、下标、函数调用
  3. 后置加加减减、类型id,类型转化
  4. 一元运算符
  5. 乘法
  6. 加法
  7. 移位
  8. 关系
  9. 逻辑
  10. 条件
  11. 赋值
  12. 逗号

基础

  1. 优先级(precedence)
  2. 结合律(asscociativity)
  3. 求值顺序

当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存的位置)。

  1. 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果也是一个左值。
  2. 取地址运算符作用域左值运算对象,返回一个指向改运算对象的指针,这个指针是一个右值。
  3. 内置解引用、下标运算符、迭代器解引用求值结果是左值.
  4. 内置类型、迭代器递增运算符作用域左值,前置版本结果左值.

使用关键字decltype的时候,左值和右值有所不同,如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型。

  1. 优先级
  2. 结合律
  3. 求值顺序

有4种表达式明确规定了求值顺序

  1. 逻辑与&&
  2. 逻辑或||
  3. 条件运算符?:
  4. 逗号表达式

算术运算符

(-m)/n = m/(-n) = -(m/n)

m%(-n) = m%n

(-m)%n = -(m%n)

m%n的符号和m的相同

逻辑和关系运算符

逻辑与、逻辑或求值短路

几个关系运算符链子一起会产生意想不到的结果

进行比较运算时,除非比较的对象是布尔类型,否则不要使用布尔字面值true和false作为运算对象。

赋值运算符

赋值运算符的结果是左侧运算对象,并且是左值。如果赋值运算符的两个运算对象类型不同,右侧对象将转换为左侧运行对象类型。

赋值运算符满足右结合律,这个和其他的二元运算符不太一样。

赋值运算符的优先级低于关系运算符

递增递减运算符

为啥有? 某些迭代器不支持算术运算

  1. 前置版本递增:首先将运算对象加1,然后将改变后的对象作为求值结果。
  2. 后置版本的递增:将运算对象加1,但是求值结果是运算对象改变之前的那个值的副本。

优先使用前置版本,前置版本的递增运算符避免了不必要的功过,把加1后的对象直接返回,相反:后置版本需要将原始值存储下来,以便返回未修改的内容,对于复杂的迭代器,这种额外的工作消耗巨大。

1
2
3
4
5
6
7
8
*iter++  //这种写法比较普遍,后置++的优先级高,等价于下面两行代码
*iter;
++iter
auto pbeg = v.begin();
while(pbeg != v.end() )
{
cout << *pbeg++ << end ; //输出当前值,并将pbeg向前移动一个元素
}
1
2
3
4
5
6
7
while (begin != v.end && !isspace(*begin))
{
*begin = toupper(*begin++); //错误:该语句未定义
}
///可以解释为下面的任何一种
*begin = toupper(*begin);
*(begin+1) = touper(*begin)

所以,如果一条子表达式改变了某个运算对象的值,另一个表达式又要使用该值的话,运算符的求值顺序非常关键。

成员访问运算符

表达式ptr->mem 等价于 (*ptr).mem。

箭头运算符作用于一个指针类型的运算对象,结果是一个左值。点运算符分成两种两种情况:如果成员所属的对象是左值,结果是左值,反之,如果成员所属的对象是右值,那么结果是右值。

条件运算符

条件运算符将简单的if else 逻辑切入到单个表达式当中。基本形式

1
cond? expr1: expr2

移位运算符

二进制移位,移出边界之外的位就被舍弃掉了

位求反运算符~ 将运算对象逐位求反。

如果运算对象是小整数,它的值会被自动提升。

  1. 位于 &
  2. 位或 |
  3. 位异或 ^

sizeof 运算符

sizeof返回一个表达式或者一个类型名字所占的字节数,满足右结合律,返回值类型是size_t类型。sizeof并不计算运算对象的值。

1
2
sizeof(type)
sizeof expr

sizeof *p sizeof不需要解引用指针也能知道它所指对象的类型,所以即使p是无效指针也不会有什么影响。

  1. 对char或者类型是char的表达式执行sizeof运算,结果是1.
  2. 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
  3. 对指针执行sizeof运算,得到指针本身所占内存的大小
  4. 对解引用指针指向sizeof运算,得到指针所指对象所占空间的大小,指针不需要有效
  5. 对数组执行sizeof运算,得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算,并求和。sizeof不会把数组转化为指针。
  6. 对string、vector对象执行sizeof运算,只返回该类型固定部分大小。不会计算对象中元素所占空间。

逗号运算符

对于逗号运算符,首先对左侧的表达式求值,然后将求值结果丢弃,逗号运算符真正的结果是右侧表达式的值。如果右侧运算对象是左值,那么求值结果是左值。

类型转换

C++语言不会直接将两个不同类型的值相加,而是先根据类型转换规则,设法将运算对象的类型统一后再求值。

隐式类型转换发生场景

  1. 在大多数表达式中,比int类型小的整数值首先提升为较大的整数类型。
  2. 在条件中,非布尔类型转换为布尔类型。
  3. 初始化过程中,初始值转换为变量的类型。在赋值语句中,右侧对象转换为左侧运算对象类型
  4. 算术或者关系运算对象有多种类型,需要转换为同一种类型
  5. 函数调用时会发生类型转换

算术转换

整数提示:char signed char、 unsigned char、 short 、unsigned short 、bool 一般会提升为int

有符号的转化为无符号的(大小相等)
整数转化为浮点数
转化为宽类型

其他隐式类型转换

  1. 数组转成指针: 在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针。当数组被用作decltyoe关键字参数,或者取地址&,sizeof及typeid等运算对象时,上述转化不会发生。如果用引用初始化数组,也不会发生转化。
  2. 指针的转化:常量整数0或者字面值nullptr能转化成任意指针类型。指向任何非常量指针能转换成void 指向任何对象的指针能转化成const void
  3. 转换成布尔类型:存在一种从算术类型或者指针类型向布尔类型自动转化的机制,如果指针或算术类型值为0,转换结果是false,否则转换结果是true。
  4. 转换成常量:允许冲非常量类型的指针转化为常量类型的指针。
  5. 命名的强制类型转换。

static_cast:任何具有明确定义的类型转化,只要不包含底层const,都可以使用
const_cast:只能改变运算对象的底层const
reinterpret_cast: 通常为运算对象的位模式提供较底层次上的重新解释。

运算符优先级

结合律 运算符 功能 用法
:: 全局作用域 ::name
:: 类作用域 class::name
:: 命名空间作用域 namespace::name
. 成员选择 object.member
-> 成员选择 pointer->member
[] 下标 expr[expr]
() 函数调用 name(expr_lit)
() 类型构造 type(expr_lit)
++ 后置递增运算 lvalue++
后置递减运算 lvalue–
typeid 类型ID typeid(type)
typeid 运行时类型ID typeid(expr)
explicit cast 类型转换 cast_name(expr)
++ 前置递增运算 ++lvalue
前置递减运算 –lvalue
~ 位求反 ~expr
! 逻辑非 !expr
- 一元负号 -expr
+ 一元正号 +expr
* 解引用 *expr
& 取地址 &lvalue
() 类型转换 (type)expr
sizeof 对象大小 sizeof expr
sizeof 类型大小 sizeof(type)
Sizeof… 参数包的大小 sizeof…(name)
new 创建对象 new type
new [] 创建数组 new type[size]
delete 释放对象 delete expr
delete [] 释放数组 delete [] expr
noexcept 能否抛出异常 noexcept(expr)
->* 指向成员选择的指针 ptr->*prt_to_member
.* 指向成员选择的指针 obj.*prt_to_member
* 乘法 expr*expr
/ 除法 expr/expr
% 取模(取余) expr%expr
+ 加法 expr + expr
- 减法 expr -expr
<< 向左移位 expr << expr
>> 向右移位 expr >> expr
< 小于 expr < expr
<= 小于等于 expr <=expr
> 大于 expr > expr
>= 大于等于 expr >= expr
== 相等 expr == expr
!= 不相等 expr != expr
& 位与 expr & expr
^ 位异或 expr ^ expr
` ` 位或 `expr expr`
&& 逻辑与 expr && expr
` ` 逻辑或 `expr expr`
?: 条件 expr?expr:expr
= 赋值 lvalue = expr
*=,/-,%= 符合赋值 lvalue+=expr等
+=,-= 符合赋值
<<=,>>= 符合赋值
&=,` =`,^= 符合赋值
throw 抛出异常 throw expr
, 逗号 expr,expr