《C++ Primer》第4章 表达式(二)

参考资料:

  • 《C++ Primer》第5版
  • 《C++ Primer 习题集》第5版

4.6 成员访问运算符(P133)

点运算符和箭头运算符都可用于访问成员,ptr->mem 等价于 (*ptr).mem 。箭头作用于指针类型对象,结果为左值;点运算符作用域对象,如果对象为左值则结果为左值,否则为右值。

4.7 条件运算符(P134)

条件运算符的形式如下:

cond ? expr1 : expr2;

其中,当 cond 为条件判断的表达式, expr1expr2 是两个类型相同或可转换为某个公共类型的表达式。条件运算符首先对 cond 求值,若结果为真,则对 expr1 求值并返回该值;反之,则对 expr2 求值并返回该值。当两个表达式都是左值或能转换成同一种左值类型时,运算的结果为左值,否则为右值。

嵌套条件运算符

string finalgrade = (graph > 90) ? "high pass"
    							 : (grade < 60) ? "fail" : "pass";

条件运算符满足右结合律

在输出表达式中使用条件运算符

条件运算符的优先级非常低,所以通常要在其两端加上括号。

4.8 位运算符(P135)

image-20230928154102763

位运算符如何处理符号位依赖于机器,所以强烈建议将位运算符用于无符号类型

移位运算符

移位运算符的右侧对象不能为负,且值必须严格小于结果的位数,否则将产生未定义行为。

需要注意的是,有符号类型在右移时插入符号位还是 0 由具体环境决定。

位求反运算符

位与、位或、位异或运算符

移位运算符满足左结合律

使用 coutcin 进行 IO 操作时会用到重载的移位运算符,重载运算符的优先级和结合律不变。移位运算符的优先级比算术运算符低,比关系运算符、赋值运算符和条件运算符高。

4.9 sizeof运算符(P139)

sizeof 运算符满足右结合律,结果为 size_t 类型的常量表达式:

sizeof(type);
sizeof expr;

sizeof 可以返回表达式结果类型的大小,但不实际计算运算对象的值:

sizeof(*p);
sizeof Sales_data::revenue;

正是由于上述特性,即使 p 是一个无效指针,此处的解引用操作也不会有负面影响。C++11 新标准还允许通过作用域运算符来获取类成员的大小。

sizeof 运算符需要注意的情形:

  • sizeof 运算符作用于数组时,不会把数组转换成指针,而是返回整个数组所占空间的大小,等价于对数组中所有元素各执行一次 sizeof 并求和。
  • sizeof 作用于 vectorstring 对象时将将返回一个固定的值,这个值与对象所含的元素个数无关。

获取数组元素个数的常见写法:

sizeof(arr)/sizeof(*arr);

4.10 逗号运算符(P140)

逗号运算符含两个运算对象,从左向右依次求值。逗号运算符首先对左侧运算对象求值,然后将求值结果丢弃,再对右侧运算对象求值并将该值作为结果(如果右侧运算对象的结果为左值,那么逗号运算符的结果也为左值)。

4.11 类型转换(P141)

int ival = 3.14 + 3;

上述表达式发生了隐式转换。算术类型之间的隐式转换被设计得尽可能避免损失精度,所以整型一般会转换成浮点型。因此,上面的表达式中的 3 首先被转换成 double 型,然后再将浮点数加法的结果转换成 int 型。

何时发生隐式类型转换

  • 在大多数表达式中,比 int 小的整型值会提升为较大的整数类型。
  • 条件中,非布尔值转换成布尔值

4.11.1 算术转换(P142)

算术转换的规则:运算对象转换成最宽的类型、整型转换成相应的浮点型。

整型提升

boolcharshort 等小整型,只要它们所有可能的值能存储在 int 里,就会被提升成 int ,否则被提升成 unsigned int

较大的 char 类型 (如 wchar_t )会被提升成最小能容纳其所有值的整型(至少为 int )。

无符号类型的运算对象

表达式首先执行整型提升,若提升后的运算对象均为带符号或均为无符号,则将小类型转换为大类型即可。

如果一个运算对象是无符号类型,一个对象是有符号类型,且那个无符号类型不小于有符号类型,则将带符号对象转换成无符号对象(如 intunsigned int 运算,要将 int 转换成 unsigned int );如果带符号类型大于无符号类型,此时如果带符号类型能容纳无符号类型的所有值,则将无符号对象转换成有符号对象,否则将有符号对象转换成无符号类型(如 longunsigned int ,转换的结果取决于 unsigned int 所占空间的大小)。

理解算术转换

3.14L + 'a';	// 'a'先被提升成int,再转换成long double

4.11.2 其他隐式类型转换(P143)

数组转换成指针:在大多数情况中,数组会自动转换成指向数组首元素的指针。当数组作为 decltype 关键字的参数,或 &sizeoftypeid 等运算符的对象时,上述转换不会发生。

指针的转换:常量整数值 0 和字面量 nullptr 能转换成任意类型的指针;任意指向非常量的指针能转换成 void* ;任意指向对象的指针能转换成 const void*

转换成布尔类型:如果指针类型或算术类型的值为 0 ,则结果为 false ,否则为 true

转换成常量:将指向非常量的指针或引用转换成相同的常量类型的指针或引用。

类类型定义的转换:类类型能定义由编译器自动执行的转换。

4.11.3 显式转换(P144)

命名的强制类型转换

cast-name<type>(expression);

其中,type 是转换的目标类型expression 是要转换的值,如果 type 是引用类型,则转换结果为左值。cast-namestatic_castdynamic_caseconst_castreinterpret_cast 中的一种

static_cast

任何具有明确定义,且不包含底层 const 的转换都可以使用 static_cast

int i = 1, j = 1;
double dval = static_cast<double>(j) / i;

当我们需要将大类型赋值给小类型不计较精度损失时,使用 static_cast 可以避免编译器输出警告信息。

static_cast 常用于找回 void* 中的值:

double d = 3.14;
void *p = &d;
double *dp = static_cast<double*>(p);

const_cast

const_cast 只能改变对象的底层 const

const char *pc = nullptr;
char *p = const_cast<char*>(pc);    // 正确
int *p = const_cast<int*>(pc);    // 错误

如果对象本身不是一个常量,使用 const_cast 获得写权限是合法的,否则将产生未定义行为:

int i = 1;
const int ci = 1;
const int *p1 = &i, *p2 = &ci;
int *q1 = const_cast<int*>(p1);    // 合法
int *q2 = const_cast<int*>(p2);    // 利用q2对ci进行写操作将会产生未定义行为

reinterpret_cast

reinterpret_cast 可以从位存储的角度对对象进行重新解释:

int *ip = nullptr;
char *pc = reinterpret_cast<char*>(ip);

应该尽可能避免使用强制类型转换

旧式的强制类型转换

早期的 C++ 版本中,显示强制类型转换包括以下两种类型:

type(expr);
(type)expr;

旧式的强制类型转换具有与 static_castconst_castreinterpret_cast 相似的行为。

4.12 运算符优先级表(P147)

image-20231007204724573 image-20231007204813931
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值