您的位置:首页 > 编程语言 > C语言/C++

C++ Primer 读书笔记 – 第五章

2010-06-25 09:48 369 查看
 第5章 表达式

⒈ 表达式(expression_r)是一个C++程序中最低级的计算,由一或多个用一个操作符(operator)连接起来的操作数(operands)组成

⒉ 每个表达式都产生一个结果。表达式可以用作操作数,因此可用多个操作符编写复合表达式

⒊ 在求解表达式的过程中如果需要储存运算结果,编译器会自动创建没有名字的临时对象(temporary object),这些对象会在外围最大的表达式结束后释放

⒋ 表达式是否合法、合法表达式含义如何(执行什么操作、结果是什么类型)均取决于操作数的类型

⒌⒈ 算术操作符

⒈ 按优先级从高到低排列为:

一元正+, 一元负-; 乘法*, 除法/, 取模%; 加法+, 减法-

⒉ 关于除法/

⑴ 两整数相除结果仍为整数,商的小数部分被截去

⑵ 一正一负两整数相除,结果值向0一侧还是向-∞一侧取整依赖于机器

⒊ 关于取模

⑴ 操作数只能为整型

⑵ 两操作数均为负时结果为负或0;

两操作数一正一负时,结果的符号随哪个操作数而定依赖于机器

⒌⒉ 关系操作符和逻辑操作符

⒈ 按优先级从高到低排列为:

逻辑非!; 小于<, 小于等于<=, 大于>, 大于等于>=; 相等==, 不等!=; 逻辑与&&; 逻辑或||

⒉ 关系操作符和逻辑操作符接受算术或指针型操作数,并返回 bool 型值

逻辑操作符视其操作数为条件表达式

⒊ 逻辑与&&和逻辑或||操作符支持短路求值(short-circuit evaluation)

⒋ 不应串接使用关系操作符

形如i<j<k的表达式得不到预期结果

⒌⒊ 位操作符

⒈ 按优先级从高到低排列为:

位取反~; 左移<<, 右移>>; 位与&; 位异或^; 位或|

⒉ 位操作符使用整型操作数,将其视为二进制位的集合

由于负整数的符号位如何处理依赖于机器,因此应使用 unsigned 整型操作数

⒌⒋ 赋值操作符

⒈ 赋值操作符的左操作数须为非 const 左值

赋值表达式的结果即为其左操作数(左值)

⒉ 赋值操作符从右向左结合,因此当各操作数都有相同的通用类型时,允许在一个表达式中进行多次赋值,如:

i = j = k = 0;

⒊ 复合赋值操作符

对于任意二元算术操作符或二元位操作符 op

a op= b;

相当于

a = a op b;

二者显著的差别在于前者只计算了一次左操作数,后者则计算了两次

⒌⒌ 自增和自减操作符

⒈ 自增++和自减–操作符为对象加1或减1提供了方便简短的实现方式,有前置和后置两种使用形式

前置操作返回加(减)1后的对象(左值),后置操作返回操作数的原值(右值)

⒉ 出于性能考虑,应只在必要时才使用后置操作符

后置操作符需要先保存原值以便返回,而前置操作符只需加(减)1后直接返回对象即可

⒌⒍ 箭头操作符

箭头操作符用于获取指针指向类类型对象的成员。

下面两个表达式等价:

p->foo;

(*p).foo;

⒌⒎ 条件操作符

⒈ 条件操作符是C++中唯一的三元操作符,能将简短的 if-else 语句嵌入表达式

格式为 cond ? expr1 : expr2

首先计算 cond 值,若为 true 则计算并返回 expr1, 为
false 则计算并返回 expr2

⒉ 避免深度嵌套以保证可读性

⒌⒏ sizeof 操作符

sizeof 操作符用于返回一个对象或类型的大小(单位字节,类型
size_t)

形式为 sizeof expr

或  sizeof(type_name)

sizeof 表达式的结果是编译时常量

对表达式使用 sizeof 时该表达式的值并不会被计算

⒊ 对数组作 sizeof 将得到整个数组在内存中的长度

⒌⒐ 逗号操作符

逗号表达式是一组由逗号分隔的表达式,从左向右计算并返回最右边表达式的值(若该表达式为左值则返回左值)

⒌⒒ new
delete 表达式

new
delete 表达式分别用于动态创建和释放单个对象或一个数组

new 表达式

new 表达式动态创建单个对象或一个数组,并返回指向该对象或数组首元素的指针

① 动态数组维数为0值时也将返回有效的非零指针,但不能解引用

② 可以创建动态的 const 对象或数组,它们无法修改但可以释放

③ 如果无法获取需要的空间,系统将抛出 bad_alloc 异常

⑵ 分配与对象(或数组元素)初始化

① 默认内置类型不初始化,类类型调用默认构造函数(必须提供)

单个对象: new [const] 类型名

一个数组: new [const] 类型名[维数]

② 添加空括号()可执行值初始化(value initialization)

即内置类型对象置为0,类类型对象调用默认构造函数(必须提供)

单个对象: new [const] 类型名()

一个数组: new [const] 类型名[维数]()

注:对于后者,我使用的编译器中,VC初始化后会把元素置为0,但mingw不会

③ 执行直接初始化

单个对象: new [const] 类型名(初始化式)

动态数组元素不支持类似初始化方式

delete 表达式

⑴ 由 new 动态分配的对象或数组需要显式地使用
delete 表达式释放(否则会造成内存泄漏)

delete ptr 和
delete[] ptr 分别释放单个对象和动态数组

① 如果指针不指向由 new 分配的对象,则对其
delete 不合法

例外:对零指针 delete 是合法的

② 读写已释放的对象或对同一内存空间多次 delete 都可能导致错误

因此 delete 之后应立即重置该指针的值(一般重置为0)

③ 释放动态数组如果丢掉方括号[],将可能导致编译器无法发现的错误

⒌⒑ 复合表达式的求值

⒈ 含有两个或以上操作符的表达式称为复合表达式(compound expression_r)

操作数和操作符的结合方式决定了符合表达式的值,而前者取决于操作符的优先级和结合性:

操作数优先与优先级更高的操作符结合;操作符优先级相同时则由结合性决定结合方向

⒉ 圆括号()凌驾于优先级之上

若不确定结合方式,则使用圆括号()强制确定操作数的组合

⒊ 结合方式确定并不意味着操作数的计算顺序并不确定

若修改了操作数的值,则不要在同一语句的其它地方再使用它,除非操作数的计算次序不成问题


运算符优先级与结合性表

⒌⒓ 类型转换

⒈ 隐式类型转换

两个类型间存在可转换关系,则称两个类型相关

⑴ 何时发生

当编译器期望获得某种类型的数据却得到另一种类型的,就会尝试自动转换类型

具体情况包括:

① 表达式中不同类型的多个操作数被转换到同一类型

② 用作条件的表达式被转换为 bool

③ 用一表达式对某变量初始化或赋值时,前者转换为后者的类型

④ 给函数传递实参和返回值时可能发生隐式类型转换

⑵ 内置类型转换规则

① 算术类型间的转换

ⅰ 浮点型转换为整型时小数部分被抛弃(在赋值、初始化等情况下发生)

ⅱ 二元(算术、关系等)操作符表达式中,为试图保留精度会把较小的类型转换为较大的类型

a类型提升

· 将比 int 小的整型提升为
int 或(当 int 不足以容纳原类型的所有可能值时)unsigned
int

· 将 float 提升为
double

signed
unsigned 之间的转换

· 对于 signed 较小类型和
unsigned 较大或相同类型,前者转换为后者

· 对于 signed 较大类型和
unsigned 较小类型,如何操作依赖于机器:

前者若能表示后者所有可能值则后者转换为前者,否则都转换为前者的 unsigned 形式

以上两种情况,负值有可能会被转换为 unsigned 导致溢出,造成意料之外的结果

② 转换为指针

ⅰ 表达式中的数组名会自动转换为指向数组首元素的指针

但以下情况除外:

a数组作为取地址操作符&的操作数

b数组作为 sizeof 操作符的操作数

c使用数组对数组的引用初始化时

ⅱ 指向任意类型对象的指针都可转换为 void* 型

ⅲ 整型常量0可转换为任意指针类型

bool 类型转换

ⅰ 算术值和指针值转换为 bool
0 → false 非0 →
true

bool 值转换为算术类型
true → 1
false → 0

④ 枚举成员转换为整型

枚举类型对象或枚举成员可自动转换为整型(具体类型依赖于机器和枚举成员最大值,但至少为int)

⑤ 转换为 const 对象

const 对象在初始化相关的
const 型引用时自动转换为 const

const 对象的地址或指向非
const 对象的指针也可转换为指向 const 对象的指针

⒉ 显式类型转换

⑴ 若可能,避免使用强制类型转换

确实需要使用时也应尽量小心

⑵ 命名的强制类型转换

dynamic_cast

用于运行时类型识别(RTTI)

const_cast

可以添加或去除指针、数据成员指针或引用的 const 特性

static_cast

ⅰ 可以显式完成编译器隐式执行的任何类型转换

因潜在精度损失产生的编译器警告会被关闭

· 可以用以避免不必要的隐式转换,如

已知 d 为 double, i 为
int, 将 i*=d; 写成 i*=static_cast<int>(d); 可省去将 i 转换为
double 这一非必要步骤

reinterpret_cast

为操作数的位模式提供较低层次的重新解释,结果依赖编译器和机器

任何不当使用都可能导致运行时错误

⑶ 旧式强制转换

在合法使用 static_cast
const_cast 的地方,提供与命名强制转换一样的功能

若两种转换均不合法,就执行 reinterpret_cast

由于不易判别每个显式转换的潜在风险,不推荐使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: