C++Primer_ch4

ch4 表达式

4.1 基础
4.2 算术运算符
4.3 逻辑和关系运算符
4.4 赋值运算符
4.5 递增和递减运算符
4.6 成员访问运算符
4.7 条件运算符
4.8 位运算符
4.9 sizeof运算符
4.10 逗号运算符
4.11 类型转换
4.12 运算符优先级表


4.1 基础

  • C++ 定义了一元运算符、二元运算符和三元运算符。函数调用也是一种特殊的运算符,运算对象数量没有限制
  • 表达式求值中,运算对象类型转换
  • 重载运算符:当运算符作用于类类型的运算对象时,用户可自行定义含义。使用重载运算符时,其包括运算对象的类型和返回类型,都是由该运算符定义的;但是运算对象的个数、运算符优先级和结合律都是无法改变的
  • 优先级、结合律、括号无视普通的组合规律
  • 求值顺序,如f1()*f2()中哪个函数先被调用(未明确规定);对于未指定求值顺序的运算符,若表达式指向并修改了同一个对象,则行为未定义。如cout<<i<<i++; 编译顺序未知
  • 建议:1、拿不准优先级和结合律时,用括号。2、若表达式某处改变了某对象的值,则其他地方不要使用它。例外:*++iter

4.2 算术运算符

  • 一元运算符优先级>乘法和除法>加法和减法;优先级相同时按照从左向右的顺序进行组合
  • 在表达式求值之前,小整数类型的运算对象被提升成较大的整数类型,所有运算对象最终会转换成同一类型
  • 一元正号运算符、加法运算符和减法运算符都能作用于指针。当一元正号运算符作用于一个指针或者算术值时,返回运算对象值的一个(提升后的)副本。一元负号运算符对运算对象值取负后,返回其(提升后)副本,布尔值不应该参与运算bool b=true;bool b2=-b //b2是true ,对于是大多数运算符来说,布尔类型的运算对象将被提升为int类型,如上线转换为1,然后-1,再转换为布尔值后为1,所以为真
  • 溢出:当计算结果超出该类型所能表示的范围时就会产生溢出。数值类型可能产生环绕
  • 整数相除还是整数,商有小数直接弃除。取余运算对象必须是整数类型。(-m)/n 和 m/(-n) 都等于 -(m/n) , m%(-n)等于m%n , (-m)%n等于-(m%n)

4.3 逻辑和关系运算符

  • 关系运算符作用于算术或指针类型,逻辑运算符作用于任何能转换为bool的类型。它们的返回类型都是bool型,运算对象和求值结果都是右值
  • 逻辑与运算符和逻辑或运算符,策略短路求值,左侧运算对象也可确保右侧运算对象求值过程的正确性和安全性
  • 关系运算符 if(i<j<k) //若k大于1则为真,实现目的采用 if(i<j && j<k)
  • 进行比较运算时,除非比较对象是布尔型,否则不要使用布尔字面值true和false作为运算对象,因为比较之前会把bool转发为比较对象的类型,如val==true 只有当val为1的时候条件才为真

4.4 赋值运算符

  • 赋值运算符的左侧运算对象必须是一个可修改的左值。如果赋值运算符左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象类型
  • 允许使用花括号括起来的初始值列表
  • 无论左侧运算对象的类型是什么,初始值列表都可以为空,编译器创建初始值
  • 赋值运算满足右结合律。对于多重赋值语句中的每一个对象,它的类型或者与右边的类型相同、或者可由右边的对象类型转换得到
  • 赋值运算优先级较低,所以在条件语句中,赋值部分通常应该加上括号
  • 复合运算符 a op= b 等价于 a = a op b

4.5 递增和递减运算符

  • 前置版本:先将运算对象+/-1,然后将改变的对象作为求值结果。后置版本:也会将运算对象+/-1,但是求值结果是运算对象改变之前那个值的副本。前置版本将对象本身作为左值返回,后置版本则将对象原始值的副本作为右值返回。除非必须,否则不用递增递减运算符的后置版本。
  • cout<< *p++ 输出当前值并将p向前移动一个元素。后置递增运算符的优先级高于解引用运算符。cout<<*iter++<<endl; 要比 cout<<*iter<<endl; ++iter; 简洁
  • 如果表达式改变了某个运算对象的值,另一个表达式又要使用该值的话,运算对象的求值顺序就很关键了。如*b=toupper(*b++); //错误,该赋值语句未定义,编译器可以按照如下两种思路处理该表达式 *b=toupper(*b) 先求左侧的值 或者 *(b+1)=toupper(*b)先求右侧的值

4.6 成员访问运算符

  • 点运算符和箭头运算符都可用于访问成员。表达式ptr->mem 等价于 (*ptr).mem 因为解引用运算符的优先级低于点运算符,所以执行解引用的运算的子表达式两端必须加上括号。 *p.size() // 错误,p是一个指针,它没有名为size的成员

4.7 条件运算符

  • 条件运算符(?:) cond?expr1:expr2;
  • 嵌套条件运算符,在输出表达式中使用条件运算符。条件运算符的优先级非常低
    cout<<((grade<60)? "fail": "pass"); //输出fail或pass
    cout<<(grade<60)? "fail": "pass"; //输出1或者0,
    cout<<grade<60? "fail": "pass"; //错误,小于运算符的优先级低于移位运算符,所以cout<<grade; 先输出grade。然后比较cout和60!cout< 60 ? ""

4.8 位运算符

在这里插入图片描述

  • 运算符如何处理运算对象的符号位依赖于机器
  • 移位运算符,二进制位或者向左移(<<)或者向右移(>>),移出边界之外的位就被舍弃掉了。左移运算符在右侧插入值为0的二进制位;右移运算符的行为则依赖于左侧运算对象的类型
  • 位求反运算符,将运算对象逐位求反后生成一个新值,将1置为0,将0置为1
  • 移位运算符(又叫IO运算符)满足左结合律。移位运算符的优先级不高不低,介于中间:比算术运算符的优先级低,但比关系运算符、赋值运算符和条件运算符的优先级高。

4.9 sizeof运算符

  • sizeof()运算符返回的是表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,所得值是一个size_t类型的常量表达式 。运算符的运算对象有两种形式:sizeof(type) 和 sizeof expr;sizeof不需要真的解引用指针就能知道它所指对象的类型
  • sizeof 运算符的结果部分依赖于其作用的类型:
    对char或者类型为char的表达式执行sizeof运算,结果为1
    对引用类型执行sizeof运算得到被引用对象所占空间大小
    对指针指向sizeof运算得到指针本身所占空间的大小
    对解引用指针执行sizeof运算得到指针所指向的对象所占空间的大小,指针不需有效
    对数组执行sizeof运算得到整个数组所占空间大小。注意sizeof运算不会把数组转换成指针来处理
    对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间

4.10 逗号运算符

  • 逗号运算符含有两个运算对象,经常被用在for循环中

4.11 类型转换

  • 两种类型可以相互转换,那么它们就是关联的。C++语言不会直接将两个不同类型的值相加,而是先根据类型转换规则设法将运算对象类型统一后,再求值。上述类型转换是自动执行,称为隐式转换

  • 在下面这些情况下,会发生隐式类型转换
    在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型
    在条件中,非bool值转换成bool类型
    初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型
    如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型
    函数调用时也会发生类型转换

  • 算术转换的含义是吧一种算术类型转换成另外一种算术类型。算术转换的规则定义了一套类型转换层次,其中运算符的运算对象将转换成最宽的类型

  • 整型提升负责把小整数类型转换成较大整数类型。如false提升为0,true提升为1

  • 无符号类型的运算对象转换结果依赖于机器中各个整数类型的相对大小了。如果不是无符号,运算符的运算对象将转换成同一种类型。运算中,如果其中的无符号类型不小于带符号类型,那么带符号的运算对象转换为无符号的 unsigned int 和 int ,则int转换为unsigned int;带符号类型大于无符号类型,此时转换的结果依赖于机器。1.如果无符号类型的所有值都能存在该带符号类型中,则无符号类型运算对象转换成带符号类型。2.如果不能,那么带符号类型的运算对象转换成无符号类型。如long 和 unsigned int ,并且int和long的大小相同,则long类型的运算对象转换为unsigned int类型;如果long类型占用的空间比int更多,则unsigned int 类型的运算对象转换成long类型

  • 理解算术转换
    在这里插入图片描述
    在这里插入图片描述

  • 其他隐式类型转换数组转换成指针,当数组被用作decltype关键字的参数,或者作为取地址符(&)、sizeof及typeid等运算符的运算对象时,上述转换不会发生。用一个引用来初始化数组,上述转换也不会发生。指针的转换,常量整数值0或者字面值nullptr能转换为任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*。转换成布尔类型,存在一种从算术类型或指针类型向布尔类型自动转换的机制。转换成常量,允许将指向非常量类型的指针转换成指向相应的常量类型的指针,相反的转换并不存在。类类型定义的转换,类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。如while(cin>>s) //while的条件部分把cin转换成布尔值,io库定义了从istream向布尔值转换的规则

  • 显式转换命名的强制类型转换 cast-name<type>(expression) cast-name可选static_cast、dynamic_cast、const_cast 和 reinterpret_cast
    static_cast:任何具有明确定义的类型转换,只要不包含底层的const,都可以使用static_cast进行转换。对于编译器无法自动执行的类型转换非常有用,强制转换的结果将与原始地址值相等。
    dynamic_cast:支持运行时类型转换,19.2节详细介绍
    const_cast:只能改变运算对象的底层const,只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误,const_cast只改变常量属性。常常用于有函数重载的上下文中
    reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释。使用reinterpret_cast十分危险
    旧式的强制类型转换 type(expr) //函数形式的强制类型转换 (type)expr //C 语言风格的强制类型转换


4.12 运算符优先级表

在这里插入图片描述

在这里插入图片描述

发布了46 篇原创文章 · 获赞 15 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/sinat_34686158/article/details/104326934