C++Primer学习第四章

4.1 基础

  1. 作用于几个对象就是几元运算符
  2. 理解运算符的 优先级、结合律、求值顺序有助于理解复杂表达式
  3. 在表达式求值过程中,常常发生运算对象类型的改变,例如,二元运算符一般都要求运算的两个对象类型相同,整数能转化位浮点数,浮点数也能转化为整数,而bool short char通常也会被提升为int
  4. 当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
  5. 无法确定运算符优先级,最好用括号,括号无视运算符优先级
  6. 一般的运算符没有规定运算对象的求值顺序,逻辑与(&&),逻辑或(||),条件(?:)和逗号(,)运算符规定先求左侧运算对象的值
  7. 书写表达式时,拿不准的情况最好用括号来强制让表达式的组合关系符合逻辑要求
  8. 书写表达式时,如果表达式改变的某个运算对象的值,就不要再在表达式的其他地方使用这个运算对象

4.2 算术运算符

  1. 在表达式求值之前,小整数类型的运算对象被提升为较大的整数类型
  2. 对大多数运算符,bool类型会被提高为int, true:1 false:0
  3. 算数运算符使用要注意溢出和其他运算符异常

4.3 逻辑和关系运算符

  1. 进行比较运算时除非比较的对象是bool值,否则不要使用bool字面值true和false作为运算对象

4.4 赋值运算符

  1. C++11新标准运行使用大括号括起来的初始值列表进行赋值
  2. 赋值运算符满足右结合律,即允许 a=b=0
  3. 赋值运算符的优先级较低,因此常常给赋值运算加上括号
  4. 注意不要混淆相等运算符和赋值运算符

4.5 递增和递减运算符

  1. 递增递减运算符可用于迭代器,因为有些迭代器本身不支持算数运算
  2. 递增递减运算符分为两种,前置版本(++i)和后置版本(i++),前置版本先将运算符对象加一然后将运算后的对象作为求值结果,后置版本也会把运算对象加一,区别是它把运算前对象的副本作为运算结果
  3. 非必要情况时最好使用前置版本,因为后置版本需要将运算前的值保存下来,如果不需要使用改变前的值,这种操作就是一种浪费
  4. P++,相当于(p++),递增后,解引用p未递增前的值

4.6 成员访问运算符

  1. 点运算符(.)可以用于访问类对象的一个成员
  2. 箭头运算符(->)可以访问指针指向对象的成员
  3. ptr->men = (*ptr).mem

4.7 条件运算符

  1. cond?a:b; cond是条件,满足返回a,不满足返回b
  2. 条件运算的嵌套最好不要超过三层

4.8 位运算符

  1. 位求反(~),位左移(<<),位右移(>>),位与(&),位异或(^),位或(|)
  2. 如果运算对象是小整型,则它的值会被自动提升为大整型
  3. 运算对象可以是有符号的也可以是无符号的,有符号的对象进行左移可能会改变符号位的值,因此进行位运算的对象最好是无符号的。
  4. 左移:先把小整型提升为整型,然后向左移位,右边补零
  5. 右移:先把小整型提升为整型,如果运算对象是无符号类型,就在左侧补零。如果运算对象是带符号类型,则在左侧插入符号位的副本或补零。
  6. 位求反:位求反运算符将运算对象提升至int型后按位求反后得到新值
  7. 位与:两个运算对象提升至整型后按位与
  8. 或:两个运算对象提升至整型后按位取或
  9. 异或:两个运算对象提升至整型后按位异或(都是1取0,否则取1)

4.9 sizeof运算符

sizeof运算符返回一条表达式或一个类型名字所占的字节数。

sizeof运算符的对象有两种形式:
1、sizeof(type)
2、sizeof expr 返回表达式expr结果类型的打小,并不计算其运算对象的值

  1. sizeof对char,结果为1
  2. sizeof对引用类型,得到被引用对象所占空间的大小
  3. sizeof对指针,得到指针本身所占空间的大小
  4. sizeof对数组,得到整个数组大小,相当于对数组中每一个元素sizeof后求和
  5. sizeof对解引用指针,得到指针指向对象的大小
  6. tips:使用sizeof得到数组的大小
    #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))

4.10 逗号表达式

逗号表达式含有两个运算对象,按照从左向右的运算顺序依次求值,先对左侧表达式求值,然后把值丢弃,运算符真正的结果是右侧表达式的值。

逗号表达式常用于语法上只要一个表达式,而逻辑上需要多个表达式的情况。例如for循环中。
image.png

4.11 类型转换

**隐式转换:**自动执行的类型转换,无需程序员介入

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

4.11.1 算术转换

运算符的运算对象将转换为最宽的数据类型,例如,一个运算对象为long double,则不管另一个运算对象的类型是什么,都会转换为long double.
当表达式中既有浮点类型又有整型时,整数值将转换为相应浮点类型。

整数提升 整数提升负责把小整数类型转换成较大的整数类型
如果某个运算符的运算对象类型不一致,则运算对象将被转换为同一种类型。
像bool,char ,signed char,unsigned char,short和unsigned short来说,只要int能够存储下他们所有可能取到的值,它们就会被提升为int类型,否则提升为unsigned int。较大的char类型(wchar_t、char16_t、char32_t)提升为成int、unsigned int 中的一种,前提是转换后的类型能够存储下它们所有可能取到的值。
无符号类型的运算对象:如果某个运算符的运算对象类型不一样,则这些运算对象将会转化为同一种类型。
首先,执行整型提升。如果提升后的的类型相同,则不需要再转换。
如果提升后的类型不同,但都是有符号的或都是无符号的,则小类型运算对象转化为较大的类型(比较的是类型的大小,而不是值的大小)。
如果提升后类型不同,且一个是有符号的,另一个是无符号的,则:
a). 无符号类型不小于带符号类型,则带符号类型转为无符号类型。如unsigned int 和int, unsigned int 类型等于int类型,则int类型对象转为unsigned int类型。
b). 如果带符号类型大于无符号类型,此时转换结果依赖于机器。如果无符号类型的所有值都能存储在该带符号类型中,则无符号类型的运算对象转化为该带符号类型。如果不能,则带符号类型的运算对象装换为无符号类型。

4.11.2 其他隐式类型转换

  • 数组转换为指针:在大多数用到数组的表达式中,数组会被转化为指向数组首元素地址的指针,既然是大多数,那就有例外,当数组作为decltype关键字的参数,或者作为取地址符(&)、zizeof及typeid等运算符的运算对象时,不会被转换为指针。
  • 指针的转换
    a. 常量整数值0或者字面值nullptr能转换为任意指针类型
    b. 指向任意非常量的指针能够转化为void*
    c. 指向任意对象的指针能转化为const void*
  • 转换成bool类型:如果指针或者算术类型的值为0,转换为false,否则为true.
  • 转换为常量:可以将指向非常量类型的指针转换为指向常量类型的指针。如T是一种类型,我们就能把指向T的指针或者引用分别转换成指向const T的指针或者引用。
  • 类类型的转换:编译器每次只能执行一种类类型的转换。

4.11.3 显式类型转换

有时我们需要进行显式的强制类型转换,但是这种操作是非常危险的,最好不要轻易使用。
命名的强制类型转换
形式:cast-name<type>(expression);
cast-name:static_cast、dynamic_cast、const_cast和reinterpret_cast中的一个
type:要转换成的目标类型
expression:待转换的值
例如:

int i = 3;
int j = 2;
double a = static_cast<double>(j)/static_cast<double>(i);

猜你喜欢

转载自blog.csdn.net/mataojie/article/details/121438518