第 4 章 表 达 式
本 章 描 述 C++ 的 表 达 式 , 表 达 式 是 用 于 一 个 或 多 个 以 下 目 的 的 运 算 符 和 操 作 数 序列 :
-
从 操 作 数 计 算 出 一 个 值
-
设 计 对 象 或 函 数
-
产 生 “ 副 作 用 ” ( 副 作 用 是 非 表 达 式 求 值 的 任 何 动 作 , 例 如 , 修 改 一个 对 象 的 值 ) 。
在 C++ 中 , 运 算 符 可 被 重 载 而 且 它 们 的 含 义 可 由 用 户 定 义 , 但 是 它 们 的 优 先 级 以及 所 带 操 作 数 的 个 数 不 能 被 修 改 。 本 章 描 述 该 语 言 中 所 提 供 的 而 非 重 载 的 运 算符 的 语 法 和 语 义 , 包 括 以 下 主 题 :
-
表 达 式 的 类 型
-
表 达 式 的 语 义
-
造 型 转 换
( 有 关 重 载 的 运 算 符 的 更 多 信 息 参 见 第 12 章 “ 重 载 ” 中 的 “ 重 载 的 运 算 符 ” ) 。
注 意 : 内 部 类 型 的 运 算 符 不 能 被 重 载 , 它 们 的 行 为 是 预 先 定 义 好 的 。
表 达 式 的 类 型
C++ 表 达 式 分 为 以 下 几 类 :
-
基 本 表 达 式 , 这 些 是 构 成 所 有 其 它 表 达 式 的 基 础 。
-
后 缀 表 达 式 , 这 些 是 基 本 表 达 式 后 面 跟 随 一 个 运 算 符 , 例 如 , 数 组 下标 或 后 缀 增 1 运 算 符 。
-
由 单 目 运 算 符 构 成 的 表 达 式 , 单 目 运 算 符 只 能 作 用 表 达 式 中 的 一 个操 作 数 。
-
由 双 目 运 算 符 构 成 的 表 达 式 , 双 目 运 算 符 作 用 于 表 达 式 中 的 两 个 操作 数 。
-
带 条 件 运 算 符 的 表 达 式 , 条 件 运 算 符 是 一 个 三 目 运 算 符 , 在 C++ 语 言中 只 有 这 种 运 算 符 带 三 个 操 作 数 。
-
常 量 表 达 式 , 常 量 表 达 式 完 全 由 常 量 数 据 构 成 。
-
带 显 式 类 型 转 换 的 表 达 式 , 显 式 类 型 转 换 或 “ 造 型 转 换 ” 可 以 用 在表 达 式 中 。
-
带 成 员 指 针 运 算 符 的 表 达 式 。
-
造 型 转 换 , 类 型 安 全 的 “ 造 型 转 换 ” 可 以 用 于 表 达 式 中 。
-
运 行 类 型 信 息 , 指 出 程 序 执 行 期 间 对 象 的 类 型 。
基 本 表 达 式
基 本 表 达 式 是 构 造 更 复 杂 表 达 式 的 基 础 。它 们 是 文 字 、名 称 和 范 围 分 辨 运 算 符 (::
限 定 的 名 称 。
语 法
基 本 表 达 式 :
文字
this
:: 标 识 符
:: 运 算 符 函 数 名 称
:: 限 定 名 称
( 表 达 式 )
名称
文 字 是 一 个 常 量 基 本 表 达 式 , 其 类 型 依 赖 于 其 说 明 的 形 式 , 有 关 说 明 文 字 的 全 部信 息 参 见 第 1 章 “ 词 法 规 定 ” 中 的 “ 文 字 ”。
this 关 键 字 是 一 个 类 对 象 指 针 , 它 在 非 静 态 成 员 函 数 内 可 以 使 用 , 且 指 向 被 调 用函 数 的 类 的 实 例 ,this 关 键 字 不 能 用 在 类 成 员 函 数 体 之 外 。
this 指 针 的 类 型 是 函 数 内 的 type *const( 其 中 type 是 类 名 ), 它 特 别 地 不 修 改this 指 针 。 以 下 例 子 给 出 了 成 员 函 数 说 明 以 及 this 类 型 :
class Example
{
public:
void Func(); //*const this
void Func() const; //const * const this
void Func() volatile; //volatile *const this
}
有 关 修 改 this 指 针 的 类 型 的 更 多 信 息 参 见 第 8 章 “ 类 ” 中 的 “ this 指 针 的 类型 ” 。
范 围 分 辨 运 算 符 (::) 后 面 跟 随 一 个 标 识 符 、 运 算 符 函 数 名 称 或 限 定 名 称 构 成 一个 基 本 表 达 式 。 这 种 表 达 式 的 类 型 通 过 标 识 符 、 运 算 符 函 数 名 称 或 名 称 的 说 明加 以 指 定 。 如 果 说 明 的 名 称 值 为 1 ( 1- value ) , 则 它 也 是 值 为 1 (1-value ) 。 范围 分 辨 运 算 符 允 许 指 示 一 个 全 局 名 称 , 即 使 那 个 名 称 被 隐 藏 在 当 前 范 围 内 。 有 关如 何 使 用 范 围 分 辨 运 算 符 的 例 子 参 见 第 2 章 “ 基 本 概 念 ” 中 的 “ 范 围 ”。
包 含 在 圆 括 号 内 的 表 达 式 是 一 个 基 本 表 达 式 , 其 类 型 和 值 与 那 些 非 括 号 括 起 的 表达 式 的 是 完 全 相 同 的 。 如 果 非 括 号 括 起 的 表 达 式 是 l 值 的 , 则 它 也 是 l 值 的 。
名 称
在 C++ 基 本 表 达 式 的 语 法 中 , 一 个 名 称 是 一 个 基 本 表 达 式 , 它 仅 可 出 现 在 成 员 选择 运 算 符 (. 或 ->) 之 后 并 命 名 一 个 类 的 成 员 。
语 法
名 称 :
标 识 符
运 算 符 函 数 名 称
转 换 函 数 名 称
~ 类 名 称
限 定 名 称
已 被 说 明 的 任 何 标 识 符 是 一 个 名 称 。
一 个 运 算 符 函 数 名 称 是 一 个 说 明 为 如 下 形 式 的 名 称 :
operator 运 算 符 名 称 ( 参 量 1 [, 参 量 2]);
有 关 运 算 符 函 数 名 称 的 更 多 信 息 , 参 见 第 12 章 “ 重 载 ” 中 的 “ 重 载 的 运 算 符 ”。一 个 转 换 函 数 名 称 是 一 个 说 明 为 如 下 形 式 的 名 称 :
运 算 符 类 型 名 称 ()
注 意 : 在 说 明 一 个 转 换 函 数 时 , 你 可 以 用 一 个 派 生 的 类 型 名 称 如 char 替 代 类 型名 称 。
转 换 函 数 提 供 向 用 户 定 义 类 型 以 及 从 用 户 定 义 类 型 的 转 换 。 有 关 用 户 提 供 的 转换 的 更 多 信 息 , 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 转 换 函 数 ”。
被 说 明 为 类 名 称 的 名 称 被 作 为 一 个 类 类 型 对 象 的 “ 析 构 函 数 ” , 该 析 构 函 数 在 一个 对 象 生 存 期 的 结 束 地 方 执 行 清 除 操 作 。有 关 析 构 函 数 的 信 息 , 参 见 第 11 章“ 特殊 成 员 函 数 ” 中 的 “ 析 构 函 数 ”。
限 定 名 称
语 法
限 定 名 称 :
限 定 类 名 称 :: 名 称
如 果 一 个 限 定 类 名 称 后 面 跟 随 范 围 分 辨 运 算 符 (::), 再 后 是 那 个 类 或 那 个 类 的 基类 的 成 员 名 称 , 那 么 范 围 分 辨 运 算 符 被 认 为 是 一 个 限 定 名 称 , 一 个 限 定 名 称 的 类型 与 成 员 的 类 型 相 同 , 而 且 限 定 名 称 表 达 式 的 结 果 是 成 员 , 如 果 成 员 是 l 值 , 则 限定 名 称 也 是 l 值 。 有 关 说 明 限 定 类 名 称 的 信 息 , 参 见 第 6 章 “ 说 明 ” 中 的 “ 类 型指 示 符 ” 或 第 8 章 “ 类 ” 中 的 “ 类 名 称 ”。
一 个 限 定 类 名 称 的 类 名 称 部 分 可 通 过 在 当 前 或 包 围 的 范 围 中 相 同 名 称 的 重 新 说明 而 加 以 隐 藏 , 类 名 称 仍 能 够 被 找 到 和 使 用 。 有 关 如 何 使 用 一 个 限 定 类 名 称 以 访问 一 个 隐 藏 类 名 称 的 例 子 参 见 第 2 章 “ 基 本 概 念 ” 中 的 “ 范 围 ”。
注 意 : 类 名 称 :: 类 名 称 和 类 名 称 :: ~ 类 名 称 的 形 式 的 类 构 造 函 数 和 析 构 函 数 分 别必 须 指 相 同 的 类 名 称 。
带 有 多 于 一 个 限 定 的 名 称 , 如 下 面 所 示 , 指 定 了 一 个 嵌 套 类 的 一 个 成 员 : 类 名 称 :: 类 名 称 :: 名 称
后 缀 表 达 式
后 缀 表 达 式 构 成 基 本 表 达 式 或 后 缀 运 算 符 跟 在 一 个 基 本 表 达 式 后 的 表 达 式 。 后缀 运 算 符 列 在 表 4.1 中 。
表 4.1 后 缀 运 算 符
运算符名称 |
运算符符号 |
---|---|
下标运算符 |
[ ] |
函数调用运算符 |
( ) |
显式类型转换运算符类型名 |
type-nam e() |
成员选择运算符 |
. 或 -> |
后缀增 1 运算符 |
++ |
后缀减 1 运算符 |
-- |
语 法
后 缀 表 达 式 :
基 本 表 达 式
后 缀 表 达 式 [ 表 达 式 ]
后 缀 表 达 式 ( 表 达 式 表 opt )
简 单 类 型 名 ( 表 达 式 表 opt )
后 缀 表 达 式 . 名 称
后 缀 表 达 式 -> 名 称
后 缀 表 达 式 ++
后 缀 表 达 式 -- 表 达 式 表 :
赋 值 表 达 式
表 达 式 表 , 赋 值 表 达 式
下 标 运 算 符
一 个 后 缀 表 达 式 后 面 跟 随 着 下 标 运 算 符 [], 用 于 指 定 数 组 索 引 。 表 达 式 之 一 必须 为 指 针 或 数 组 类 型 , 即 必 须 已 被 说 明 为 type * 或 type[] 。 其 它 表 达 式 必 须 为一 个 整 数 类 型 ( 包 括 枚 举 类 型 ) 。 在 通 常 用 法 中 , 括 在 方 括 号 中 的 表 达 式 是 整 型 之一 , 但 并 没 有 严 格 的 要 求 , 考 虑 以 下 例 子 :
MyType m[10]; // 说 明 一 个 用 户 定 义 类 型 的 数 组
MyType n1=m[2]; // 选 择 数 组 的 第 三 元 素MyType n2=2[m]; // 选 择 数 组 的 第 三 元 素
在 前 面 例 子 中 , 表 达 式 m[2] 与 2[m] 完 全 相 同 。 尽 管 m 不 是 一 个 整 数 类 型 , 但 效 果
是 相 同 的 。 m[2] 与 2[m] 之 所 以 相 等 是 因 为 下 标 表 达 式 e1 [ e2 ] 的 结 果 由
*(( e2 )+( e1 ))
给 定 。 由 表 达 式 产 生 的 地 址 不 是 距 离 地 址 e1 的 e2 字 节 , 而 是 地 址 被 定 位 以 产 生数 组 e2 中 的 下 一 个 对 象 , 例 如 :
double aDb1[2];
aDb[0] 和 aDb[1] 的 地 址 分 别 是 8 个 字 节 , 即 — 个 double 类 型 的 对 象 的 尺 寸 , 这样 按 对 象 类 型 确 定 尺 寸 是 由 C++ 语 言 自 动 完 成 的 , 且 在 本 章 后 面 的 “ 附 加 的 运 算符 ” 中 定 义 , 其 中 还 讨 论 了 指 针 类 型 操 作 数 的 加 法 和 减 法 。
正 和 负 下 标
数 组 的 第 一 个 元 素 是 元 素 0,C++ 数 组 的 范 围 是 从 array [0] 到 array [ 尺 寸 -1], 但C++ 支 持 正 和 负 下 标 。 负 下 标 必 须 落 在 数 组 界 限 内 或 结 果 是 不 可 预 料 的 。 以 下 例子 说 明 了 这 个 概 念 :
#include <iostream.h>
void main()
{
int iNumberArray[1024];
int *iNumberLine = &iNumberArray[512];
cout << iNumberArray[-256] << "\n"; // 不 可 预 料 的
cout << iNumberLine[-256] << "\n"; // 可 行 的
}
在 iNumberArray 中 的 负 下 标 可 能 产 生 运 行 错 误 , 因 为 它 产 生 一 个 比 原 数 组 在 存储 器 中 低 256 个 字 节 的 一 个 地 址 。 iNumberLine 对 象 被 初 始 化 为 iNumberArray 的 中 间 量 , 因 此 对 它 既 可 以 使 用 正 数 组 索 引 也 可 以 使 用 负 数 组 索 引 。 数 组 下 标 错误 不 产 生 编 译 错 误 , 但 它 们 产 生 不 可 预 料 的 结 果 。
下 标 运 算 符 是 可 以 互 换 的 , 因此 , 只 要 下 标 运 算 符 不 被 重 载 , 则 表 达 式 array [ index 和 index [ array ] 被 认 为 是 相 等 的 。( 参 见 第 12 章“ 重 载 ”中 的“ 重 载 的 运 算 符 ”) 。第 一 种 格 式 是 最 常 见 的 编 码 用 法 , 不 过 两 者 都 行 。
函 数 调 用 运 算 符
- 个 后 缀 表 达 式 后 面 跟 着 函 数 调 用 运 算 符 (), 则 指 定 一 函 数 调 用 , 函 数 调 用 运 算符 的 参 量 是 0 个 或 多 个 表 达 式 , 函 数 的 实 际 参 量 由 逗 号 隔 开 。
后 缀 表 达 式 必 须 是 以 下 类 型 之 一 :
- 函 数 返 回 类 型 T, 一 个 说 明 例 子 为 :
T func(int i)
- 函 数 返 回 类 型 T 的 指 针 , 一 个 说 明 例 子 为 :
T (*func)(int i)
- 函 数 返 回 类 型 T 的 引 用 , 一 个 说 明 例 子 为 :
T (&func)(int i)
- 成 员 指 针 函 数 间 接 引 用 返 回 类 型 T, 函 数 调 用 例 子 为 :
(pObject->*pmf)();
(Object.*pmf)();
形 式 参 量 与 实 际 参 量
调 用 程 序 以“ 实 际 参 量 ”形 式 向 被 调 用 函 数 传 递 信 息 , 被 调 用 函 数 使 用 相 应 的“ 形式 参 量 ” 访 问 信 息 。
当 一 个 函 数 被 调 用 时 , 执 行 以 下 任 务 :
-
所 有 的 实 际 参 量 ( 那 些 由 调 用 者 提 供 ) 被 求 值 , 这 些 变 量 被 求 值 时 没有 隐 式 地 顺 序 , 但 在 进 入 函 数 之 前 所 有 的 变 量 被 求 值 , 且 所 有 的 副 作用 被 完 成 。
-
每 个 形 式 参 量 用 其 表 达 式 表 中 相 应 的 实 际 参 量 进 行 初 始 化 ( 一 个 形式 参 量 是 一 个 在 函 数 头 部 已 作 说 明 , 并 用 于 函 数 体 内 的 参 量 ), 转 换被 完 成 好 象 通 过 初 始 化 , 即 标 准 的 和 用 户 定 义 的 转 换 均 被 执 行 以 将实 际 参 量 转 换 为 正 确 的 类 型 。 初 始 化 的 执 行 由 以 下 例 子 进 行 概 念 性地 说 明 :
void func(int i);// 函 数 原 型
...
Func(7); // 执 行 函 数 调 用调 用 前 的 概 念 性 初 始 化 为 :
int Temp_i=7;
Func(Temp_i);
注 意 , 初 始 化 被 执 行 就 好 象 是 使 用 等 号 语 法 而 不 是 括 号 语 法 ,i 的 一 个 拷 贝 在 向函 数 传 递 值 之 前 被 作 好 ( 有 关 更 多 的 信 息 , 参 见 第 7 章 “ 说 明 符 ” 中 的 “ 初 始 化表 达 式 ” , 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 转 换 ”、“ 使 用 特 殊 成 员 函 数 的 初 始化 ” 和 “ 显 式 初 始 化 ” ) 。
因 此 , 如 果 函 数 原 型 ( 说 明 ) 调 用 一 个 long 类 型 参 量 , 而 如 果 调 用 程 序 提 供 一 个int 类 型 的 实 际 参 量 , 则 实 际 参 量 用 标 准 类 型 转 换 提 升 为 long 类型 ( 参 见 第 3 章“ 标 准 转 换 ” ) 。
提 供 一 个 实 际 参 量 而 没 有 标 准 的 或 用 户 定 义 的 转 换 将 其 转 换 为 形 式 参 量 的 类 型则 是 一 个 错 误 。
对 于 类 类 型 的 实 际 参 量 , 形 式 参 量 通 过 调 用 类 构 造 函 数 被 初 始 化 ( 有 关 这 些 特 殊类 成 员 函 数 的 更 多 信 息 , 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 构 造 函 数 ” ) 。
- 函 数 调 用 被 执 行 。
以 下 程 序 段 说 明 了 一 个 函 数 调 用 :
void fanc(long param1,double param2);
void main()
{
int i,j;
// 用 实 际 参 量 i 和 j 调 用 func
func(i,j);
...
}
// 用 形 式 参 量 param1 和 param2 定义 func void func(long param1,double param2)
...
}
当 从 main 调用 func 时 , 形 式 参 量 param1 用 i 值 ( 使 用 标 准 转 换 将 i 转 换 为 long 类 型 相 对 应 的 正 确 类 型 ) 进 行 初 始 化 , 形 式 参 量 param2 用 j 值 ( 使 用 标 准 转 换 将j 转 换 为 double 类 型 ) 进 行 初 始 化 。
参 量 类 型 的 处 理
被 说 明 为 常 量 类 型 的 形 式 参 量 不 能 在 函 数 体 内 进 行 改 变 , 函 数 可 以 改 变 任 何 非const 类 型 的 变 量 。 但 是 改 变 相 对 函 数 是 局 部 的 , 不 会 影 响 实 际 参 量 的 值 , 除 非实 际 参 量 是 一 个 非 const 类 型 对 象 的 一 个 引 用 。
以 下 函 数 说 明 了 一 部 分 这 些 概 念 :
int func1(const int i,int j,char *c)
{
i=7; // 错误 :i 是 常 量
j=1; // 可 以 , 但 j 值 在 返 回 值 丢 失
*c= ′ a ′ +j; // 可以 : 在 调 用 函 数 中 改 变 c 的 值
return i;
}
double& func2(double& d,coust char *c)
d=14.387; // 可 以 : 在 调 用 函 数 中 改 变 d 的 值
*c= ′ a ′ ; // 错 误 :c 是 一 个 指 向 常 量 对 象 的 指 针
return d;
}
省 略 号 和 缺 省 参 量
函 数 可 以 说 明 为 接 受 比 函 数 定 义 中 指 定 参 量 更 少 的 情 况 , 这 是 通 过 使 用 以 下 两 种方 法 之 一 实 现 的 : 省 略 号 (...) 或 缺 省 参 量 。
省 略 号 意 味 着 参 量 可 能 被 要 求 , 但 说 明 中 没 有 指 定 数 目 和 类 型 。 这 通 常 是 糟 糕 的C++ 编 程 方 法 , 因 为 它 使 C++ 的 益 处 之 一 即 类 型 安 全 性 失 败 。 不 同 的 转 换 用 于 使用 省 略 号 说 明 的 函 数 而 不 是 那 些 形 式 和 实 际 参 量 类 型 已 知 的 函 数 :
-
如 果 实 际 参 量 为 float 类 型 , 则 它 在 函 数 调 用 前 被 提 升 为 double 类型 。
-
任 何 有 符 号 的 或 无 符 号 的 char 、 short 、 枚 举 类 型 或 位 域 使 用 整 型提 升 被 转 换 为 有 符 号 的 或 无 符 号 的 int 。
-
任 何 作 为 一 个 数 据 结 构 的 值 传 递 的 类 类 型 参 量 ; 拷 贝 是 通 过 二 进 制拷 贝 创 建 的 而 不 是 通 用 调 用 类 的 拷 贝 构 造 函 数 ( 如 果 存 在 的 话 ) 实 现的 。
省 略 号 , 如 果 使 用 , 必 须 在 参 量 表 的 最 后 进 行 说 明 。 有 关 传 递 可 变 个 数 参 量 的 更多 信 息 , 参 见“ Microsoft Visual C++6.0 参 考 库 ”中 的“ Microsoft Visual C++6.0
运 行 库 参 考 ” 中 的 va_arg 、 va_start 和 va_list 的 讨 论 。
缺 省 参 量 能 使 你 能 够 指 定 这 样 的 一 个 参 量 的 值 , 假 设 它 在 函 数 调 用 中 不 提 供 。 以下 代 码 段 给 出 了 缺 省 参 量 的 工 作 情 况 , 有 关 指 定 缺 省 参 量 的 限 制 的 更 多 信 息 见 第7 章 中 的 “ 缺 省 参 量 ”。
#include <iostream.h>
// 说 明 一 个 print 函 数 打 印 字 符 串 及 一 个 终 止 符 。
void print (const char *string, const char *termin a tor="\n");
void main()
{
print("hello,");
print("World!");
print("good morning",",");
print("sunshine.");
}
// 定 义 print
void print(char *string, char *terminator)
{
if (string!=NULL)
cont << string;
if (terminator!=NULL)
cout << terminator;
}
前 面 的 程 序 说 明 了 一 个 函 数 print, 它 带 两 个 参 数 , 但 第 二 个 参 量 terminator 有一 个 缺 省 值 “ \n ”。 在 main 中 , 开 头 两 次 调 用 print 允 许 缺 省 的 第 二 个 参 量 提 供一 个 换 行 终 止 打 印 的 字 符 串 , 第 三 个 调 用 为 第 二 个 参 量 指 定 了 明 确 的 值 , 程 序 的输 出 为 :
hello, world!
good morning, sunsline.
函 数 调 用 结 果
- 个 函 数 调 用 求 值 为 一 个 r 值 , 除 非 函 数 被 说 明 为 一 个 引 用 类 型 。 带 引 用 的 函 数返 回 类 型 求 值 为 l 值 , 而 且 可 以 用 于 一 个 赋 值 语 句 的 左 侧 , 如 下 所 示 :
#include <iostream.h>
class Point
{
public:
// 定 义 “ accessor ” 函 数 为 引 用 类 型
unsigned & x() { return _x;}
unsigned & y() { return _y;} private:
unsigned _x;
unsigned _y;
};
void main()
{
Point ThePoint;
ThePoint.x( )=7; // 使 用 x() 作 为 一 个 l 值
unsigned y=ThePoint.y( ); // 使 用 y() 作 为 一 个 r 值
// 使 用 x() 和 y() 作为 r 值
cout << "x=" << ThePoint.x() << "\n"
<< "y=" << ThePoint.y() << "\n";
} 000000
前 面 的 代 码 定 义 了 一 个 类 调 用 Point, 包 含 表 示 x 和 y 坐 标 的 私 有 数 据 对 象 , 这些 数 据 对 象 必 须 被 修 改 且 它 们 的 值 被 检 索 。 该 程 序 只 是 这 样 的 一 个 类 的 几 种 设计 之 一 , 使 用 GetX 和 SetX 或者 GetY 和 SetY 函 数 是 另 一 种 可 能 的 设 计 。
返 回 类 类 型 、 类 类 型 指 针 或 类 类 型 引 用 的 函 数 可 被 用 作 成 员 选 择 运 算 符 的 左 操作 数 , 因 此 , 以 下 代 码 是 合 法 的 :
class A:
{
public:
int SetA(int i){ return(I=i); }
int GetA() { return I; }
private:
int I;
};
// 说 明 三 个 函 数
//func1, 返 回 类 型 A
//func2, 返 回 类 型 A 的 一 个 指 针
//func3, 返 回 类 型 A 的 一 个 引 用A func1();
A* func2();
A& func3();
int iResult=func1().GetA(); func2()->SetA(3);
func3().SetA(7);
函 数 可 被 递 归 地 调 用 , 有 关 函 数 说 明 的 更 多 信 息 参 见 第 6 章 “ 说 明 中 的 函 数 指 示符 ” , 第 8 章 “ 类 ” 中 的 “ 指 示 符 ” 和 “ 成 员 函 数 ”。 相 关 的 材 料 在 第 2 章 “ 基本 概 念 ” 中 的 “ 程 序 和 连 接 ” 中 。
成 员 选 择 运 算 符
一 个 后 缀 表 达 式 后 面 跟 随 着 一 个 成 员 选 择 运 算 符 (.) 和 一 个 名 称 , 是 一 个 后 缀 表达 式 的 另 一 种 例 子 。 成 员 选 择 运 算 符 的 第 一 个 操 作 数 必 须 有 类 或 类 引 用 类 型 , 第二 个 操 作 数 必 须 标 识 那 个 类 的 一 个 成 员 。
表 达 式 的 结 果 是 成 员 的 值 , 而 且 如 果 被 命 名 的 成 员 值 为 1, 则 它 也 值 为 1 。
一 个 后 缀 表 达 式 后 面 跟 随 着 一 个 成 员 选 择 运 算 符 (->) 和 一 个 名 称 , 是 一 个 后 缀 表达 式 。 成 员 选 择 运 算 符 的 第 一 个 操 作 数 必 须 有 一 个 类 对 象 的 类 型 指 针 ( 说 明 为class 、 struct 或 union 类 型 的 一 个 对 象 ); 第 二 个 操 作 数 必 须 指 定 那 个 类 的 一个 成 员 。
表 达 式 的 结 果 是 成 员 的 值 , 而 且 如 果 被 命 名 的 成 员 值 为 1, 则 它 也 是 值 为 1 。
-> 运 算 符 间 接 引 用 指 针 , 因 此 , 表 达 式 e->member 和 (* e ). member ( 其 中 e 表 示 一个 表 达 式 ) 产 生 完 全 相 同 的 结 果 ( 除 了 运 算 符 -> 或 * 被 重 载 时 之 外 ) 。
当 一 个 值 通 过 一 个 联 合 的 某 成 员 被 存 储 , 但 通 过 另 一 个 成 员 被 检 索 时 , 没 有 转 换被 执 行 。 以 下 程 序 将 数 据 作 为 int 存 入 对 象 U, 但 检 索 数 据 却 作 为 char 类 型 的两 个 分 开 的 字 节 :
#include <iostream.h>
void main()
{
struct ch
{
char b1;
char b2;
};
union u
{
struct ch uch;
s hort i;
};
u U;
U.i=0x6361; // “ ac ” 的 位 模 式
cout << U.uch.b1 << U.uch.b2 << "\n";
}
后 缀 增 1 及 减 1 运 算 符
C++ 提 供 前 缀 及 后 缀 增 1 和 减 1 运 算 符 ; 本 节 仅 描 述 后 缀 增 1 和 减 1 运 算 符 。 ( 更
多 的 信 息 参 见 第 12 章 “ 重 载 ” 中 的 “ 增 1 和 减 1 ” ) 。 两 者 的 区 别 在 于 , 在 后 缀表 示 法 中 , 运 算 符 出 现 在 后 缀 表 达 式 之 后 , 而 在 前 缀 表 示 法 中 , 运 算 符 出 现 在 表 达式 前 。 以 下 例 子 给 出 了 一 个 后 缀 增 1 运 算 符 :
i++
使 用 后 缀 增 1 或 “ 后 增 1 ” 运 算 符 (++) 的 作 用 是 操 作 数 增 加 相 应 类 型 的 一 个 单位 量 。 同 样 地 , 使 用 后 缀 减 1 或 “ 后 减 1 ” 运 算 符 (--) 的 作 用 是 操 作 数 减 少 相 应类 型 的 一 个 单 位 量 。
例 如 , 使 用 后 缀 增 1 运 算 符 于 一 个 long 类 型 对 象 数 组 的 一 个 指 针 , 实 际 上 指 针 内部 表 示 法 中 增 加 了 4 。 此 动 作 使 先 前 指 向 数 组 第 n 个 元 素 的 指 针 指 向 第 ( n+1 ) 个元 素 。
后 缀 增 1 和 后 缀 减 1 运 算 符 的 操 作 数 必 须 是 算 术 或 指 针 类 型 的 可 修 改 ( 非 const) 的 l 值 , 后 缀 增 1 或 后 缀 减 1 表 达 式 的 结 果 是 使 用 增 1 运 算 符 之 前 的 后 缀 表 达 式的 值 , 结 果 的 类 型 与 后 缀 表 达 式 的 相 同 , 但 不 再 是 一 个 l 值 。
以 下 代 码 说 明 了 后 缀 增 1 运 算 符 :
if (var++>0)
*p++=*q++;
在 此 例 中 , 变 量 var 与 0 相 比 , 而 后 增 1 。 如 果 var 在加 1 之 前 为 正 数 , 则 执 行 下一 条 语 句 。 首 先 , 由 q 指 向 的 对 象 的 值 赋 给 由 p 指 向 的 对 象 , 然后 ,q 和 p 都 增 1 。后 增 1 和 后 减 1, 当 被 用 于 枚 举 类 型 时 , 产 生 整 型 值 。 因 此 , 以 下 代 码 是 非 法 的 : enum Days {
Sunday=1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
void main()
{
Days Today = Tuesday;
Days SaveToday;
SaveToday = Today++; // 错 误
}
这 段 代 码 的 目 的 是 保 存 today 的 星 期 号 , 然 后 移 到 下 一 天 , 但 是 结 果 是 today++ 表 达 式 产 生 一 个 int 即 在 赋 给 枚 举 类 型 Days 的 对 象 时 出 现 一 个 错 误 。
带 单 目 运 算 符 的 表 达 式
单 目 运 算 符 仅 作 用 于 表 达 式 中 的 一 个 操 作 数 , 单 目 运 算 符 有 :
-
间 接 运 算 符 (*)
-
取 地 址 运 算 符 (&)
-
单 目 加 运 算 符 (+)
-
单 目 负 运 算 符 (-)
-
逻 辑 非 运 算 符 (!)
-
“ 1 ” 的 求 补 运 算 符 (~)
-
前 缀 增 1 运 算 符 (++)
-
前 缀 减 1 运 算 符 (--)
-
sizeof 运 算 符
-
new 运 算 符
-
delete 运 算 符
这 些 运 算 符 具 有 从 右 向 左 的 结 合 律 。
语 法
单 目 表 达 式 :
后 缀 表 达 式
++ 单 目 表 达 式
-- 单 目 表 达 式
单 目 运 算 符 造 型 转 换 表 达 式
sizeof. 单 目 表 达 式
sizeof( 类 型 名 称 )
分 配 表 达 式
撤 消 分 配 表 达 式
单 目 运 算 符 : 以 下 之 一
* & + - ! ~
间 接 运 算 符 (*)
单 目 间 接 运 算 符 (*) “ 间 接 引 用 ” 一 个 指 针 , 即 它 将 一 个 指 针 值 转 换 为 一 个 l 值 。间 接 运 算 符 的 操 作 数 必 须 是 一 个 类 型 的 指 针 。 间 接 表 达 式 的 结 果 是 指 针 类 型 从其 派 生 的 类 型 。 在 此 上 下 文 中 ,* 运 算 符 的 使 用 不 同 于 它 作 为 双 目 运 算 符 即 乘 法的 含 义 。
如 果 操 作 数 指 向 一 个 函 数 , 结 果 是 一 个 函 数 指 示 符 。 如 果 它 指 向 一 个 存 储 位 置 , 则 结 果 是 指 定 该 存 储 位 置 的 一 个 l 值 。
如 果 指 针 值 是 无 效 的 , 则 结 果 是 不 确 定 的 。 以 下 表 包 括 一 些 最 常 见 的 条 件 使 指 针值 无 效 :
-
指 针 是 一 个 空 指 针 。
-
指 针 指 定 一 个 局 部 项 的 地 址 , 在 引 用 时 是 不 可 见 的 。
-
指 针 指 定 一 个 地 址 不 适 当 地 与 该 对 象 指 向 的 类 型 对 齐
-
指 针 指 定 一 个 地 址 不 被 执 行 程 序 使 用 。
取 地 址 运 算 符 (&)
单 目 取 地 址 运 算 符 (&) 取 其 操 作 数 的 地 址 , 取 地 址 运 算 仅 能 用 于 以 下 情 况 :
-
函 数 ( 尽 管 取 函 数 地 址 的 用 法 是 不 需 要 的 )
-
l 值
-
限 定 名 称
在 上 面 所 列 的 前 面 两 种 情 况 下 , 表 达 式 的 结 果 是 从 操 作 数 类 型 派 生 出 的 一 个 指 针类 型 ( 一 个 r 值 ) 。 例 如 , 如 果 操 作 数 为 char 类 型 , 则 表 达 式 的 结 果 是 char 指 针类 型 。 取 地 址 运 算 符 被 用 于 const 或 volatile 对 象 则 等 于 const type * 或
volatile type *, 其 中 type 为 源 对 象 的 类 型 。
第 三 种 情 况 将 取 地 址 运 算 符 应 用 在 一 个 限 定 名 称 上 , 产 生 的 结 果 取 决 于 限 定 名称 是 否 指 定 一 个 静 态 成 员 , 如 果 是 , 结 果 是 指 向 成 员 说 明 中 指 定 类 型 的 一 个 指 针 ; 如 果 成 员 不 是 静 态 的 , 则 结 果 是 由 限 定 类 名 称 指 示 的 类 成 员 名 称 的 一 个 指 针 ( 有关 限 定 类 名 称 的 更 多 信 息 , 参 见 本 章 前 面 的 “ 基 本 表 达 式 ” ) 。 以 下 代 码 段 给 出 : 根 据 成 员 是 否 是 静 态 的 其 结 果 是 如 何 不 同 的 :
class PTM
{
public:
int iValue;
static float fValue;
};
int PTM::*piValue = &PTM::iValue; // 可 行 : 非 静 态 的float PTM::*pfValue = &PTM::fValue; // 错 误 : 静 态 的float *spfValue = &PTM::fValue; // 可 行
在 此 例 中 , 表 达 式 &PTM::fValue 产 生 float* 类 型 而 不 是 float PTM::* 类 型 , 因为 fValue 是 一 个 静 态 成 员 。
- 个 重 载 的 函 数 的 地 址 仅 在 清 楚 地 知 道 引 用 的 是 函 数 的 哪 个 版 本 时 才 能 被 获取 。 关 于 如 何 获 取 一 个 特 定 的 重 载 的 函 数 的 地 址 的 信 息 , 参 见 第 12 章 “ 重 载 ” 中 的 “ 重 载 的 函 数 的 地 址 ”。
对 一 个 引 用 类 型 使 用 取 地 址 运 算 符 与 对 一 个 引 用 限 定 的 对 象 上 使 用 该 运 算 符 给
出 相 同 的 结 果 。 以 下 程 序 说 明 了 这 个 概 念 :
#include <iostream.h>
void main()
{
double d; // 定 义 double 类 型 的 一 个 对 象
double& rd=d; // 定 义 该 对 象 的 一 个 引 用
// 比 较 对 象 的 地 址 和 对 象 引 用 的 地 址
if (&d==&rd)
cout << "&d equals &rd" << "\n";
else
cout << "&d is not equal to &rd" << "\n";
}
程 序 的 输 出 总 是 :&d equals &rd 。
单 目 加 运 算 符 (+)
单 目 加 运 算 符 (+) 的 结 果 是 其 操 作 数 的 值 。 单 目 加 运 算 符 的 操 作 数 必 须 是 一 个 算术 类 型 。
在 整 型 操 作 数 上 执 行 整 型 提 升 。 结 果 类 型 是 操 作 数 被 提 升 的 类 型 。 因 而 , 表 达 式
+ch, 其中 ch 为 char 类 型 , 结 果 为 int 类 型 ; 值 未 修 改 。 关 于 提 升 是 如 何 完 成的 信 息 , 参 见 第 3 章 “ 标 准 转 换 ” 中 的 “ 整 型 提 升 ”。
单 目 负 运 算 符 (-)
单 目 负 运 算 符 (-) 对 其 操 作 数 求 反 。 单 目 负 运 算 符 的 操 作 数 必 须 为 一 个 算 术 类型 。
在 整 型 操 作 数 上 执 行 整 型 提 升 , 结 果 类 型 是 操 作 数 被 提 升 的 类 型 。 有 关 提 升 是如 可 完 成 的 信 息 , 参 见 第 3 章 “ 标 准 转 换 ” 的 “ 整 型 提 升 ”。
Microsoft 特 殊 处 →
无 符 号 量 单 目 求 反 的 执 行 是 从 2 n 中 减 去 操 作 数 的 值 , 其中 n 是 给 定 无 符 号 类 型的 对 象 的 位 数 (Microsoft C++ 运 行 在 对 2 的 补 码 运 算 的 处 理 器 中 , 在 其 它 处 理 器中 求 反 算 法 可 能 有 所 不 同 ) 。
Microsoft 特 殊 处 结 束
逻 辑 非 运 算 符 (!)
如 果 操 作 数 为 非 0 值 , 则 逻 辑 非 运 算 符 (!) 的 结 果 为 0; 仅 当 操 作 数 为 0 时 结 果 方为 1 。 操 作 数 必 须 为 算 术 或 指 针 类 型 , 结 果 是 int 类 型 。
对 一 个 表 达 式 e , 单 目 表 达 式 ! e 等 于 表 达 式 ( e ==0), 除 非 包 含 了 重 载 的 运 算 符 。以 下 例 子 说 明 了 逻 辑 非 运 算 符 (!):
if ( !(x<y) )
如 果 x 大 于 等 于 y, 表 达 式 的 结 果 为 1( 真 ); 如 果 x 小 于 y, 结 果 为 0( 假 ) 。指 针 的 单 目 算 术 运 算 是 非 法 的 。
“ 1 ” 的 求 补 运 算 符 (~)
“ 1 ” 求 补 运 算 符 (~), 有 时 被 称 为“ 按 位 补 ” 运 算 符 , 产 生 其 操 作 数 的 按 位 的“ 1 ”
的 补 , 即 在 操 作 数 中 每 一 位 为 1 的 在 结 果 中 为 0; 相 反 地 , 操 作 数 中 为 0 的 每 位 在结 果 中 为 1 。“ 1 ” 求 补 运 算 符 的 操 作 数 必 须 为 整 型 。
unsigned short y=0xAAAA;
y=~y;
在 此 例 中 , 赋 给 y 的 新 值 是 无 符 号 值 0xAAAA 的 “ 1 ” 求 补 或 0x5555 。
在 整 型 操 作 数 上 执 行 整 型 提 升 , 结 果 类 型 是 操 作 数 被 提 升 的 类 型 。 关 于 升 级 是 如何 完 成 的 的 更 多 信 息 , 参 见 第 3 章 “ 标 准 转 换 ” 中 的 “ 整 型 提 升 ”。
增 1 和 减 1 运 算 符 (++,--)
前 缀 增 1 运 算 符 (++), 亦 被 称 为 “ 前 增 1 ” 运 算 符 , 在 其 操 作 数 上 加 1, 这 个 加 1 后 的 值 是 表 达 式 的 结 果 。 操 作 数 必 须 是 一 个 非 const 类 型 的 l 值 , 结 果 是 与 操 作数 类 型 相 同 的 一 个 l 值 。
前 缀 减 1 运 算 符 (--), 亦 被 称 为 “ 前 减 1 ” 运 算 符 , 与 前 增 1 运 算 符 相 似 , 除 了 操作 数 是 减 1 且 结 果 是 这 个 减 1 后 的 值 。
前 缀 和 后 缀 增 1 及减 1 运 算 符 均 影 响 其 操 作 数 , 关 键 的 不 同 点 是 表 达 式 的 值 何 时发 生 增 1 或 减 1( 有 关 更 多 的 信 息 , 参 见 本 章 前 面 的“ 后 缀 增 1 和 减 1 运 算 符 ” ) 。在 前 缀 形 式 中 , 增 1 或减 1 发 生 在 值 被 用 于 表 达 式 求 值 之 前 , 所 以 表 达 式 的 值 与操 作 数 的 值 不 同 ; 在 后 缀 形 式 中 , 增 1 或减 1 发 生 在 位 被 用 于 表 达 式 求 值 之 后 , 所以 表 达 式 的 值 与 操 作 数 的 值 相 同 。
整 型 或 浮 点 类 型 的 操 作 数 按 整 数 值 1 进 行 增 或 减 , 结 果 的 类 型 与 操 作 数 类 型 相 同 ; 指 针 类 型 操 作 数 按 它 所 指 对 象 的 尺 寸 增 1 或 减 1, 被 增 1 的 指 针 指 向 下 一 个 对 象 , 被 减 1 的 指 针 指 向 前 一 个 对 象 。
这 个 例 子 说 明 了 单 目 减 1 运 算 符 :
if (line [--i]!= ‘ \n ’ )
return;
在 此 例 中 , 变 量 i 在 用 作 line 下 标 之 前 减 1 。
因 为 增 1 和 减 1 运 算 符 有 副 作 用 , 在 宏 中 使 用 带 增 1 或 减 1 运 算 符 的 表 达 式 可 产生 意 料 不 到 的 结 果 ( 有 关 宏 的 更 多 信 息 , 参 见 本 部 分 后 面 “ 预 处 理 器 引 用 ” 中 的“ 宏 ” ) 。 考 虑 以 下 例 子 :
#define max(a,b) ((a)<(b)) ? (b) : (a)
...
int i,j,k;
k=max(++i,j); 宏 扩 展 为 :
k=((++i)<(j)) ? (j) : (++i); 如 果 i 大 于 等 于 j, 它 将 增 1 两 次 。
注 意 : 在 很 多 情 况 下 ,C++ 的 内 联 函 数 (inline functions) 比 宏 更 好 , 因 为 它 们 消除 了 这 里 描 述 的 副 作 用 , 而 允 许 语 言 执 行 更 完 整 的 类 型 检 测 。
sizeof 运 算 符
sizeof 运 算 符 产 生 关 于 char 类 型 尺 寸 的 操 作 数 尺 寸 ,sizeof 运 算 符 的 结 果 是
size_t 类 型 , 它 是 定 义 在 包 括 文 件 STDDEF.H 中 的 一 个 整 型 。 sizeof 操 作 数 可以 是 以 下 之 一 :
-
一 个 类 型 名 。 要 对 类 型 名 用 sizeof, 则 名 称 必 须 用 圆 括 号 括 起 来 。
-
一 个 表 达 式 。 当 用 于 表 达 式 时 ,sizeof 可 以 使 用 或 不 使 用 括 号 指 定 , 该 表 达 式 不 求 值 。
当 sizeof 运 算 符 用 于 char 类 型 的 对 象 时 , 它 结 果 为 1; 当 sizeof 运 算 符 用 于 一个 数 组 时 , 它 结 果 为 那 个 数 组 中 的 字 节 总 数 。 例 如 :
#include <iostream.h>
void main()
{
char szHello[]="Hello, world!";
cout << "The size of the type of " << szHello <<" is:"
<< sizeof(char) << "\n";
cout <<"The length of " << szHello << "is:"
<< sizeof szHello << "\n";
}
程 序 输 出 为 :
The size of the type of Hello, world! is:1 The length of Hello, world! is:14
当 sizeof 运 算 符 用 于 class 、 struct 或 union 类 型 时 , 结 果 是 该 class 、 struct
或 union 类 型 的 一 个 对 象 的 字 节 数 加 上 字 边 界 上 任 何 添 加 的 对 齐 成 员 的 填 充 字节 。 (/Zp[ 紧 凑 结 构 成 员 ] 编 译 器 选 项 和 pack 编 译 指 示 影 响 成 员 的 对 齐 边 界 ) 。sizeof 运 算 符 从 不 产 生 0, 即 使 对 一 个 空 类 亦 如 此 。
sizeof 运 算 符 不 能 用 于 以 下 操 作 数 :
-
函 数 ( 但 是 sizeof 可 应 用 于 函 数 指 针 )
-
位 域
-
未 定 义 的 类
-
void 类 型
-
不 完 整 类 型
-
括 号 括 起 的 不 完 整 类 型 的 名 称
当 sizeof 运 算 符 用 于 一 个 引 用 时 , 结 果 就 如 同 sizeof 用 于 对 象 自 身 一 样 。sizeof 运 算 符 经 常 被 用 于 计 算 一 个 数 组 的 元 素 个 数 , 使 用 如 下 形 式 的 表 达 式 :
sizeof array/sizeof array[0]
new 运 算 符
new 运 算 符 试 图 动 态 地 分 配 ( 在 运 行 时 ) 类 型 名 称 的 一 个 或 多 个 对 象 。 new 运 算 符不 能 用 于 分 配 一 个 函 数 , 但 可 以 用 于 分 配 一 个 函 数 指 针 。
语 法
分 配 表 达 式 :
:: opt new 位 置 opt 新 类 型 名 称 新 初 始 化 器 opt
:: op t new 位 置 opt ( 类 型 名 称 ) 新 初 始 化 器 opt
位 置 :
( 表 达 式 表 ) 新 类 型 名 称 :
类 型 指 示 符 表 新 说 明 符 opt
new 运 算 符 被 用 于 分 配 对 象 及 对 象 数 组 ,new 运 算 符 从 被 称 为 “ 自 由 存 储 ” 的 程序 存 储 器 区 域 中 进 行 分 配 。 在 C 中 , 自 由 存 储 区 经 常 被 指 示 为 “ 堆 ”。
当 new 被 用 于 分 配 一 个 单 个 对 象 时 , 它 产 生 一 个 指 向 那 个 对 象 的 指 针 ; 结 果 类 型为 新 类 型 名 称 * 或 类 型 名 称 *; 当 new 被 用 于 分 配 一 个 单 维 对 象 数 组 时 , 它 产 生 一个 指 向 数 组 第 一 个 元 素 的 指 针 , 结 果 类 型 为 新 类 型 名 称 * 或 类 型 名 称 *; 当 new 被用 于 分 配 一 个 多 维 对 象 数 组 时 , 它 产 生 一 个 指 向 数 组 第 一 个 元 素 的 指 针 , 而 且 结果 类 型 保 留 除 最 左 边 数 组 维 数 外 的 所 有 的 尺 寸 , 例如 :
new float[10][25][10]
产 生 类 型 float(*)[25][10] 。因 此 , 以 下 代 码 不 能 工 作 , 因 为 它 试 图 用 类 型 float 的 指 针 的 维 数 [25][10] 赋 给 一 个 float 数 组 的 指 针 :
float *fp;
fp=new float [10][25][10]; 正 确 的 表 达 式 应 是 :
float (*cp)[25][10] cp=new float[10][25][10];
cp 的 定 义 用 维 数 [25][10] 分 配 一 个 float 类 型 数 组 的 指 针 , 它 不 分 配 指 针 数 组 。除 最 左 边 数 组 维 数 外 的 所 有 维 数 必 须 为 等 于 正 数 的 常 量 表 达 式 ; 最 左 边 数 组 维 数可 以 为 等 于 正 数 的 任 意 表 达 式 , 当 用 new 运 算 符 分 配 一 个 数 组 时 , 第 一 个 维 数 可以 为 0,new 运 算 符 返 回 一 个 唯 一 的 指 针 。
类 型 指 示 符 表 不 能 包 含 const 、 volatile 、 类 说 明 或 枚 举 说 明 。 因 此 , 以 下 表 达式 是 非 法 的 :
volatile char *vch = new volatile char[20]; new 运 算 符 不 分 配 引 用 类 型 , 因 为 它 们 不 是 对 象 。
如 果 没 有 足 够 的 存 储 器 满 足 分 配 需 求 , 缺 省 地 operator new 返 回 NULL 。 你 可 以通 过 编 写 定 制 的 异 常 处 理 例 程 并 以 你 的 函 数 名 称 作 为 参 量 调 用
_set_new_handler 运 行 库 函 数 来 改 变 这 种 缺 省 行 为 。 有 关 这 种 恢 复 模 式 详 细 内容 , 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 运 算 符 new 功 能 ”。
用 new 分 配 的 对 象 的 生 存 期
用 new 运 算 符 分 配 的 对 象 当 它 们 从 定 义 的 所 在 范 围 退 出 时 不 被 销 毁 。 因 为 new 运 算 符 返 回 它 分 配 的 对 象 的 指 针 , 程 序 必 须 用 合 适 的 范 围 定 义 一 个 指 针 以 访 问 那些 对 象 。 例 如 :
void main()
{
// 使 用 new 运 算 符 分 配 一 个 20 个 字 符 的 数 组
char *AnArray = new char[20];
for (int i=0;i<20;++i)
{
// 在 循 环 的 第 一 次 迭 代 时 , 分 配 另 一 个 20 个 字 符 的 数 组
if (i==0)
{
char *AnotherArray = new char[20];
}
...
}
delete AnotherArray; // 错 误 : 指 针 超 出 范 围
delete AnArray; // 可 以 : 指 针 还 在 范 围 内
}
在 例 子 中 一 旦 指 针 AnotherArray 超 出 了 范 围 , 对 象 再 也 不 能 被 删 除 了 。
初 始 化 用 new 分 配 的 对 象
选 项 new 初 始 化 器 域 包 含 在 new 运 算 符 的 语 法 内 , 这 允 许 使 用 用 户 定 义 的 构 造 函数 去 初 始 化 new 对 象 。 关 于 初 始 化 是 如 何 完 成 的 更 多 信 息 , 参 见 第 7 章 “ 说 明符 ” 中 “ 初 始 化 器 ”。
以 下 例 子 说 明 如 何 使 用 带 new 运 算 符 的 初 始 化 表 达 式 : #include <iostream.h>
class Acct
{
public:
// 定 义 缺 省 构 造 函 数 和 构 造 函 数 接 受 初 始 的 balance
Acct() { balance=0.0; }
Acct( double init_balance ) { balance = init_balance; } private:
double balance;
};
void main()
{
Acct *CheckingAcct = new Acct;
Acct *SavingAcct = new Acct(34.98);
double *HowMuch = new double(43.0);
...
}
在 此 例 中 , 对 象 CheckingAcct 使 用 new 运 算 符 分 配 , 但 没 有 指 定 缺 省 的 初 始 化 , 因 此 , 调 用 类 的 缺 省 构 造 函 数 Acct() ; 然 后 以 同 样 的 方 式 分 配 对 象 SavingAcct, 但 被 显 式 地 初 始 化 为 34.98 。 由 于 34.98 是 double 类 型 , 调 用 以 该 类 型 为 参 量的 构 造 函 数 进 行 初 始 化 处 理 。 最 后 , 非 类 类 型 HowMuch 初 始 化 为 43.0 。
如 果 一 个 对 象 是 一 个 类 类 型 的 , 而 且 那 个 类 有 构 造 函 数 ( 如 前 面 例 子 那 样 ), 仅 在以 下 条 件 之 一 被 满 足 时 对 象 才 能 被 new 运 算 符 初 始 化 :
-
在 初 始 化 器 中 提 供 的 参 量 与 构 造 函 数 中 的 那 些 参 量 相 符 。
-
类 有 一 个 缺 省 的 构 造 函 数 ( 一 个 可 以 不 带 参 量 调 用 的 构 造 函 数 ) 。
访 问 控 制 和 模 糊 控 制 在 operator new 上 和 在 按 照 第 9 章 “ 派 生 类 中 的 模 糊 性 ” 及 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 使 用 特 殊 成 员 函 数 初 始 化 ” 中 提 出 的 规 则 集的 构 造 函 数 上 执 行 。
当 使 用 new 运 算 符 分 配 数 组 时 没 有 显 式 地 为 每 个 元 素 执 行 初 始 化 ; 如 果 存 在 的 话 , 只 有 缺 省 构 造 函 数 被 调 用 。 有 关 更 多 的 信 息 , 参 见 第 7 章 “ 说 明 符 ” 中 的 “ 缺 省参 量 ”。
如 果 存 储 器 分 配 失 败 (operator new 返 回 一 个 0 值 ), 则 没 有 初 始 化 被 执 行 , 这 阻止 了 对 不 存 在 的 数 据 进 行 初 始 化 。
由 于 用 函 数 调 用 , 初 始 化 表 达 式 被 求 值 的 顺 序 未 定 义 , 而 且 , 你 不 能 依 靠 这 些 表 达式 在 存 储 器 分 配 执 行 之 前 完 全 被 求 值 。 如 果 存 储 器 分 配 失 败 ,new 运 算 符 返 回 0, 在 初 始 化 器 中 的 某 些 表 达 式 可 能 不 能 被 完 全 求 值 。
new 是 如 何 工 作 的
分 配 表 达 式 即 包 含 new 运 算 符 的 表 达 式 做 三 件 事 :
-
为 对 象 或 待 分 配 的 对 象 定 位 并 保 留 存 储 器 , 当 这 步 完 成 时 , 分 配 正 确的 存 储 单 元 数 , 但 还 不 是 一 个 对 象 。
-
初 始 化 对 象 , 一 旦 初 始 化 完 成 , 则 有 足 够 的 信 息 为 该 对 象 分 配 存 储 。
-
返 回 一 个 指 向 由 new - type- name 或 type - name 派 生 的 指 针 类 型 对 象 的一 个 指 针 , 程 序 使 用 该 指 针 访 问 新 分 配 的 对 象 。
new 运 算 符 调 用 函 数 operator new 。 对 于 任 何 类 型 的 数 组 及 非 class 、 struct 或 union 类 型 的 对 象 , 全 局 函 数 ::operator new 被 调 用 去 分 配 存 储 器 , 类 类 型 对象 可 以 在 每 个 类 基 础 上 定 义 自 己 的 operator new 静 态 成 员 。
当 编 译 器 遇 到 new 运 算 符 去 分 配 一 个 type 类 型 对 象 时 , 它 调 用 typ e::operator new (sizeof (type )); 或 者 若 是 没 有 用 户 定 义 的 operator new 被 定 义 , 则 调用 ::operator new (sizeof ( type )) 。 因 此 ,new 运 算 符 可 以 为 对 象 分 配 正 确 的
存 储 器 数 目 。
注 意 : operator new 的 参 量 是 size_t 类 型 , 这 个 类 型 定 义 在 DIRECT.H 、MALLOC.H 、 MEMORY.H 、 SEARCH.H 、 STDDEF.H 、 STDIO.H 、 STDLIB.H 、 STRING.H
和 TIME.H 中 。
语 法 中 的 一 个 选 项 允 许 位 置 的 规 格 ( 参 见 new 运 算 符 的 语 法 ) 。 该 位 置 参 量 仅 能在 operator new 用 户 定 义 的 实 现 中 被 使 用 ; 它 允 许 将 额 外 的 信 息 传 递 给 operator new, 带 位 置 域 的 表 达 式 如 :
T *TObject = new(0x0040) T; 被 翻 译 为 :
T *TObject = T::operator new(sizeof(T),0x0040);
位 置 域 最 初 的 目 的 是 允 许 依 赖 于 硬 件 的 对 象 在 用 户 指 定 的 地 址 处 分 配 。
注 意 : 尽 管 前 面 的 例 子 给 出 的 是 在 位 置 域 仅 有 一 个 参 量 , 但 对 这 种 方 式 有 多 少 个额 外 的 参 量 可 被 传 递 给 operator new 并 没 有 限 制 。
即 使 当 operator new 已 为 一 个 类 类 型 定 义 , 全 局 运 算 符 还 可 以 通 过 下 面 例 子 中的 形 式 来 使 用 :
T *TObject = ::new TObject:
范 围 分 辩 运 算 符 (::) 强 制 使 用 全 局 new 运 算 符 。
delete 运 算 符
delete 运 算 符 撤 消 由 new 运 算 符 创 建 的 对 象 分 配 。 delete 运 算 符 有 一 个 void 类 型 的 结 果 , 因 此 不 返 回 值 ,delete 的 操 作 数 必 须 是 由 new 运 算 符 返 回 的 一 个 指针 。
对 不 是 用 new 分 配 的 对 象 指 针 使 用 delete 会 产 生 意 料 不 到 的 结 果 , 但 是 你 可 以对 具 有 0 值 的 指 针 使 用 delete 。 因 为 在 失 败 的 情 况 下 new 总 是 返 回 0, 所 以 这 种措 施 意 味 着 删 除 一 个 失 败 的 new 操 作 的 结 果 是 无 害 的 。
语 法
撤 消 分 配 表 达 式 :
:: opt delete 造 型 转 换 表 达 式
:: opt delete [] 造 型 转 换 表 达 式
对 一 个 对 象 使 用 delete 运 算 符 将 撤 消 其 存 储 器 分 配 , 一 个 程 序 在 对 象 被 删 除 后间 接 引 用 一 个 指 针 则 会 产 生 意 料 不 到 的 结 果 或 崩 溃 。
如 果 delete 运 算 符 的 操 作 数 是 一 个 可 修 改 的 l 值 , 则 在 对 象 被 删 除 后 其 值 是 未定 义 的 。
常 量 对 象 指 针 不 能 用 delete 运 算 符 撤 消 分 配 。
delete 是 如 何 工 作 的
delete 运 算 符 调 用 函 数 运 算 符 delete, 对 类 类 型 (class( 类 ) 、 struct( 结 构 ) 和union( 联 合 )) 对 象 ,delete 运 算 符 在 撤 消 存 储 器 分 配 ( 如 果 指 针 非 空 ) 之 前 调 用对 象 的 析 构 函 数 。 对 非 类 类 型 对 象 , 全 局 delete 运 算 符 被 调 用 。 对 类 类 型 对象 ,delete 运 算 符 可 被 定 义 在 一 个 每 个 类 基 础 上 。 如 果 对 给 定 类 没 有 这 样 的 定义 , 则 全 局 运 算 符 被 调 用 。
使 用 delete
对 delete 运 算 符 有 两 个 语 法 变 型 : 一 个 针 对 单 个 对 象 的 , 另 一 个 是 对 对 象 数 组
的 。 以 下 代 码 段 给 出 这 些 的 不 同 :
void main()
{
// 使 用 new 运 算 符 在 自 由 存 储 区 分 配 一 个 用 户 定 义 对 象 UDObject 和 double 类 型 对 象
UDType *UDObject = new UDType;
double *dObject = new double;
...
// 删 除 两 个 对 象
delete UDObject;
delete dObject;
...
// 使 用 new 运 算 符 在 自 由 存 储 区 分 配 一 个 用 户 定 义 对 象 数 组
UDType (*UDArr)[7] = new UDType[5][7];
...
// 使 用 数 组 语 法 删 除 对 象 数 组
delete [] UDArr;
}
这 两 种 情 况 产 生 未 定 义 的 结 果 : 对 一 个 对 象 使 用 delete 的 数 组 形 式 (delete[]) 和 对 一 个 数 组 使 用 delete 的 非 数 组 形 式 。
带 双 目 运 算 符 的 表 达 式
双 目 运 算 符 作 用 于 一 个 表 达 式 中 的 两 个 操 作 数 。 双 目 运 算 符 有 : 乘 法 运 算 符
乘法 (*)
除法 (/)
取模 (%) 加 法 运 算 符
加法 (+)
减法 (-) 位 移 运 算 符
右移 (>>)
左移 (<<)
关 系 及 相 等 性 运 算 符
小于 (<)
大于 (>)
小 于 等 于 (<=)
大 于 等 于 (>=)
等于 (==)
不 等 于 (!=) 按 位 运 算 符
按 位 与 (&)
按 位 异 或 (^)
按 位 或 (|)
逻 辑 与 (&&)
逻 辑 或 (||)
乘 法 运 算 符
乘 法 运 算 符 有 :
-
乘 法 (*)
-
除 法 (/)
-
取 模 或 “ 除 法 取 余 ” (%)
这 些 双 目 运 算 符 具 有 从 左 到 右 的 结 合 律 。
语 法
乘 法 表 达 式 :
pm 表 达 式
乘 法 表 达 式 * pm 表 达 式
乘 法 表 达 式 / pm 表 达 式
乘 法 表 达 式 % pm 表 达 式
乘 法 表 达 式 采 用 算 术 类 型 操 作 数 , 取 模 运 算 符 (%) 有 一 个 较 为 严 格 的 要 求 , 即 其 操作 数 必 须 为 整 型 ( 要 得 到 浮 点 除 法 的 余 数 , 使 用 运 行 函 数 fmod) 。第 3 章 “ 标 准转 换 ” 中 的 “ 算 术 转 换 ” 中 包 含 的 转 换 适 用 于 操 作 数 , 且 结 果 是 转 换 后 的 类 型 。乘 法 运 算 符 产 生 的 是 第 一 个 操 作 数 被 第 二 个 相 乘 的 结 果 。
除 法 运 算 符 产 生 的 是 第 一 个 操 作 数 被 第 二 个 相 除 的 结 果 。
取 模 运 算 符 产 生 由 表 达 式 e 1-( e 1/ e 2)* e 2 给 出 的 余 数 , 其 中 e 1 是 第 一 个 操 作数 , e 2 是 第 二 个 操 作 数 , 其 中 两 个 操 作 数 均 为 整 型 。
在 除 法 或 取 模 数 表 达 式 中 被 0 除 都 是 未 定 义 的 , 会 产 生 一 个 运 行 错 误 。 因 此 , 以下 表 达 式 产 生 未 定 义 的 错 误 的 结 果 :
i % 0
f / 0.0
如 果 乘 法 、 除 法 或 取 模 表 达 式 的 两 个 操 作 数 具 有 相 同 的 符 号 , 则 结 果 为 正 ; 否 则 , 结 果 为 负 , 取 模 操 作 的 符 号 结 果 是 定 义 的 实 现 。
Microsoft 特 殊 处 →
在 Microsoft C++ 中 , 取 模 表 达 式 的 结 果 总 是 与 第 一 个 操 作 数 的 符 号 相 同 。
Microsoft 特 殊 处 结 束
如 果 两 个 整 数 计 算 的 除 法 是 不 精 确 的 , 而 且 仅 一 个 操 作 数 为 负 , 结 果 是 比 除 法 产生 的 准 确 值 小 的 最 大 整 数 ( 在 量 上 , 不 考 虑 符 号 ) 。 例 如 ,-11/3 计 算 的 结 果 为 - 3.666666666, 则 整 型 除 法 的 结 果 为 -3 。
乘 法 运 算 符 之 间 的 关 系 由 以 下 标 识 给 出 : ( e1 / e2 ) *e2 + e1%e2 == e1
加 法 运 算 符
加 法 运 算 符 有 :
-
加 法 (+)
-
减 法 (-)
这 些 双 目 运 算 符 具 有 从 左 到 右 的 结 合 律 。
语 法
加 法 表 达 式 :
乘 法 表 达 式
加 法 表 达 式 + 乘 法 表 达 式
加 法 表 达 式 - 乘 法 表 达 式
加 法 运 算 符 采 用 算 术 或 指 针 类 型 操 作 数 。 加 法 运 算 符 (+) 的 结 果 是 操 作 数 求 和 ; 减 法 运 算 符 (-) 的 结 果 是 操 作 数 之 间 的 差 。 如 果 一 个 或 两 个 操 作 数 都 是 指 针 , 它们 必 须 是 对 象 的 指 针 , 而 不 是 函 数 的 指 针 。
加 法 运 算 符 的 操 作 数 为 算 术 、 整 型 或 标 量 型 , 这 些 定 义 在 表 4.2 中 。
表 4.2 加 法 运 算 符 使 用 的 类 型
类型 含义
算术型 整型和浮点类型统称为 " 算术 " 类型
整型 char 和所有尺寸的 int(long , short) 类型和枚举都是 “整数” 类型
标量型 标量型操作数是算术或指针类型的操作数
这 些 运 算 符 的 合 法 组 合 为 : 算 术 型 + 算 术 型
标 量 型 + 整 型整 型 + 标 量 型算 术 型 - 算 术 型标 量 型 - 标 量 型
注 意 : 加 法 和 减 法 不 是 等 价 操 作 。
如 果 两 个 操 作 数 都 是 算 术 类 型 , 则 在 第 3 章 “ 标 准 转 换 ” 中 “ 算 术 转 换 ” 包 括 的转 换 应 用 于 操 作 数 , 而 且 结 果 是 被 转 换 了 的 类 型 。
指 针 类 型 的 加 法
如 果 加 法 操 作 中 一 个 操 作 数 为 指 向 一 个 对 象 数 组 的 指 针 , 则 另 一 个 必 须 为 整 型 , 其 结 果 是 与 源 指 针 同 类 型 的 指 针 , 且 指 向 另 一 个 数 组 元 素 。 以 下 代 码 段 说 明 这 个概 念 :
short IntArray[10]; //short 类 型 对 象 占 两 个 字 节short *pIntArray=Int Array;
for (int i=0;i<10;++i)
{
*pIntArray=i;
cout << *pIntArray << "\n";
pIntArray = pIntArray + 1;
}
尽 管 整 型 值 1 被 加 到 pIntArray 上 , 但 它 并 不 意 味 着 “ 地 址 加 1 ” ; 而 是 指 “ 调整 指 针 指 向 数 组 中 的 下 一 个 对 象 ” , 而 且 正 好 是 走 了 两 个 字 节 ( 或 sizeof(int)) 。注 意 : pIntArray=pIntArray+1 形 式 的 代 码 在 C++ 程 序 中 很 少 看 到 。 为 了 执 行 增1, 这 样 形 式 更 好 :pIntArray++ 或 pIntArray+=1 。
指 针 类 型 的 减 法
如 果 两 个 操 作 数 都 是 指 针 , 减 法 的 结 果 是 两 个 操 作 数 之 间 的 差 ( 在 数 组 元 素 中 ) 。减 法 表 达 式 产 生 一 个 类 型 ptrdiff_t( 定 义 在 标 准 包 括 文 件 STDDEF.H 中 ) 的 有 符号 的 整 型 结 果 。
操 作 数 之 一 可 以 是 整 型 , 它 只 能 是 第 二 个 操 作 数 。 减 法 的 结 果 与 源 指 针 同 类 型 , 减 法 的 值 是 指 向 第 ( n-i ) 个 数 组 元 素 的 指 针 , 其中 n 是 由 源 指 针 指 向 的 元 素 ,i 是第 二 个 操 作 数 的 整 型 值 。
位 移 运 算 符
按 位 移 位 运 算 符 有 :
-
右 移 (>>)
-
左 移 (<<)
这 些 双 目 运 算 符 具 有 从 左 到 右 的 结 合 律 。
语 法
移 位 表 达 式 :
加 法 表 达 式
移 位 表 达 式 << 加 法 表 达 式
移 位 表 达 式 >> 加 法 表 达 式
移 位 运 算 符 的 两 个 操 作 数 都 必 须 是 整 型 , 整 型 提 升 的 执 行 按 照 第 3 章 “ 标 准 转换 ” 中 的 “ 整 型 提 升 ” 中 指 定 的 规 则 进 行 。 结 果 的 类 型 与 左 操 作 数 类 型 相 同 。右 移 表 达 式 el>>e2 的 值 为 e1/2 e2 ; 左 移 表 达 式 e1<<e2 的 值 为 e1*2 e2 。
如 果 移 位 表 达 式 的 右 操 作 数 为 负 数 或 如 果 右 操 作 数 大 于 等 于 左 操 作 数 ( 提 升 了 的
的 位 数 , 则 结 果 是 未 定 义 的 。
左 移 运 算 符 使 得 第 一 个 操 作 数 的 位 模 式 向 左 移 动 由 第 二 个 操 作 数 指 定 的 位 数 , 由移 位 操 作 进 行 的 空 位 填 充 是 0 填 充 , 这 是 逻 辑 移 位 , 与 循 环 移 位 相 反 。
右 移 运 算 符 使 得 第 一 个 操 作 数 的 位 模 式 向 右 移 动 由 第 二 个 操 作 数 指 定 的 位 数 。对 无 符 号 量 移 位 操 作 的 空 位 填 充 是 0 填 充 , 对 有 符 号 量 , 符 号 位 被 传 入 空 位 。 如果 左 操 作 数 是 一 个 无 符 号 量 , 则 移 位 是 一 个 逻 辑 移 位 , 否 则 是 算 术 移 位 。Microsoft 特 殊 处 →
有 符 号 的 负 数 的 右 移 结 果 是 依 赖 于 定 义 的 实 现 , 尽 管 Microsoft C++ 传 播 最 重 要的 位 以 填 充 空 位 , 但 不 保 证 其 它 的 实 现 也 这 样 做 。
Microsoft 特 殊 处 结 束
关 系 与 相 等 运 算 符
关 系 与 相 等 运 算 符 指 定 其 操 作 数 的 相 等 、 不 等 或 关 系 值 , 关 系 运 算 符 在 表 4.3 中所 示 。
表 4.3 关 系 与 相 等 运 算 符
运算符 含义
== 等于
!= 不等于
< 小于
> 大于
<= 小于等于
>= 大于等于
关 系 运 算 符
双 目 关 系 运 算 符 指 定 以 下 关 系 :
-
小 于
-
大 于
-
小 于 等 于
-
大 于 等 于
语 法
关 系 表 达 式 :
移 位 表 达 式
关 系 表 达 式 < 移 位 表 达 式
关 系 表 达 式 > 移 位 表 达 式
关 系 表 达 式 <= 移 位 表 达 式
关 系 表 达 式 <= 移 位 表 达 式
关 系 运 算 符 具 有 从 左 到 右 的 结 合 律 。 关 系 运 算 符 的 两 个 操 作 数 必 须 为 算 术 或 指针 类 型 , 它 们 产 生 int 类 型 值 。如 果 表 达 式 中 关 系 为 假 则 返 回 的 值 为 0; 否 则 为 1 。考 虑 以 下 代 码 , 其 中 说 明 了 几 种 关 系 表 达 式 :
#include <iostream.h>
void main()
{
cout << "The true expression 3 > 2 yields: "
<< (3 > 2) << "\n";
cout << "The false expression 20 < 10 yields: "
<< (20 < 10) << "\n";
cout << "The expression 10 < 20 < 5 yields: "
<< (10 < 20 < 5) << "\n";
}
此 程 序 的 输 出 为 :
The true expression 3 > 2 yields:1 The false expression 20 < 10 yields:0 The expression 10 < 20 < 5 yields:1
在 前 面 例 子 中 的 表 达 式 必 须 包 含 在 括 号 内 , 因 为 插 入 运 算 符 (<<) 比 关 系 运 算 符 优先 级 高 。 因 此 , 不 带 括 号 的 第 一 个 表 达 式 将 等 于 :
(cout << "The true expression 3 > 2 yields:" << 3) < (2 << "\n");
注 意 第 三 个 表 达 式 等 于 1, 因 为 关 系 运 算 符 从 左 到 右 的 结 合 律 , 表 达 式 10<20<5 的 显 式 分 组 为 :
(10 < 20) < 5
因 此 , 测 试 执 行 的 是 :
1<5
结 果 为 1( 或真 ) 。
第 3 章 “ 标 准 转 换 ” 中 的 “ 算 术 转 换 ” 包 含 的 常 用 的 算 术 转 换 应 用 于 算 术 类 型的 操 作 数 上 。
使 用 关 系 运 算 符 比 较 指 针
当 同 类 型 对 象 的 两 个 指 针 进 行 比 较 时 , 结 果 由 指 向 程 序 地 址 空 间 的 对 象 位 置 指定 。 指 针 还 可 以 和 等 于 0 或 void * 类 型 指 针 的 常 量 表 达 式 进 行 比 较 。 如 果 指 针比 较 相 对 于 void* 类 型 指 针 , 则 另 一 指 针 被 隐 式 地 转 换 为 void* 类 型 。 然 后 进 行比 较 。
不 同 类 型 的 两 个 指 针 不 能 进 行 比 较 , 除 非 :
-
一 个 类 型 是 从 另 一 类 型 派 生 而 来 的 类 类 型 。
-
至 少 一 个 指 针 被 显 式 地 转 换 ( 强 制 ) 为 void* 类 型 ( 另 一 个 指 针 隐 式 地转 换 为 void * 类 型 ) 。
指 向 相 同 类 型 的 相 同 对 象 的 两 个 指 针 保 证 比 较 结 果 为 相 等 。 如 果 一 个 对 象 的 两个 非 静 态 成 员 指 针 进 行 比 较 , 使 用 以 下 规 则 :
- 如 果 类 类 型 不 是 联 合 , 而 且 如 果 两 个 成 员 不 是 由 一 个 访 问 指 示 符 如公 共 的 、 保 护 的 和 私 有 的 分 开 , 在 后 面 说 明 的 成 员 指 针 将 比 前 面 说
明 的 成 员 的 指 针 大 些 ( 有 关 访 问 指 示 符 见 第 10 章 “ 成 员 访 问 控 制 ” 中 的 “ 访 问 指 定 符 ” 的 语 法 部 分 ) 。
-
如 果 两 个 成 员 被 访 问 指 示 符 分 开 , 则 结 果 是 不 确 定 的 。
-
如 果 类 类 型 是 一 个 联 合 , 在 这 个 联 合 中 的 不 同 数 据 的 指 针 相 比 较 是相 等 的 。
如 果 两 个 指 针 指 向 同 一 数 组 中 的 元 素 或 指 向 的 某 个 元 素 超 出 了 数 组 的 尾 界 , 则 带有 较 高 下 标 的 对 象 的 指 针 相 比 高 些 。 指 针 的 比 较 仅 当 指 针 指 向 同 一 数 组 对 象 或指 向 超 过 数 组 边 界 的 某 个 位 置 时 才 确 保 是 有 效 的 , 。
相 等 运 算 符
双 目 相 等 运 算 符 比 较 其 操 作 数 为 严 格 相 等 或 不 等 。
语 法
相 等 表 达 式 :
关 系 表 达 式
相 等 表 达 式 == 关 系 表 达 式
相 等 表 达 式 != 关 系 表 达 式
相 等 运 算 符 : 等 于 (==) 和 不 等 于 (!=) 比 关 系 运 算 符 优 先 级 低 , 但 它 们 的 行 为 是 相似 的 。
如 果 两 个 操 作 数 有 相 同 的 值 则 相 等 运 算 符 ( == ) 返 回 真 值 ; 否 则 返 回 假 值 。 不 等 运算 符 (!=) 当 两 个 操 作 数 具 有 不 同 的 值 时 返 回 真 值 , 否 则 返 回 假 值 。
相 等 运 算 符 可 以 比 较 两 个 同 类 型 成 员 指 针 , 在 这 种 比 较 中 , 执 行 成 员 指 针 的 转 换如 第 3 章 “ 标 准 转 换 ” 中 的 “ 成 员 指 针 转 换 ” 讨 论 的 。 成 员 指 针 还 可 以 与 等 于
0 的 常 量 表 达 式 进 行 比 较 。
按 位 运 算 符
按 位 运 算 符 有 :
-
按 位 与 (&)
-
按 位 异 或 (^)
-
按 位 或 (|)
这 些 运 算 符 返 回 其 操 作 数 的 按 位 组 合 。
按 位 与 运 算 符
按 位 与 运 算 符 (&) 返 回 两 个 操 作 数 的 按 位 与 结 果 。 左 右 操 作 数 中 所 有 均 为 1 的 位在 结 果 中 为 1, 而 任 一 个 操 作 数 中 为 0 的 位 在 结 果 中 为 0 。
语 法
与 表 达 式 :
关 系 表 达 式
与 表 达 式 & 相 等 表 达 式
按 位 与 运 算 符 的 两 个 操 作 数 都 必 须 为 整 型 , 包 含 在 第 3 章 “ 标 准 转 换 ” 中 的 “ 算术 转 换 ” 中 的 常 用 算 术 转 换 被 用 于 操 作 数 上 。
按 位 异 或 运 算 符
按 位 异 或 运 算 符 (^) 返 回 两 个 操 作 数 的 按 位 异 或 结 果 。 在 左 右 之 一 操 作 数 中 为 1 的 所 有 位 但 不 都 是 1 的位 , 出 现 在 结 果 中 为 1; 两 个 操 作 数 中 相 同 值 的 位 ( 为 1 或
- 则 在 结 果 中 为 0 。
语 法
异 或 表 达 式 :
与 表 达 式
异 或 表 达 式 ^ 与 表 达 式
按 位 异 或 运 算 符 的 两 个 操 作 数 必 须 为 整 型 , 包 含 在 第 3 章 “ 标 准 转 换 ” 中 “ 算 术转 换 ” 的 常 用 算 术 转 换 被 用 于 操 作 数 上 。
按 位 或 运 算 符
按 位 或 运 算 符 返 回 两 个 操 作 数 按 位 或 的 结 果 , 在 左 或 右 操 作 数 中 为 1 的 所 有 位 在结 果 中 均 为 1, 在 两 个 操 作 数 中 均 为 0 的 位 在 结 果 中 为 0 。
语 法
或 表 达 式 :
异 或 表 达 式
或 表 达 式 | 异 或 表 达 式
按 位 或 运 算 符 的 两 个 操 作 数 都 必 须 为 整 型 , 包 含 在 第 三 章 “ 标 准 转 换 ” 中 “ 算 术转 换 ” 的 常 用 算 术 转 换 被 用 于 操 作 数 上 。
逻 辑 运 算 符
逻 辑 运 算 符 : 逻 辑 与 (&&) 逻 辑 或 (||) 被 用 于 组 合 使 用 关 系 或 相 等 表 达 式 形 成 的 多个 条 件 。
逻 辑 与 运 算 符
逻 辑 与 运 算 符 在 两 个 操 作 数 均 为 非 0 时 返 回 整 型 值 1; 否 则 返 回 0 。 逻 辑 与 具 有从 左 到 右 的 结 合 律 。
语 法
逻 辑 与 表 达 式 :
或 表 达 式
逻 辑 与 表 达 式 && 或 表 达 式
逻 辑 与 运 算 符 的 操 作 数 不 要 求 为 同 一 类 型 , 但 它 们 必 须 为 整 型 或 指 针 类 型 , 操 作数 通 常 为 关 系 或 相 等 表 达 式 。
在 继 续 计 算 逻 辑 与 表 达 式 的 值 之 前 , 第 一 个 操 作 数 被 完 全 求 值 , 且 所 有 的 副 作 用也 被 完 成 。
第 二 个 操 作 数 仅 在 第 一 个 操 作 数 被 求 值 为 真 ( 非 0) 时 才 被 计 算 求 值 , 这 种 求 值 去掉 了 第 二 个 操 作 数 在 逻 辑 与 表 达 式 为 假 时 不 必 要 的 求 值 , 你 可 以 用 这 种 短 路 计 算防 止 空 指 针 的 间 接 引 用 , 正 如 以 下 例 中 所 示 :
char *pch=0
...
(pch) && (*pch= ′ a ′ );
如 果 pch 为 空 (0), 表 达 式 的 右 端 永 远 不 被 求 值 , 因 此 , 通 过 空 指 针 的 赋 值 是 不 可能 的 。
逻 辑 或 运 算 符
逻 辑 或 运 算 符 在 任 意 一 个 操 作 数 为 非 0 时 返 回 整 数 值 1; 否 则 返 回 0, 逻 辑 或 具 有
从 左 到 右 的 结 合 律 。
语 法
逻 辑 或 表 达 式 :
逻 辑 与 表 达 式
逻 辑 或 表 达 式 || 逻 辑 与 表 达 式
逻 辑 或 运 算 符 的 操 作 数 不 要 求 为 同 一 类 型 , 但 必 须 为 整 型 或 指 针 类 型 , 操 作 数 通常 为 关 系 或 相 等 表 达 式 。
在 继 续 计 算 逻 辑 或 表 达 式 之 前 第 一 个 操 作 数 被 完 全 求 值 , 且 所 有 的 副 作 用 被 完成 。
第 二 个 操 作 数 仅 在 第 一 个 操 作 数 被 求 值 为 假 (0) 时 才 被 计 算 求 值 , 这 种 求 值 去 除了 第 二 个 操 作 数 在 逻 辑 或 表 达 式 为 真 时 不 必 要 的 求 值 。
printf("%d",(x==w || x==y || x==z));
在 此 例 中 , 如 果 x 等 于 w 、 y 或 z 中 的 任 一 值 ,printf 函 数 的 第 二 个 变 量 求 值 为真 , 且 打 印 值 1 。 否 则 , 它 求 值 为 假 , 且 打 印 值 0, 只 要 有 一 个 条 件 求 值 为 真 , 求 值即 终 止 。
赋 值 运 算 符
赋 值 运 算 符 在 由 左 操 作 数 指 定 的 对 象 中 存 入 一 个 值 。 有 两 种 赋 值 操 作 : “ 简 单 赋值 ” 即 将 第 二 个 操 作 数 的 值 存 入 到 由 第 一 个 操 作 数 指 定 的 对 象 中 ; “ 复 合 赋 值 ” 即 在 存 储 结 果 之 前 执 行 一 个 算 术 、 移 位 或 按 位 操 作 。 表 4.4 中 除 了 = 运 算 符 外 所有 的 赋 值 运 算 符 都 是 复 合 赋 值 运 算 符 。
表 4.4 赋 值 运 算 符
运算符 含义
= 将 第 二 个 操 作 数 的 值 存 入 第 一 个 操 作 数 指 定 的 对 象 中 ( “ 简 单 赋值” )
*= 将 第 一 个 操 作 数 值 乘 以 第 二 个 操 作 数 值 , 将 结 果 存 入 第 一 个 操 作数指定 的对象中
/= 将 第 一 个 操 作 数 值 除 以 第 二 个 操 作 数 值 , 将 结 果 存 入 第 一 个 操 作数指定的对象中
%= 第 一 个 操 作 数 除 以 第 二 个 操 作 数 的 模 数 , 将 结 果 存 入 第 一 个 操 作数指定 的对象中
+= 将 第 二 个 操 作 数 的 值 加 入 第 一 个 操 作 数 的 值 中 , 将 结 果 存 入 第 一个操作数指定的对象中
-= 从 第 一 个 操 作 数 值 中 减 去 第 二 个 操 作 数 的 值 , 将 结 果 存 入 第 一 个操作数指定的对象中
<<= 将 第 一 个 操 作 数 的 值 左 移 第 二 个 操 作 数 值 指 定 的 位 数 , 将 结 果 存入第一个操作数指定的对象中
>>= 将 第 一 个 操 作 数 的 值 右 移 第 二 个 操 作 数 的 值 指 定 的 位 数 , 将 结 果存入第一个操作数指定的对象中
&= 获 取 第 一 和 第 二 个 操 作 数 按 位 与 的 结 果 , 将 结 果 存 入 第 一 个 操 作数指定的对象中
^= 获 取 第 一 和 第 二 个 操 作 数 按 位 异 或 的 结 果 , 将 结 果 存 入 第 一 个 操作数指定的对象中
续 表
|= 获 取 第 一 和 第 二 个 操 作 数 按 位 或 的 结 果 , 将 结 果 存 入 第 一 个 操 作数指定的对象中
语 法
赋 值 表 达 式 :
条 件 表 达 式
单 目 表 达 式 赋 值 运 算 符 赋 值 表 达 式赋 值 运 算 符 : 以 下 之 一
= *= /= %= += -= <<= >>= &= ^= |=
赋 值 运 算 符 的 结 果
赋 值 运 算 符 返 回 由 左 操 作 数 指 定 的 对 象 赋 值 后 的 值 。 结 果 类 型 为 左 操 作 数 类 型 。赋 值 表 达 式 的 结 果 总 是 一 个 l 值 。 这 些 运 算 符 具 有 从 右 到 左 的 结 合 律 。 左 操 作数 必 须 是 一 个 可 修 改 的 l 值 。
注 意 : 在 ANSI C 中 , 赋 值 表 达 式 的 结 果 不 是 一 个 l 值 , 因 此 ,C++ 的 合 法 表 达 式(a+=b)+=c 在 C 中 是 非 法 的 。
简 单 赋 值
简 单 赋 值 运 算 符 (=) 将 第 二 个 操 作 数 的 值 存 入 第 一 个 操 作 数 指 定 的 对 象 中 。 如 果两 个 对 象 都 是 算 术 类 型 , 在 值 存 储 前 右 操 作 数 被 转 换 为 左 操 作 数 的 类 型 。
const 和 volatile 类 型 对 象 可 以 赋 给 volatile 或 既 不 是 const 又 不 是 volatile 类 型 的 l 值 。
对 类 类 型 (struct 、 union 及 class 类 型 ) 对 象 的 赋 值 由 命 名 为 运 算 符 = 的 函 数 来执 行 。 此 运 算 符 函 数 的 缺 省 行 为 是 执 行 按 位 拷 贝 ; 但 此 行 为 可 用 重 载 的 运 算 符 进行 修 改 ( 有 关 更 多 的 信 息 参 见 第 12 章 “ 重 载 ” 中 的 “ 重 载 的 运 算 符 ” ) 。
任 何 从 一 给 定 基 类 非 模 糊 地 派 生 的 类 的 对 象 可 被 赋 给 基 类 的 一 个 对 象 。 反 之 则不 行 , 因 为 从 派 生 类 到 基 类 存 在 隐 式 转 换 , 但 从 基 类 到 派 生 类 没 有 。 例 如 : #include <iostream.h>
class ABase
{
public:
ABase() { cout << "constructing ABase\n"; }
};-+
class ADerived:public ABase
{
public:
ADerived() { cout << "constructing ADerived\n"; }
};
void main()
{
ABase aBase;
ADerived aDerived;
aBase=aDerived; // 可 行
aDerived=aBase; // 错 误
}
引 用 类 型 的 赋 值 就 好 象 正 在 对 引 用 指 向 的 对 象 进 行 赋 值 一 样 。
对 类 类 型 对 象 , 赋 值 与 初 始 化 不 同 。 为 了 说 明 赋 值 和 初 始 化 如 何 不 同 , 考 虑 以 下代 码 :
userType1 A;
UserType2 B=A;
上 面 代 码 给 出 了 一 个 初 始 化 器 , 它 为 UserType1 调 用 构 造 函 数 , 该 构 造 函 数 是 有一 个 UserType1 类 型 的 一 个 参 量 。 给 出 代 码 :
UserType1 A;
UserType2 B;
B=A;
赋 值 语 句 :
B=A;
可 以 有 以 下 的 一 个 作 用 :
-
为 UserType2 调 用 函 数 运 算 符 =, 提 供 的 运 算 符 = 用 一 个 UserType1 参 量 提 供 的 。
-
调 用 显 式 转 换 函 数 UserType1::operator UserType2, 如 果 此 函 数 存
在 的 话 。
- 调 用 一 个 构 造 函 数 UserType2::UserType2, 假 如 这 样 的 构 造 函 数 存在 , 而 且 带 一 个 UserType1 参 量 , 并 拷 贝 结 果 。
复 合 赋 值
表 4.4 中 给 出 的 复 合 赋 值 运 算 符 指 定 为 e1 op=e2 形式 , 其 中 e1 是 一 个 非 常 量 类型 的 可 修 改 的 l 值 , e2 是 以 下 之 一 :
-
一 个 算 术 类 型
-
一 个 指 针 , 如 果 op 是 + 或 -
e1 op=e2 形 式 与 e1=e1 op e2 执 行 相 同 的 动 作 , 但 e1 仅 被 求 值 一 次 。
对 枚 举 类 型 的 复 合 赋 值 将 产 生 一 个 错 误 信 息 。 如 果 左 操 作 数 是 一 个 指 针 类 型 , 右操 作 数 必 须 为 指 针 类 型 或 必 须 为 等 于 0 的 常 量 表 达 式 ; 如 果 左 操 作 数 是 一 个 整 型 , 右 操 作 数 一 定 不 能 为 指 针 类 型 。
逗 号 运 算 符
逗 号 运 算 符 允 许 对 两 个 语 句 分 组 , 其 中 一 个 是 预 期 的 。
语 法
表 达 式 :
赋 值 表 达 式
表 达 式 , 赋 值 表 达 式
逗 号 运 算 符 具 有 从 左 到 右 的 结 合 律 。 由 逗 号 分 开 的 两 个 表 达 式 从 左 到 右 求 值 。左 操 作 数 总 是 被 求 值 的 , 而 且 所 有 副 作 用 在 右 操 作 数 求 值 之 前 完 成 。
考 虑 表 达 式 :
e1,e2
该 表 达 式 的 类 型 和 值 是 e2 的 类 型 和 值 , e1 求 值 的 结 果 被 丢 弃 。 如 果 右 操 作 数是 一 个 l 值 则 结 果 是 一 个 l 值 。
逗 号 具 有 特 殊 含 义 时 ( 例 如 , 在 函 数 的 实 参 中 或 集 合 初 始 化 器 中 ), 逗 号 运 算 符 及其 操 作 数 必 须 包 括 在 括 号 中 。 因 此 , 以 下 函 数 调 用 不 是 等 价 的 :
// 说 明 函 数
void Func(int ,int); void Func(int);
Func(arg1,arg2); // 调 用 Func(int ,int) Func((arg1,arg2)); // 调 用 Func(int)
该 例 子 说 明 逗 号 运 算 符 :
for (i=j=1;i+j<20;i+=i,j--);
在 此 例 中 ,for 语 句 的 第 三 个 表 达 式 的 每 个 操 作 数 被 独 立 地 求 值 , 左 操 作 数 i+=i 首 先 被 求 值 ; 然 后 右 操 作 数 j-- 被 求 值 。
func_one(x,y+2,z); func_two((x--,y+2),z);
在 func_one 函 数 调 用 中 , 由 逗 号 分 开 的 三 个 参 量 被 传 递 :x,y+2 和 z; 在 func_two 函 数 调 用 中 , 括 号 迫 使 编 译 器 将 第 一 个 逗 号 解 释 为 序 列 求 值 运 算 符 。 此 函 数 调 用向 func_two 传 递 两 个 参 量 , 第 一 个 参 量 是 序 列 求 值 操 作 (x--,y+2) 的 结 果 , 具 有表 达 式 y+2 的 值 和 类 型 ; 第 二 个 参 量 是 z 。
带 条 件 运 算 符 的 表 达 式
条 件 运 算 符 (?:) 是 一 个 三 目 运 算 符 ( 它 带 三 个 操 作 数 ) 。 条 件 运 算 符 的 工 作 如 下 :
-
在 继 续 之 前 , 第 一 个 操 作 数 被 求 值 , 且 所 有 的 副 作 用 被 完 成 。
-
如 果 第 一 个 操 作 数 求 值 为 真 ( 一 个 非 0 值 ), 第 二 个 操 作 数 被 求 值 。
-
如 果 第 一 个 操 作 数 求 值 为 假 (0), 第 三 个 操 作 数 被 求 值 。
条 件 运 算 符 的 结 果 是 被 求 值 的 操 作 数 , 即 第 二 或 第 三 个 的 结 果 。 在 条 件 表 达 式 中后 两 个 操 作 数 只 有 一 个 被 求 值 。
语 法
条 件 表 达 式 :
逻 辑 或 表 达 式
逻 辑 或 表 达 式 ? 表 达 式 : 条 件 表 达 式
条 件 表 达 式 无 结 合 律 。 第 一 个 操 作 数 必 须 为 整 型 或 指 针 类 型 , 以 下 规 则 用 于 第 二及 第 二 个 操 作 数 :
-
如 果 两 个 表 达 式 是 同 类 型 的 , 则 结 果 是 那 个 类 型 的 。
-
如 果 两 个 表 达 式 都 是 算 术 类 型 , 则 常 用 的 算 术 转 换 ( 包 含 在 第 3 章“ 标准 转 换 ” 中 的 “ 算 术 转 换 ” ) 被 执 行 以 将 它 们 转 换 为 公 用 类 型 。
-
如 果 两 个 表 达 式 都 是 指 针 类 型 或 一 个 是 指 针 类 型 , 另 一 个 是 求 值 为 0 的 常 量 表 达 式 , 则 指 针 转 换 被 执 行 去 将 它 们 转 换 为 公 用 类 型 。
-
如 果 两 个 表 达 式 都 是 引 用 类 型 , 则 引 用 转 换 被 执 行 以 将 它 们 转 换 为公 用 类 型 。
-
如 果 两 个 表 达 式 都 是 void 类 型 , 则 公 用 类 型 为 void 类 型 。
-
如 果 两 个 表 达 式 是 一 个 给 定 类 类 型 , 则 公 用 类 型 是 那 个 类 类 型 。
任 何 未 列 入 前 面 清 单 的 第 二 和 第 三 个 操 作 数 的 组 合 都 是 非 法 的 。 结 果 的 类 型 是公 用 类 型 , 且 如 果 第 二 和 第 三 个 操 作 数 为 同 一 类 型 且 都 是 l 值 的 则 结 果 是 一 个 l 值 。
例 如 :
(val>=0) ? val : -val
如 果 条 件 为 真 , 表 达 式 等 于 val; 否则 , 表 达 式 等 于 -val 。
带 量 表 达 式
C++ 在 以 下 说 明 中 要 求 常 量 表 达 式 , 即 等 于 常 量 的 表 达 式 :
-
数 组 界 限
-
case 语 句 中 的 选 择 器
-
位 域 长 度 规 格
-
枚 举 初 始 化 器
语 法
常 量 表 达 式 :
条 件 表 达 式
在 常 量 表 达 式 中 唯 一 合 法 的 操 作 数 是 :
-
文 字
-
枚 举 常 量
-
用 常 量 表 达 式 初 始 化 的 说 明 为 常 量 的 值
-
sizeof 表 达 式
在 常 量 表 达 式 中 非 整 型 常 量 必 须 转 换 ( 显 式 或 隐 式 地 ) 为 合 法 的 整 型 。 因 此 , 以 下代 码 是 合 法 的 :
const double Size=11.0 ; char chArray[(int)Size];
在 常 量 表 达 式 中 显 式 地 转 换 为 整 型 是 合 法 的 , 除 了 用 作 sizeof 运 算 符 的 操 作 数之 外 。 所 有 其 它 类 型 或 派 生 类 型 都 是 非 法 的 。
逗 号 运 算 符 和 赋 值 运 算 符 不 能 用 于 常 量 表 达 式 中 。
带 显 式 类 型 转 换 的 表 达 式
正 如 第 3 章 “ 标 准 转 换 ” 中 描 述 的 ,C++ 提 供 隐 式 类 型 转 换 。 当 你 需 要 更 精 确 地控 制 应 用 的 转 换 时 , 你 还 可 以 指 定 显 式 类 型 转 换 。
显 式 类 型 转 换 运 算 符
C++ 允 许 使 用 与 函 数 调 用 语 法 类 似 的 语 法 进 行 显 式 类 型 转 换 , 一 个 简 单 类 型 名 称后 面 跟 随 一 个 括 在 括 号 中 的 表 达 式 表 , 构 成 用 指 定 表 达 式 指 定 的 类 型 的 一 个 对 象以 下 例 子 给 出 了 一 个 转 到 int 类 型 的 显 式 类 型 转 换 :
int i=int(d);
以 下 例 子 使 用 定 义 在 “ 函 数 调 用 结 果 ” 中 的 Point 类 的 修 改 后 的 版 本 : #include <iostream.h>
class Point
{
public:
// 定 义 缺 省 构 造 函 数
Point() {_x = _y = 0;}
// 定 义 另 一 个 构 造 函 数
Point(int X,int Y) {_x=X;_y=Y;}
// 定 义 作 为 引 用 类 型 的 “ accessor ” 函 数
unsigned& x() { return _x;}
unsigred& y() { return _y;}
void Show() { cout << "x=" << _x << ","
<< "y=" << _y << "\n";} private:
unsigned _x;
unsigned _y;
};
void main ()
{
Point Point1,Point2;
// 赋 给 point1 显 示 转 换 为 (10,10)
Point1=Point(10,10);
// 通 过 对 unsigned 类 型 赋 一 个 20 的 显 示 转 换 使 x() 作 为 一 个 l 值 来 用
Point1.x()=unsigned(20);
Point1.Show();
// 赋 给 Point2 缺 省 的 point 对 象
Point2=Point();
Point2.Show();
}
此 程 序 的 输 出 为 :
x=20,y=10 x=0,y=0
尽 管 前 面 的 例 子 阐 述 的 是 使 用 常 量 的 显 式 类 型 转 换 , 在 对 象 上 用 同 样 的 技 巧 去 执行 这 些 转 换 。 以 下 代 码 段 说 明 这 种 情 况 :
int i=7; float d:
d=float(i);
显 示 类 型 转 换 还 可 以 用 “ 造 型 转 换 ” 语 法 指 定 , 前 面 的 例 子 , 用 造 型 转 换 语 法 重写 为 :
d=(float)i;
当 从 单 值 转 换 时 , 造 型 转 换 和 函 数 形 式 转 换 具 有 相 同 的 结 果 。 但 在 函 数 形 式 语 法中 , 你 可 以 为 转 换 指 定 多 个 参 量 。 这 个 不 同 点 对 于 用 户 定 义 的 类 型 是 很 重 要 的 。
考 虑 一 个 Point 类 及 其 转 换 :
struct Point
{
Point(short x, short y) { _x=x,_y=y; }
...
short _x,_y;
};
...
Point pt=Point(3,10);
前 面 的 例 子 , 使 用 了 函 数 形 式 转 换 , 显 示 了 如 何 将 两 个 值 ( 一 个 为 x, 一 个 为 y) 转换 为 用 户 定 义 的 类 型 Point 。
重 要 点 : 使 用 显 式 类 型 转 换 要 小 心 , 因 为 它 们 覆 盖 了 C++ 编 译 器 的 内 部 类 型 检 查 。
语 法
造 型 转 换 表 达 式 :
单 目 表 达 式
( 类 型 名 ) 造 型 转 换 表 达 式
造 型 标 记 必 须 用 于 不 具 有 一 个 简 单 类 型 名 称 ( 例 如 指 针 或 引 用 类 型 ) 的 类 型 的 转换 。 可 以 用 一 个 简 单 类 型 名 称 表 示 的 类 型 的 转 换 可 以 用 任 何 两 者 之 一 形 式 书 写 。有 关 哪 些 构 成 一 个 简 单 类 型 名 称 的 更 多 信 息 , 参 见 第 6 章 “ 说 明 ” 中 的 “ 类 型 指示 符 ”。
在 造 型 转 换 内 的 类 型 定 义 是 非 法 的 。
合 法 转 换
如 果 转 换 可 以 用 标 准 转 换 实 现 , 则 你 可 以 从 一 给 定 类 型 向 另 一 类 型 做 显 式 转 换 , 其 结 果 是 相 同 的 。 本 节 描 述 的 转 换 是 合 法 的 , 任 何 其 它 的 非 显 式 地 由 用 户 定 义 的转 换 ( 为 一 个 类 类 型 ) 是 非 法 的 。
如 果 指 针 足 够 大 以 便 保 存 整 型 值 , 则 一 个 整 型 值 可 被 显 式 地 转 换 为 一 个 指 针 , 转换 到 一 个 整 型 值 的 指 针 可 以 再 转 回 到 指 针 , 其 值 是 相 同 的 。 这 个 一 致 性 是 由 以下 等 式 给 出 ( 其 中 p 表 示 任 意 类 型 的 一 个 指 针 ):
p ==( type *) 整 型 转 换 ( p )
用 显 式 转 换 , 编 译 器 不 检 查 被 转 换 的 值 是 否 适 合 新 类 型 , 但 从 指 针 到 整 型 的 转 换除 外 , 反 之 亦 然 。
本 节 描 述 以 下 转 换 :
-
转 换 指 针 类 型
-
转 换 空 指 针
-
转 换 到 一 个 前 向 引 用 类 类 型
-
转 换 到 引 用 类 型
-
在 成 员 指 针 类 型 间 转 换
转 换 指 针 类 型
-
个 对 象 类 型 的 指 针 可 被 显 式 地 转 换 到 另 一 个 对 象 类 型 的 指 针 , 被 说 明 为 void * 的 指 针 被 认 为 是 指 向 任 何 对 象 类 型 的 指 针 。
-
个 基 类 指 针 可 以 显 式 地 转 换 到 一 个 派 生 类 的 指 针 , 只
要 以 下 条 件 被 满 足 :
-
有 一 个 非 模 糊 的 转 换 。
-
在 任 意 点 , 基 类 都 未 被 说 明 为 虚 拟 的 。
-
由 于 到 void * 类 型 的 转 换 可 以 改 变 一 个 对 象 的 表 示 , 所 以 不 保 证 转 换 type1*
void * type2 * 等 价 于 转 换 type1* type2* ( 仅 是 值 的 改 变 ) 。
当 执 行 这 样 的 转 换 时 , 结 果 是 指 向 表 示 基 类 的 源 对 象 的 子 对 象 的 一 个 指 针 。关 于 模 糊 的 和 虚 拟 的 基 类 更 多 信 息 参 见 第 9 章 “ 派 生 的 类 ”。
C++ 允 许 对 象 或 函 数 指 针 显 式 转 换 到 void * 类 型 。
如 果 函 数 指 针 类 型 有 足 够 的 位 容 纳 对 象 指 针 类 型 , 则 对 象 指 针 类 型 可 显 式 地 转 换到 函 数 指 针 。
常 量 对 象 的 指 针 可 显 式 地 转 换 到 非 常 量 类 型 的 指 针 , 此 转 换 的 结 果 指 向 源 对 象 。常 量 类 型 对 象 或 常 量 类 型 对 象 的 引 用 可 造 型 转 换 到 非 常 量 类 型 的 一 个 引 用 。 结果 是 源 对 象 的 一 个 引 用 。 源 对 象 很 可 能 说 明 为 常 量 , 因 为 它 要 在 程 序 持 续 期 间 保留 常 量 。 因 此 , 一 个 显 式 转 换 导 致 这 种 安 全 保 护 失 败 , 而 允 许 对 这 种 对 象 的 修 改 , 在 这 种 情 况 中 的 动 作 是 不 确 定 的 。
volatile 类 型 对 象 的 指 针 可 以 造 型 转 换 到 非 volatile 类 型 的 指 针 , 这 种 转 换 的结 果 指 的 是 源 对 象 。 类 似 地 ,volatile 类 型 对 象 可 以 造 型 转 换 为 非 volatile 类型 的 引 用 。
转 换 空 指 针
空 指 针 (0) 被 转 换 到 它 自 己 。
转 换 到 一 个 前 向 引 用 类 类 型
一 个 类 已 被 说 明 但 尚 未 定 义 ( 一 个 前 向 引 用 ) 可 被 用 于 指 针 造 型 转 换 , 在 这 种 情 况
下 , 如 果 类 的 关 系 已 知 , 编 译 器 返 回 一 个 源 对 象 指 针 , 而 不 是 可 能 的 子 对 象 的 指针 。
转 换 到 引 用 类 型
任 何 其 地 址 可 被 转 换 到 一 个 给 定 指 针 类 型 的 对 象 , 也 可 以 转 换 到 模 拟 的 引 用 类 型例 如 , 任 何 其 地 址 可 被 转 换 到 类 型 char * 的 对 象 也 可 转 换 到 类 型 char & 。 没 有构 造 函 数 或 类 转 换 函 数 被 调 用 以 产 生 一 个 到 引 用 类 型 的 转 换 。
对 象 或 值 可 被 转 换 到 类 类 型 对 象 , 仅 当 为 此 目 的 已 特 别 提 供 了 一 个 构 造 函 数 或 转换 运 算 符 。 关 于 这 些 用 户 定 义 的 函 数 的 更 多 信 息 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 转 换 构 造 函 数 ”。
- 个 基 类 的 引 用 到 一 个 派 生 类 的 引 用 的 转 换 ( 或 反 之 亦 然 ) 与 指 针 的 作 法 相 同 。到 引 用 类 型 的 造 型 转 换 结 果 为 一 个 l 值 , 造 型 转 换 到 其 它 类 型 的 结 果 不 是 l 值 。在 指 针 或 引 用 造 型 转 换 的 结 果 上 执 行 的 操 作 仍 在 源 对 象 上 执 行 。
在 成 员 指 针 类 型 之 间 转 换
- 个 成 员 指 针 可 以 按 以 下 规 则 转 换 为 不 同 的 成 员 指 针 类 型 : 两 个 指 针 必 须 是 同 一个 类 的 成 员 指 针 或 必 须 是 两 个 类 的 成 员 指 针 , 且 其 中 之 一 非 模 糊 地 从 另 一 个 派 生而 来 。 当 转 换 成 员 指 针 函 数 时 , 返 回 的 与 参 量 的 类 型 必 须 匹 配 。
带 成 员 指 针 运 算 符 的 表 达 式
成 员 指 针 运 算 符 .* 和 ->* 返 回 表 达 式 左 侧 指 定 的 对 象 的 特 定 的 类 成 员 的 值 , 以 下例 子 显 示 了 如 何 使 用 这 些 运 算 符 :
#include <iostream.h>
class Window
{
public:
void Paint(); // 使 窗 口 重 画
int WindowsId;
};
// 定 义 派 生 的 类 型 pmfnPaint 和 p mW indowId
// 这 些 类 型 分 别 是 Paint() 和 WindowId 成 员 指 针void (Window::*pmfnPaint)()=&Window::Paint; int Window::*pmWindowId=&Window::WindowId;
void main()
{
Window AWindow;
Window *pWindow=new Window;
// 正 常 调 用 Paint 函 数 , 然 后 使 用 成 员 指 针
AWindow.Paint();
(AWindow.*pmfnPaint)();
pWindow -> Paint();
(pWindow -> *pmfnPaint)(); // 由 于 * 没 有 函 数 调 用 联 结 紧 密 , 所 以 需 要括 号
int Id;
// 检 索 窗 口 编 号
Id=AWindow.*pmWindowId;
Id=pWindow->*pmWindowId;
}
在 上 面 例 子 中 ,pmfnPaint 的 成 员 指 针 被 用 于 调 用 成 员 函 数 Paint 。 另 一 个 成 员pmWindowId 的 指 针 被 用 于 访 问 WindowId 成 员 。
语 法
pm- 表 达 式 :
造 型 转 换 表 达 式
pm- 表 达 式 .* 造 型 转 换 表 达 式
pm- 表 达 式 ->* 造 型 转 换 表 达 式
双 目 运 算 符 .* 将 其 第 一 个 操 作 数 ( 即 必 须 为 一 个 类 类 型 对 象 ) 和 第 二 操 作 数 ( 即 必须 为 成 员 指 针 类 型 ) 组 合 在 一 起 。
双 目 运 算 符 ->* 将 其 第 一 个 操 作 数 ( 即 必 须 为 类 类 型 对 象 的 指 针 ) 和 第 二 个 操 作 数( 即 必 须 为 成 员 指 针 类 型 ) 组 合 在 一 起 。
在 一 个 包 含 .* 运 算 符 的 表 达 式 中 , 第 一 个 操 作 数 必 须 为 在 第 二 个 操 作 数 中 指 定 的成 员 指 针 的 类 类 型 , 且 可 访 问 该 指 针 或 非 模 糊 地 从 那 个 类 派 生 且 可 访 问 的 一 个可 访 问 类 型 。
在 一 个 包 含 ->* 运 算 符 的 表 达 式 中 , 第 一 个 操 作 数 必 须 为 第 二 个 操 作 数 指 定 类 型的 “ 类 类 型 指 针 ” 类 型 , 或 必 须 为 从 那 个 类 非 模 糊 派 生 出 的 一 个 类 型 。
考 虑 以 下 类 和 程 序 段 :
class BaseClass
{
publc:
BaseClass(); // 基 类 构 造 函 数
void Funcl();
};
// 说 明 成 员 函 数 Func1 的 一 个 指 针
void (BaseClass::*pmfnFunc1)()=&BaseClass::Func1;
class Derived : public BaseClass
{
public:
Derived(); // 派 生 类 构 造 函 数
void Func2();
};
// 说 明 成 员 函 数 Func2 的 一 个 指 针
void (Derived::*pmfnFunc2)()=&Derived::Func2;
void main()
{
BaseClass ABase;
Derived ADerived;
(ABase.*pmfnFunc1)(); // 可 行 : 为 Baseclass 定 义
(ABase.*pmfnFunc2)(); // 错 误 : 不 能 用 基 类 去 访 问 派 生 类 成 员 指 针
(ADerived.*pmfnFunc1)(); // 可 行 :Derived 是 从 BaseClass 非 模 糊 地 派生 的
(ADerived.*pmfnFunc2)(); // 可 行 : 为 Derived 定义
}
.* 或 ->* 成 员 指 针 运 算 符 的 结 果 是 成 员 指 针 说 明 中 指 定 的 类 型 的 一 个 对 象 或 函数 。 因 此 , 在 前 面 的 例 子 中 ,ADerived.*pmfnFunc1() 表 达 式 的 结 果 是 返 回 void 的 函 数 的 一 个 指 针 。 如 果 第 二 个 操 作 数 值 为 1, 则 此 结 果 也 值 为 1 。
注 意 : 如 果 成 员 指 针 运 算 符 之 一 的 结 果 是 一 个 函 数 , 那 么 结 果 仅 能 被 用 作 函 数 调用 运 算 符 的 一 个 操 作 数 。
表 达 式 的 语 义
本 节 解 释 表 达 式 在 何 时 以 及 以 什 么 样 的 顺 序 被 求 值 , 包 括 以 下 主 题 :
-
求 值 的 顺 序
-
顺 序 点
-
模 糊 表 达 式
-
表 达 式 中 的 表 示
求 值 的 顺 序
本 节 讨 论 表 达 式 被 求 值 的 顺 序 , 但 不 解 释 这 些 表 达 式 中 运 算 符 的 语 法 或 语 义 , 本章 较 前 面 的 章 节 提 供 了 这 些 运 算 符 每 个 的 完 整 参 考 。
表 达 式 按 照 其 运 算 符 的 优 先 级 及 分 组 情 况 被 求 值 ( 第 1 章“ 词 法 规 定 ”中 的 表 1.1 给 出 了 施 加 于 表 达 式 上 的 C++ 运 算 符 的 关 系 ) 。 考 虑 这 个 例 子 :
#include <iostream.h>
void main()
{
int a=2,b=4,c=9;
cout << a + b * c << "\n";
cout << a + (b * c) << "\n";
cout << (a + b) * c<< "\n";
}
前 面 代 码 的 输 出 为 : 38
38
54
图 4.1 表 达 式 求 值 顺 序
图 4.1 中 给 出 的 表 达 式 求 值 顺 序 由 运 算 符 的 优 先 级 和 结 合 律 确 定 :
-
在 这 个 表 达 式 中 乘 法 (*) 具 有 最 高 优 先 级 , 因 此 ,b*c 子 表 达 式 首 先 被 求 值 。
-
加 法 (+) 具 有 次 级 优 先 级 , 所 以 a 被 加 到 b 和 c 的 积 中 。
-
左 移 (<<) 在 表 达 式 中 具 有 最 低 优 先 级 , 但 有 两 次
出 现 , 由 于 左 移 运 算 符 从 左 到右 分 组 , 左 子 表 达 式 首 先 被 求 值 , 而 后 是 右 子 表 达 式 。
当 括 号 被 用 于 分 组 子 表 达 式 时 , 它 们 改 变 表 达 式 被 求 值 的 优 先 级 以 及 求 值 顺 序 , 如 图 4.2 所 示 。
图 4.2 带 括 号 的 表 达 式 求 值 顺 序
像 图 4.2 中 那 些 表 达 式 纯 粹 是 为 了 副 作 用 而 求 值 , 在 这 种 情 况 下 , 将 信 息 转 换 到标 准 输 出 设 备 。
注 意 : 左 移 运 算 符 被 用 于 向 一 个 类 输 出 流 对 象 中 插 入 一 个 对 象 。 当 与 输 入 输 出 流一 起 使 用 时 , 有 时 它 被 称 为 “ 插 入 ” 运 算 符 。 有 关 输 入 输 出 流 库 的 更 多 内 容 , 参见 “ Microsoft Visual C++6.0 参 考 库 ” 的 “ Micosoft Visual C++6.0 运 行 库参 考 ” 卷 。
顺 序 点
- 个 表 达 式 在 连 续 的 “ 顺 序 点 ” 之 间 只 能 改 变 一 个 对 象 的 值 一 次 。
Microsoft 特 殊 处 →
C++ 语 言 定 义 现 在 不 指 定 顺 序 点 。 对 任 何 包 含 C 运 算 符 和 不 包 含 重 载 的 运 算 符 的表 达 式 ,Microsoft C++ 使 用 与 ANSI C 相 同 的 顺 序 点 。 当 运 算 符 被 重 载 时 , 语 义从 运 算 符 顺 序 变 为 函 数 调 用 顺 序 。 Microsoft C++ 使 用 以 下 顺 序 点 :
-
逻 辑 与 运 算 符 (&&) 的 左 操 作 数 , 在 继 续 之 前 , 逻 辑 与 运 算 符 的 左 操 作数 被 完 全 求 值 , 且 所 有 的 副 作 用 被 完 成 。 不 保 证 逻 辑 与 运 算 符 的 右操 作 数 将 被 求 值 。
-
逻 辑 或 运 算 符 (||) 的 左 操 作 数 , 在 继 续 之 前 , 逻 辑 或 运 算 符 的 左 操 作数 被 完 全 求 值 , 且 所 有 的 副 作 用 被 完 成 。 不 保 证 逻 辑 或 运 算 符 的 右操 作 数 将 被 求 值 。
-
逗 号 运 算 符 的 左 操 作 数 , 在 继 续 之 前 , 逗 号 运 算 符 的 左 操 作 数 被 完 全求 值 , 且 所 有 的 副 作 用 被 完 成 。 逗 号 运 算 符 的 两 个 操 作 数 总 是 被 求值 的 。
-
函 数 调 用 运 算 符 , 一 个 函 数 的 函 数 调 用 表 达 式 和 所 有 的 参 量 , 包 括 缺省 参 量 , 在 进 入 函 数 之 前 被 求 值 , 且 所 有 的 副 作 用 被 完 成 。 在 参 量 或函 数 调 用 表 达 式 之 间 没 有 特 别 指 定 求 值 的 顺 序 。
-
条 件 运 算 符 的 第 一 个 操 作 数 , 在 继 续 之 前 , 条 件 运 算 符 的 第 一 个 操 作数 被 完 全 求 值 , 且 所 有 的 副 作 用 被 完 成 。
-
一 个 完 整 初 始 化 表 达 式 的 结 束 , 如 在 说 明 语 句 中 一 个 初 始 化 的 结束 。
-
在 表 达 式 语 句 中 的 表 达 式 , 表 达 式 语 句 由 可 选 的 表 达 式 后 跟 一 个 分号 (;) 组 成 。 表 达 式 为 其 副 作 用 而 被 完 全 求 值 。
-
在 一 个 选 择 (if 或 switch) 语 句 中 的 控 制 表 达 式 , 在 依 赖 于 这 样 的 代码 被 执 行 之 前 , 表 达 式 被 完 全 求 值 , 且 所 有 副 作 用 被 完 成 。
-
在 一 个 while 或 do 语 句 中 的 控 制 表 达 式 。 在 while 或 do 循 环 的 下一 次 重 复 的 任 何 语 句 执 行 之 前 , 表 达 式 被 完 全 求 值 , 且 所 有 副 作 用 被
完 成 。
-
for 语 句 三 个 表 达 式 中 的 每 一 个 , 在 进 入 下 一 个 表 达 式 之 前 , 每 一 个表 达 式 被 完 全 求 值 , 且 所 有 副 作 用 被 完 成 。
-
在 返 回 语 句 中 的 表 达 式 , 在 控 制 返 回 到 调 用 函 数 之 前 , 表 达 式 被 完 全求 值 , 且 所 有 副 作 用 被 完 成 。
Microsoft 特 殊 处 结 束
模 糊 的 表 达 式
某 些 表 达 式 在 含 义 上 是 模 糊 的 , 当 同 一 个 表 达 式 中 一 个 对 象 的 值 被 不 止 一 次 地 修改 时 , 这 些 表 达 式 最 频 繁 地 出 现 。 这 些 表 达 式 依 赖 于 一 个 特 定 的 求 值 顺 序 , 而 该语 言 中 却 没 有 定 义 一 种 求 值 顺 序 , 考 虑 以 下 例 子 :
int i=7;
func(i,++i);
C++ 语 言 不 保 证 在 函 数 调 用 中 参 量 的 求 值 顺 序 。 因 此 , 在 上 例 中 ,func 可 能 为 其参 量 接 收 7 和 8 或者 8 和 8, 这 取 决 于 参 量 是 从 左 到 右 还 是 从 右 到 左 求 值 的 。
表 达 式 中 的 表 示 法
C++ 语 言 在 指 定 操 作 数 时 指 定 某 些 兼 容 性 , 表 4.5 给 出 了 要 求 操 作 数 为 type 类 型的 运 算 符 可 接 受 的 操 作 数 的 类 型 。
表 4.5 运 算 符 可 接 受 的 操 作 数 类 型
期望的类型 允许的类型
type const type
volatile type
type &
const type &
volatile type &
volatile const type
volatile const type &
type* type* coust
type* volatile
type* volatile const
const type type
const type
const type &
volatile type type
volatile type
volatile type &
由 于 前 面 的 规 则 总 是 可 以 用 于 组 合 中 , 在 期 望 一 个 指 针 的 地 方 可 以 提 供 一 个 常 量指 针 指 向 一 个 volatile 对 象 。
造 型 转 换
C++ 语 言 规 定 : 如 果 一 个 从 基 类 派 生 的 类 包 含 虚 函 数 , 则 指 向 那 个 基 类 类 型 的 指 针可 用 于 调 用 存 在 于 派 生 类 对 象 中 的 该 虚 函 数 的 实 现 。 包 含 虚 函 数 的 类 有 时 被 称为 一 个 “ 多 态 类 ”。
由 于 一 个 派 生 类 完 全 包 含 它 所 从 派 生 的 所 有 基 类 的 定 义 , 所 以 可 安 全 地 造 型 一 个指 针 向 上 到 类 层 次 指 向 这 些 基 类 的 任 意 一 个 。 给 定 一 个 指 向 基 类 的 指 针 , 按 层 次向 下 造 型 指 针 可 以 是 安 全 的 , 如 果 正 被 指 向 的 对 象 实 际 是 从 基 类 派 生 的 一 个 类 型则 是 安 全 的 。 在 这 种 情 况 下 , 实 际 对 象 被 说 是 “ 完 整 的 对 象 ” , 基 类 指 针 被 说 是指 向 完 整 对 象 的 “ 子 对 象 ”。 例 如 , 考 虑 图 4.3 中 给 出 的 类 层 次 。
图 4.3 类 层 次
- 个 类 型 C 的 对 象 , 如 图 4.4 中 所 示 被 可 视 化 。
图 4.4 具 有 B 子 对 象 和 A 子 对 象 的 类 C
给 定 一 个 类 C 的 实 例 , 有 一 个 B 子 对 象 和 一 个 A 子 对 象 。 包 括 A 和 B 子 对 象 的 C 的 实 例 是 “ 完 整 的 对 象 ”。
使 用 运 行 类 型 信 息 , 可 以 检 查 一 个 指 针 是 否 真 的 指 向 一 个 完 整 对 象 , 而 且 是 否 可以 安 全 地 造 型 指 向 其 层 次 中 另 一 个 对 象 。 dynamic_cast 运 算 符 可 用 于 完 成 这 些
类 型 的 造 型 转 换 , 它 还 执 行 必 要 的 运 行 检 查 以 便 使 操 作 安 全 。
造 型 运 算 符
有 几 个 造 型 运 算 符 是 C++ 语 言 特 定 的 。 这 些 运 算 符 旨 在 除 去 旧 的 C 语 言 造 型 形式 中 模 糊 性 和 危 险 的 继 承 。 这 些 运 算 符 是 :
-
dynamic_cast 用 于 多 态 类 型 的 转 换
-
static_cast 用 于 非 多 态 类 型 的 转 换
-
const_cast 用 于 除 去 const , volatile 和 __ unaligned 属性
-
reinterpret_cast 用 于 位 的 简 单 再 解 释
使 用 const_cast 和 reinterpret_cast 作 为 一 种 最 后 的 手 段 , 因 为 这 些 运 算 符 与旧 的 造 型 形 式 有 相 同 的 危 险 性 。 但 是 它 们 还 是 有 必 要 用 于 完 全 取 代 旧 的 造 型 形式 。
dynamic_cast 运 算 符
表 达 式 dynamic_cast< type_id >(expression) 将 expression 操 作 数 转 换 为 一 个type-id 类 型 的 对 象 , type-id 必 须 为 以 前 定 义 的 类 类 型 或 “ void 指 针 ” 的 一 个 指针 或 一 个 引 用 。 如 果 type-id 是 一 个 指 针 , 则 expression 的 类 型 必 须 为 一 个 指 针 或如 果 type-id 是 一 个 引 用 , 则 它 为 一 个 l 值 。
语 法
dynamic_cast< type_id >( expression )
如 果 type-id 是 一 个 指 向 expression 的 非 模 糊 可 访 问 的 直 接 或 间 接 基 类 的 指 针 , 则
结 果 是 type-id 类 型 的 唯 一 子 对 象 的 一 个 指 针 , 例 如 :
class B{...};
class C:public B {...};
class D:public C {...};
void f(D* pd)
{
C* pc=dynamic_cast<C*>(pd); // 可 行 :C 是 一 个 直 接 基 类 ,pc 指 向 pd 的 C 子 对 象
B* pb=dynamic_cast<B*>(pd); // 可 行 :B 是 一 个 间 接 基 类 ,pd 指 向 pd 的 C 子 对 象
...
}
这 种 类 型 转 换 被 称 为 向 上 造 型 , 因 为 它 使 指 针 按 类 层 次 向 上 移 , 从 一 个 派 生 的 类到 一 个 派 生 源 类 。 向 上 造 型 是 一 种 隐 式 转 换 。
如 果 type-id 是 void*, 则 执 行 一 种 运 行 检 查 以 确 定 expression 的 实 际 类 型 , 结 果是 由 expression 指 向 的 完 整 对 象 的 一 个 指 针 。 例 如 :
class A{...};
class B{...}; void f()
{
A* pa=new A;
B* pb=new B;
void* pv=dynamic_cast<void*>(pa);
//pv 现 在 指 向 类 型 A 的 一 个 对 象
...
pv=dynamic_cast<void*>(pb);
//pv 现 在 指 向 类 型 B 的 一 个 对 象
}
如 果 type-id 不 是 void*, 则 执 行 运 行 检 查 去 查 看 由 expression 指 向 的 对 象 是 否 可转 换 为 type-id 指 向 的 类 型 。
如 果 expression 的 类 型 是 type-id 类 型 的 一 个 基 类 , 则 执 行 运 行 检 查 去 查 看expression 是 否 确 实 指 向 type-id 类 型 的 一 个 完 整 对 象 , 如 果 是 真 的 , 则 结 果 是type-id 类 型 的 一 个 完 整 对 象 的 指 针 。 例 如 :
class B{...};
class D : public B{...};
void f()
{
B* pb=new D;// 不 清 楚 但 是 可 行
B* pb2=new B;
D* pd=dynamic_cast<D*>(pb); // 可 行 :pb 确 实 指 向 一 个 D
...
D* pd2=dynamic_cast<D*>(pb2); // 错 误 :pb2 指 向 一 个 B, 而 不 是 一 个D,pd2==NULL
...
}
这 种 类 型 转 换 被 称 为 向 下 造 型 , 因 为 它 使 指 针 按 类 层 次 向 下 移 , 从 一 个 给 定 类 到一 个 从 它 派 生 出 的 类 。
在 多 重 继 承 的 情 况 下 , 模 糊 性 的 可 能 性 被 引 入 。 可 虑 图 4.5 中 给 出 的 类 层 次 :
图 4.5 类 层 次 显 示 多 重 继 承
类 型 D 的 一 个 对 象 的 指 针 可 被 安 全 地 造 型 到 B 或 C 。 但 是 如 果 D 被 造 型 到 指 向一 个 A 对 象 , 结 果 将 是 A 的 哪 个 实 例 呢 ? 这 将 导 致 一 个 模 糊 造 型 错 误 。 为 了 克 服这 个 问 题 , 你 可 以 执 行 两 个 非 模 糊 的 造 型 。 例 如 :
void f()
{
D* pd=new D;
A* pa=dynamic_cast<A*>(pd); // 错 误 : 模 糊 的
B* pb=dynamic_cast<B*>(pd); // 首 行 造 型 到 B
A* pa2=dynamic_cast<A*>(pb); // 可 行 : 非 模 糊 的
}
当 你 使 用 虚 拟 基 类 时 还 会 引 入 更 进 一 步 的 模 糊 性 。 考 虑 图 4.6 所 示 的 类 层 次 :
图 4.6 类 层 次 显 示 虚 拟 基 类
在 此 层 次 中 ,A 是 一 个 虚 拟 基 类 , 关 于 一 个 虚 拟 基 类 的 定 义 见 第 9 章 “ 派 生 类 ” 中 的 “ 虚 拟 基 类 ”。 给 定 E 类 的 一 个 实 例 及 指 向 A 子 对 象 的 一 个 指 针 , 转 到 指 向B 的 指 针 的 dynamic_cast 将 因 为 模 糊 性 而 失 败 。 你 必 须 先 造 型 回 到 完 整 的 E 对象 , 然 后 沿 层 次 向 上 , 以 一 种 非 模 糊 的 方 式 到 达 正 确 的 B 对 象 。
考 虑 图 4.7 所 示 的 类 层 次 。
图 4.7 类 层 次 显 示 重 复 基 类
给 定 类 型 E 的 一 个 对 象 和 D 子 对 象 的 一 个 指 针 , 从 D 子 对 象 导 航 到 最 左 边 的 A 子对 象 , 可 有 三 种 转 换 。 你 可 以 执 行 一 个 dynamic_cast 转 换 从 D 指 针 转 到 E 指 针 , 然 后 一 个 转 换 ( 为 dynamic_cast 或 隐 式 转 换 ) 从 E 到 B, 最 后 一 个 隐 式 转 换 从 B 到 A 。 例 如 :
void f(D* pd)
{
E * p e =dynamic_cast<B*>(pd);
B* pb = pe // 向 上 造 型 , 隐 式 转 换
A* pa=pb; // 向 上 造 型 , 隐 式 转 换
}
dynamic_cast 运 算 符 还 可 以 用 于 执 行 “ 跨 越 造 型 ”。 使 用 相 同 的 类 层 次 , 例 如 可以 造 型 一 个 指 针 从 B 子 对 象 到 D 子 对 象 , 只 要 完 整 的 对 象 是 E 类 型 的 。
考 虑 跨 越 造 型 , 确 实 有 可 能 仅 在 两 步 内 做 转 换 从 D 指 针 到 最 左 边 的 A 子 对 象 的 指针 。 你 可 以 从 D 到 B 造 型 转 换 , 然 后 一 个 隐 式 转 换 从 B 到 A 。 例 如 :
void f(D* pd)
{
B* pb=dynamic_cast<B*>( p d); // 跨 越 造 型 转 换
A* pa=pb;// 向 上 造 型 , 隐 式 转 换
}
- 个 空 指 针 值 被 dynamic_cast 转 换 为 目 标 类 型 的 空 指 针 值 。
当 你 使 用 dynamic_cast< type_id >( expression ) 时 , 如 果 expression 不 能 被 完 全 地转 换 到 type_id 类型 , 则 运 行 检 查 会 导 致 造 型 转 换 失 败 。 例 如 :
class A{...};
class B{...}; void f()
{
A* pb=new A;
B* pb=dynamic_cast<B*>(pa); // 失 败 , 不 安 全 ;B 不 是 从 A 派 生 来 的
...
}
到 指 针 类 型 的 一 个 失 败 的 造 型 转 换 的 值 是 空 指 针 。 到 引 用 类 型 的 失 败 的 造 型 转换 丢 弃 了 一 个 bad_cast 异 常 。
bad_cast 异 常
dynamic_cast 运 算 符 丢 弃 了 一 个 bad_cast 异 常 , 作 为 到 引 用 类 型 的 一 个 失 败 的
造 型 的 值 ,bad_cast 的 接 口 为 :
class bad_cast:public logic { public:
bad_cast(const __exString& what_arg): logic(what_ary) {}
void raise() {handle_raise(); throw *this;}
// 虚 拟 的 __exString what() const; // 继 承 的
};
static_cast 运 算 符
static_cast< type_id >( expression ) 表 达 式 将 expression 转 换 到 独 立 地 基 于 表 达 式中 给 出 的 类 型 type_id 类 型 。 没 有 运 行 类 型 检 查 被 执 行 以 确 保 转 换 的 安 全 性 。语 法
static_cast< type_id >( expression )
static_cast 运 算 符 可 被 用 在 例 如 转 换 一 个 基 类 指 针 到 一 个 派 生 类 指 针 的 操 作 , 这 样 的 转 换 不 总 是 安 全 的 , 例如 :
class B{...};
class D:public B{...}; void f(B* pb,D* pd)
{
D* pd2=static_cast<D*>(pb); // 不 安 全 ,pb 可 能 只 指 向 B
B* pb2=static_cast<B*>(pd); // 安 全 转 换
...
}
与 dynamic_cast 相 反 , 在 pb 的 static_cast 转 换 上 没 有 作 任 何 运 行 检 查 。 由 pb 指 向 的 对 象 可 能 不 是 D 类 型 的 对 象 , 使 用 *pd2 的 情 况 可 能 是 灾 难 性 的 。 例 如 , 调用 一 个 函 数 是 D 类 的 一 个 成 员 但 不 是 B 类 的 , 可 能 导 致 访 问 冲 突 。
dynamic_cast 和 static_cast 运 算 符 可 在 一 个 类 层 次 中 移 动 一 个 指 针 , 但static_cast 仅 依 赖 于 造 型 转 换 语 句 中 提 供 的 信 息 , 因 此 是 不 安 全 的 。 例 如 : class B{...};
class D:public B{...};
void f(B* pb)
{
D* pd1=dynamic_cast<D*>(pb);
D* pd2=static_cast<D*>(pb);
}
如 果 pb 真 的 指 向 类 型 D 的 一 个 对 象 , 则 pd1 和 pd2 将 得 到 相 同 的 结 果 。如 果 pd==0, 则 它 们 也 将 得 到 相 同 的 结 果 。
如 果 pb 指 向 类 型 B 的 一 个 对 象 , 但 不 是 指 向 完 整 的 D 类 , 则 dynamic_cast 将 足以 知 道 返 回 0 。 但 是 static_cast 依 赖 于 程 序 员 的 坚 持 让 pb 指 向 类 型 D 的 一 个对 象 而 且 简 单 地 返 回 那 个 假 定 的 D 对 象 的 指 针 。
因 此 ,static_cast 可 以 实 现 相 反 的 隐 式 转 换 , 在 这 种 情 况 下 , 结 果 是 未 定 义 的 , 留 给 程 序 员 去 确 保 static_cast 转 换 的 结 果 为 安 全 的 。
这 些 动 作 还 适 用 于 类 类 型 以 外 的 其 它 类 型 。 例 如 ,static_cast 可 被 用 于 从 int 到 char 的 转 换 。 但 结 果 字 符 可 能 没 有 足 够 的 位 数 保 持 整 个 整 型 值 , 又 一 次 留 给程 序 员 去 确 保 static_cast 转 换 的 结 果 为 安 全 的 。
static_cast 运 算 符 还 可 被 用 于 执 行 任 何 隐 式 转 换 , 包 括 标 准 转 换 和 用 户 定 义 的转 换 。 例 如 :
typedef unsigned char BYTE
void f()
{
char ch;
int i=65;
float f=2.5;
double db1;
ch=static_cast<char>(i); // 从 int 到 char
db1=static_cast<double>(f); // 从 float 到 double
...
i=static_cast<BYTE>(ch);
...
}
static_cast 运 算 符 可 显 式 地 将 一 个 整 型 值 转 换 到 一 个 枚 举 类 型 。 如 果 整 型 值未 落 在 枚 举 值 的 范 围 内 , 枚 举 结 果 值 是 不 确 定 的 。
static_cast 运 算 符 将 一 个 空 指 针 值 转 换 到 目 标 类 型 的 空 指 针 值 。
任 何 表 达 式 可 用 static_cast 运 算 符 显 式 地 转 换 到 void 类 型 , 目 标 void 类 型 可有 选 择 地 包 括 const 、 volatile 或 __unaligned 属 性 。
static_cast 运 算 符 不 能 造 型 除 去 const 、 volatile 或 _ _unaligned 属 性 。 有 关除 去 这 些 属 性 的 信 息 参 见 “ const_cast 运 算 符 ”。
const_cast 运 算 符
const_cast 运 算 符 可 被 用 于 从 一 个 类 中 除 去 const 、 volatile 和 _ _ unaligned 属 性 。
语 法
const_cast< type_id >( expression)
任 何 对 象 类 型 的 指 针 或 一 个 数 据 成 员 的 指 针 可 被 显 式 地 转 换 到 完 全 相 同 的 类 型 , 带 const , volatile 和 __unaligned 限 定 符 除 外 。 对 指 针 和 引 用 , 结 果 将 指 向 源对 象 , 对 数 据 成 员 指 针 结 果 和 数 据 成 员 的 源 ( 非 造 型 ) 指 针 一 样 指 向 同 一 成 员 。 由于 依 赖 于 引 用 对 象 的 类 型 , 通 过 指 针 、 引 用 或 数 据 成 员 指 针 的 求 结 果 的 写 操 作 将产 生 未 定 义 的 动 作 。
const_cast 运 算 符 将 一 个 空 指 针 值 转 换 为 目 标 类 型 的 空 指 针 值 。
reinterpret_cast 运 算 符
reinterpret_cast 运 算 符 允 许 任 何 指 针 被 转 换 到 任 何 其 它 指 针 类 型 , 它 还 允 许
任 何 整 型 转 换 到 任 意 指 针 类 型 , 且 反 之 亦 然 。 滥 用 reinterpret_cast 运 算 符 可轻 易 导 致 不 安 全 , 除 非 是 所 期 望 的 转 换 是 固 有 的 低 等 级 , 否 则 你 应 使 用 其 它 造 型运 算 符 之 一 。
语 法
reinterpret_cast<type_id>(expression)
reinterpret_cast 运 算 符 可 被 用 于 像 char* 到 int* 或 One_class* 到 Unrelated* 这 种 内 部 的 非 安 全 的 转 换 。
reinterpret_cast 的 结 果 除 了 用 于 造 型 回 到 其 源 类 型 外 , 不 能 安 全 地 用 于 其 它任 何 情 况 , 其 它 用 法 至 多 也 是 不 可 移 植 的 。
reinterpret_cast 运 算 符 不 能 造 型 除 去 const 、 volatile 或 __unaligned 属 性 , 关 于 除 去 这 些 属 性 的 信 息 参 见 const_cast 运 算 符 。
reinterpret_cast 运 算 符 将 一 个 空 指 针 值 转 换 到 目 标 类 型 的 空 指 针 值 。
运 行 类 型 信 息
运 行 类 型 信 息 (RTTI) 是 一 种 机 制 , 允 许 对 象 的 类 型 在 程 序 执 行 期 间 被 确 定 。 RTTI 加 到 C++ 语 言 中 , 因 为 很 多 类 库 销 售 者 正 自 己 实 现 这 个 功 能 , 这 导 致 库 之 间 的 不兼 容 。 因 此 , 很 明 显 需 要 在 语 言 级 支 持 运 行 类 型 信 息 。
为 了 明 确 性 ,RTTI 的 这 个 讨 论 几 乎 完 全 限 制 于 指 针 。 但 是 讨 论 的 概 念 也 适 用 于引 用 。
运 行 类 型 信 息 有 三 个 主 要 的 C++ 语 言 元 素 :
-
dynamic_cast 运 算 符 , 用 于 多 态 类 型 的 转 换 。 更 多 的 信 息 参 见 本 章前 面 部 分 “ dynamic_cast 运 算 符 ”。
-
typeid 运 算 符 , 用 于 指 定 一 个 对 象 的 确 切 类 型 。
-
type_info 类 , 用 于 保 持 由 typeid 运 算 符 返 回 的 类 型 信 息 。
typeid 运 算 符
typeid 运 算 符 允 许 一 个 对 象 的 类 型 在 运 行 时 被 确 定 。
语 法
typeid( type_id ) typeid(expression)
- 个 typeid 表 达 式 的 结 果 是 一 个 常 量 type_info& 。 其 值 是 一 个 type_info 对象 的 引 用 。 对 象 表 示 type_id 或 expression 的 类 型 , 这 依 赖 于 typeid 使 用 的 是哪 种 形 式 。 更 多 的 信 息 参 见 本 章 后 面 “ type_info 类 ”。
typeid 运 算 符 在 被 用 于 一 个 多 态 类 类 型 的 l 值 时 做 运 行 检 查 , 对 象 的 真 正 类 型不 能 由 所 提 供 的 静 态 信 息 确 定 。 这 些 情 况 有 :
-
一 个 类 的 引 用
-
一 个 指 针 , 用 * 间 接 引 用
-
一 个 下 标 指 针 ( 如 [])( 注 意 用 带 多 态 类 型 的 指 针 的 下 标 通 常 是 不 安全 的 )
如 果 expression 指 向 一 个 基 类 类 型 , 且 对 象 实 际 上 是 从 基 类 派 生 的 类 型 , 则 结 果是 派 生 类 的 type_info 引 用 。 expression 必 须 指 向 一 个 多 态 类 型 , 即 带 虚 拟 函 数的 类 。 否 则 , 结 果 是 在 expression 中 指 出 的 静 态 类 的 type_info 。 而 且 , 指 针 必 须为 间 接 引 用 的 , 因 此 它 所 指 向 的 对 象 被 使 用 。 没 有 间 接 引 用 指 针 , 则 结 果 将 是 指针 的 type_info, 而 不 是 它 所 指 向 的 内 容 。 例 如 :
class Base{...};
class Derived:public Base{...}; void f()
{
Derived* pd=new Derived;
Base* pb=pd;
...
const type_info& t=typeid(pb); //t 保 持 type_info 指 针
const type_info& t1=typeid(*pb); //t1 保 持 派 生 的 info
...
}
如 果 expression 间 接 引 用 一 个 指 针 , 而 且 那 个 指 针 的 值 为 0,typeid 丢 弃 一 个bad_typeid 异 常 。 如 果 指 针 不 是 指 向 一 个 有 效 的 对 象 , 异 常 __non_rtti_object 被 丢 弃 。
如 果 expression 既 不 是 对 象 的 基 类 的 一 个 指 针 也 不 是 一 个 引 用 , 则 结 果 是 表 示
expression 的 静 态 类 型 的 一 个 type_info 引 用 。
bad_typeid 异 常
在 有 些 情 况 下 ,typeid 运 算 符 丢 弃 一 个 bad_typeid 异 常 ,bad_typeid 的 接 口 为 : class bad_typeid:public logic{
public:
bad_typeid(c o nst char * what_arg):logic(what_arg) {}
void raise() { handle_raise(); throw * this; }
// 虚 拟 的 __exString what() const;// 继 承 的
};
更 多 的 信 息 参 见 “ typeid 运 算 符 ”。
type_info 类
type_info 类 描 述 由 编 译 器 在 程 序 内 产 生 的 类 型 信 息 , 该 类 的 对 象 有 效 地 存 储 一个 指 向 类 的 名 称 的 指 针 ,type_info 类 还 存 储 适 合 于 比 较 两 个 类 型 的 相 等 性 或 整理 顺 序 的 一 个 编 码 值 。 类 型 的 编 码 规 则 和 整 理 顺 序 是 未 指 定 的 , 而 且 在 不 同 程 序间 可 以 有 所 不 同 。
要 使 用 type_info 类 必 须 包 含 typeinfo.h 头 文 件 。class type_info {
public:
virtual ~ type_info();
int operator==(const type_info& rhs) const;
int operator!=(const type_info& rhs) const;
int before(const type_info& rhs) const;
const char* name() const;
const char* raw_name() const; private:
...
};
== 和 != 运 算 符 可 分 别 用 于 比 较 与 其 它 type_info 对 象 是 相 等 还 是 不 相 等 的 。
在 类 型 的 整 理 顺 序 和 继 承 性 关 系 之 间 没 有 联 系 。在 成 员 函 数 之 前 使 用 type_info: 去 指 定 类 型 的 整 理 顺 序 。 不 保 证 前 面 的 type_info:: 在 不 同 的 程 序 或 即 使 是 同一 程 序 的 不 同 运 行 中 产 生 相 同 的 结 果 。 在 这 种 况 下 , 前 面 的 type_info:: 类 似 于取 地 址 (&) 运 算 符 。
type_info::name 成 员 函 数 为 代 表 类 型 的 、 人 可 读 的 名 称 的 0 终 止 字 符 串 返 回一 个 char* 常 量 。 被 指 向 的 存 储 器 是 被 高 速 缓 存 的 , 而 且 永 远 不 应 该 被 直 接 撤 消分 配 。
type_inf::raw_name 成 员 函 数 为 表 示 对 象 类 型 装 饰 名 的 0 终 止 字 符 串 返 回 一 个char* 常 量 。 名 称 实 际 上 被 存 在 其 装 饰 格 式 内 , 以 节 省 空 间 。 因 而 , 此 函 数 比type_info::name 速 度 快 , 因 为 它 不 需 要 非 装 饰 名 称 。 由 type_info::raw_name 函数 返 回 的 字 符 串 在 比 较 操 作 中 很 有 用 , 但 却 不 是 可 读 的 。 如 果 你 需 要 一 个 人 可 读的 字 符 串 , 则 用 type_info::name 函 数 。
只 有 指 定 /GR( 使 能 运 行 类 型 信 息 ) 编 译 器 选 项 时 产 生 多 态 类 的 类 型 信 息 。
第 5 章 语 句
C++ 语 句 是 程 序 元 素 , 用 于 控 制 对 象 是 如 何 以 及 以 怎 样 的 顺 序 操 作 的 。 本 章 包 括 :
-
概 述
-
标 号 语 句
-
表 达 式 语 句 , 这 些 语 句 为 一 个 表 达 式 的 副 作 用 或 其 返 回 值 而 求 值 。
-
空 语 句 , 这 些 语 句 可 被 提 供 于 C++ 语 法 要 求 有 语 句 的 , 但 不 执 行 任 何动 作 的 地 方 。
-
复 合 语 句 , 这 些 语 句 是 包 括 于 花 括 号 ({}) 中 的 语 句 组 。 它 们 可 被 用于 语 法 中 任 何 需 要 单 个 语 句 的 地 方 。
-
选 择 语 句 , 这 些 语 句 执 行 一 个 测 试 , 如 果 测 试 为 真 ( 非 0), 则 执 行 某 部分 代 码 ; 如 果 测 试 为 假 , 则 可 执 行 另 一 部 分 代 码 。
-
迭 代 语 句 , 这 些 语 句 反 复 执 行 某 一 块 代 码 , 直 到 一 个 指 定 的 终 止 条 件被 满 足 为 止 。
-
跳 转 语 句 , 这 些 语 句 或 者 将 控 制 立 即 转 给 函 数 中 的 另 一 个 地 方 或 者从 函 数 返 回 控 制 。
-
说 明 语 句 , 说 明 向 一 个 程 序 中 引 入 名 称 ( 第 6 章 “ 说 明 ” 对 说 明 提 供更 详 细 的 信 息 ) 。
-
异 常 处 理 语 句 , 包括 C++ 异 常 处 理 (try 、 throw 或 catch) 和 结 构 异 常处 理 ( __ try/ _ _ except 、 _ _ try/ _ _finally) 。 try _ except 语 句 提 供 一
个 方 法 在 正 常 终 止 执 行 的 事 件 出 现 时 去 获 取 一 个 程 序 的 控 制权 ;try _ finally 和 levave 语 句 提 供 一 个 方 法 , 在 一 块 代 码 的 执 行 被中 断 时 以 保 证 清 除 代 码 的 执 行 。
语 句 概 述
C++ 语 句 顺 序 地 执 行 , 除 非 表 达 式 语 句 、 选 择 语 句 、 迭 代 语 句 或 跳 转 语 句 特 别 修改 了 那 个 顺 序 。
语 法
语 句 :
标 号 语 句
表 达 式 语 句
复 合 语 句
选 择 语 句
迭 代 语 句
跳 转 语 句
说 明 语 句
try-throw-catch
在 大 多 数 情 况 下 ,C++ 语 句 的 语 法 与 ANSI C 是 完 全 相 同 的 , 两 者 之 间 基 本 的 不 同点 是 : 在 C 中 , 说 明 仅 在 块 的 开 始 被 允 许 ;C++ 添 加 了 declaration-statement, 有 效 地除 去 了 这 个 限 制 。 这 使 你 可 以 在 一 个 用 于 预 计 算 初 始 化 值 的 程 序 中 引 入 变 量 。在 块 内 说 明 变 量 还 允 许 你 对 这 些 变 量 的 范 围 和 生 存 期 施 以 准 确 的 控 制 。
标 号 语 句
标 号 语 句 为 将 程 序 的 控 制 直 接 转 给 一 个 给 定 的 语 句 , 该 语 句 必 须 被 标 号 , 参 见 下节 “ 使 用 带 goto 语 句 的 标 号 ” 和 “ 在 case 语 句 中 使 用 标 号 ”。
语 法
标 号 语 句 :
标 识 符 : 语 句
cast 常 量 表 达 式 : 语 句
default: 语 句
使 用 带 goto 语 句 的 标 号
在 源 程 序 中 一 个 标 识 符 标 号 的 出 现 说 明 了 一 个 标 号 , 只 有 一 条 goto 语 句 可 以 将控 制 转 给 一 个 标 识 符 标 号 。 以 下 代 码 段 给 出 了 使 用 goto 语 句 和 一 个 标 识 符 标 号从 一 个 紧 密 嵌 套 的 循 环 中 跳 出 的 情 况 :
for(p=0;p<NUM_PATHS;++p)
{
NumFiles=Fil l Array(pFileArray,pszFNames);
for(i=0;i<NumFiles;++i)
{
if ((pFileArray[i]=fopen(pszFNames[i],"r"))==NULL)
goto FileOpenError;
// 处 理 被 打 开 的 文 件
}
}
FileOpenError:
cerr << "Fatal file open error,Processing interrupted.\n");
在 上 面 例 子 中 , 如 果 出 现 了 一 个 不 知 道 的 文 件 打 开 错 误 , 则 goto 语 句 直 接 将 控 制转 到 打 印 错 误 信 息 的 语 句 。
一 个 标 号 不 能 自 己 独 立 出 现 , 而 必 须 总 是 附 于 一 条 语 句 上 。 如 果 一 个 标 号 被 它 自身 需 要 , 则 在 标 号 后 放 一 条 空 语 句 。
标 号 具 有 函 数 范 围 , 不 能 在 其 函 数 内 被 再 说 明 。 但 是 相 同 的 名 称 可 在 不 同 的 函 数里 用 作 标 号 。
在 case 语 句 中 使 用 标 号
在 case 关 键 字 后 出 现 的 标 号 不 能 在 switch 语 句 外 面 再 出 现 ( 此 限 制 还 适 用 于default 关 键 字 ) 。 以 下 代 码 段 给 出 了 case 标 号 的 正 确 使 用 :
//Microsoft 窗 口 信 息 处 理 循 环 样 本switch(msg)
{
case WM_TIMER:// 处 理 时 间 事 件
SetClassWord(hWnd,GCW_HICON,ahIcon[nIcon++]);
ShowWindow(hWnd,SW_SHOWNA);
nIcon %=14;
Yield();
break; case WM_PAINT:
// 获 取 设 备 上 下 文 的 一 个 句 柄
//BeginPaint 在 合 适 情 况 发 出 WM_ERASEBKGND
memset(&ps,0x00,sizeof(PAINTSTRUCT));
hDC=BeginPaint(hWnd,&ps);
// 通 知 窗 口 绘 制 已 完 成
EndPaint(hWnd,&ps);
break;
case WM_CLOSE:
// 关 闭 此 窗 口 和 所 有 子 窗 口
killTimer(hWnd,TIMER1);
DestroyWindow(hWnd);
if (hWnd==hWndMain)
PostQuitMessage(0); // 退 出 应 用
break;
default:
// 没 有 被 case 语 句 特 别 包 含 的 所 有 信 息 采 用 此 选 项
return DefWindowProc(hWnd,Message,wParam,lParam);
break;
}
表 达 式 语 句
表 达 式 语 句 使 表 达 式 被 求 值 。 作 为 一 条 表 达 式 语 句 的 结 果 没 有 控 制 的 转 移 或 循环 的 发 生 。
语 法
表 达 式 语 句 :
表 达 式 opt;
一 条 表 达 式 语 句 中 的 所 有 表 达 式 在 执 行 下 一 条 语 句 前 被 求 值 。 且 所 有 副 作 用 都完 成 。 最 普 通 的 表 达 式 语 句 是 赋 值 和 函 数 调 用 。 C++ 还 提 供 一 空 语 句 。
空 语 句
“ 空 语 句 ” 是 一 条 没 有 表 达 式 的 表 达 式 语 句 , 它 在 语 言 的 语 法 要 求 一 条 语 句 但 没
有 表 达 式 求 值 时 是 很 有 用 的 , 它 由 一 个 分 号 组 成 。
空 语 句 通 常 被 用 作 循 环 语 句 中 的 占 位 员 或 在 复 合 语 句 或 函 数 的 末 尾 放 置 标 号 的语 句 。
以 下 代 码 段 显 示 了 结 合 空 语 句 如 何 将 一 个 字 符 串 拷 贝 到 另 一 个 : char *strcpy(char *Dest,const char *Source)
{
char *DestStart=Dest;
// 将 由 Source 所 指 的 值 赋 给 Dest, 直 到 遇 到 字 符 串 的 结 束 符
while(*Dest++=*Source++)
; // 空 语 句
return DestStart;
}
复 合 语 句 ( 块 )
复 合 语 句 由 括 在 花 括 号 ({}) 中 的 0 个 或 多 个 语 句 组 成 , 一 个 复 合 语 句 可 被 用 于 任何 需 要 语 句 的 地 方 , 复 合 语 句 通 常 被 称 为 “ 块 ”。
语 法
复 合 语 句 :
{ 语 句 表 opt} 语 句 表 :
语句
语 句 表 语 句
以 下 例 子 使 用 一 个 复 合 语 句 作 为 if 语 句 的 语 句 部 分 ( 有 关 if 语 句 的 详 细 语 法 见“ if 语 句 ” ):
if (Amount>100)
{
cout << "Amount was too large to handle\n";
Alert();
}
else
Balance-=Amount;
注 意 : 因 为 一 个 说 明 是 一 条 语 句 , 所 以 一 个 说 明 可 以 是 语 句 表 中 语 句 之 一 。 因 此 , 在 一 个 复 合 语 句 内 说 明 的 , 但 没 有 显 式 地 说 明 为 static 的 名 称 , 具 有 局 部 范 围 和( 对 对 象 而 言 ) 生 存 期 , 有 关 具 有 局 部 范 围 的 名 称 的 处 理 的 详 细 内 容 参 见 第 2 章“ 基 本 概 念 ” 中 的 “ 范 围 ”。
选 择 语 句
C++ 的 选 择 语 句 if 和 switch 提 供 一 种 方 法 有 选 择 地 执 行 部 分 代 码 。语法
选 择 语 句 :
if( 表 达 式 ) 语 句
if ( 表 达 式 ) 语 句 else 语 句
switch( 表 达 式 ) 语 句
if 语 句
if 语 句 对 括 号 内 的 表 达 式 求 值 , 表 达 式 必 须 为 算 术 的 或 指 针 类 型 ; 或 必 须 为 定 义了 到 一 个 算 术 的 或 指 针 类 型 的 非 模 糊 转 换 的 一 个 类 类 型 ( 有 关 转 换 见 第 3 章 “ 标准 转 换 ” ) 。
在 if 语 法 两 种 形 式 的 任 何 一 种 形 式 中 , 如 果 表 达 式 求 值 为 一 个 非 0 值 ( 真 ), 则 依赖 于 求 值 的 语 句 被 执 行 ; 否 则 , 被 跳 过 。
在 if...else 语 法 中 , 如 果 表 达 式 求 值 结 果 为 0, 则 第 二 条 语 句 被 执 行 。if...else 语 句 的 else 子 句 与 最 近 的 前 面 的 尚 未 与 else 语 句 匹 配 的 if 语 句 相匹 配 , 以 下 代 码 段 说 明 这 是 如 何 工 作 的 :
if (condition1==true)
if (condition2==true)
cout << "condition1 true; condition2 true\n"; else
cout << "condition1 true; condition2 false\n"; else
cout << "condition1 false\n";
很 多 程 序 员 使 用 花 括 号 ({}) 显 式 地 指 明 复 杂 的 if 和 else 子 句 的 匹 配 , 如 下 例 中所 示 :
if (condition1==true)
{
if (condition1==true)
cont << "condition1 true; condition2 true\n";
else
cont << "condition1 true; condition2 false\n";
}
else
cont << "condition1 false\n";
尽 管 花 括 号 不 是 严 格 必 须 的 , 但 它 们 指 明 了 if 和 else 语 句 之 间 的 配 对 关 系 。
switch 语 句
C++ 的 switch 语 句 允 许 在 多 个 代 码 节 中 作 选 择 , 选 择 依 赖 于 表 达 式 的 值 。 括 在 括号 中 的 表 达 式 即 “ 控 制 表 达 式 ” 必 须 为 一 个 整 型 或 一 个 类 类 型 且 非 模 糊 地 转 到整 型 。 整 型 提 升 如 第 3 章 “ 标 准 转 换 ” 中 “ 整 型 提 升 ” 所 描 述 的 被 执 行 。switch 语 句 导 致 一 个 非 条 件 的 跳 转 到 、 进 入 或 跳 过 为 “ 开 关 体 ” 的 语 句 , 这 依赖 于 控 制 表 达 式 的 值 , case 标 号 的 值 和 default 标 号 的 是 否 存 在 。 开 关 体 通 常是 一 个 复 合 语 句 ( 尽 管 这 不 是 语 法 上 的 要 求 ) 。 通 常 情 况 下 , 开 关 体 中 的 某 些 语 句用 case 标 号 或 用 default 标 号 标 记 。 标 记 语 句 不 是 语 法 上 必 须 的 , 但 没 有 它 们switch 语 句 就 毫 无 意 义 。 default 标 号 仅 能 出 现 一 次 。
语 法
case 常 量 表 达 式 : 语 句default: 语 句
在 case 标 号 中 的 常 量 表 达 式 被 转 换 到 控 制 表 达 式 的 类 型 , 然 后 比 较 相 等 性 。 在一 个 给 定 的 switch 语 句 中 , 在 case 语 句 中 的 常 量 表 达 式 不 能 有 两 次 求 值 为 相 同的 值 。 其 行 为 如 表 5.1 所 示 :
表 5.1 switch 语 句 行 为
条件 操作
被 转 换 的 值 与 被 提 升 的 控 制 表 达 式 相匹配
没有一个常量与 case 标号中的常量匹配存在 default 标号
没有一个常量与 case 标号中的常量匹配 , 不存在 default 标号
控制转到那个标号后的语句控制转到 default 标号
控制转到 switch 语句后面的语句
switch 语 句 的 一 个 内 部 块 可 以 包 含 带 初 始 化 的 定 义 , 只 要 它 们 是 可 到 达 的 , 即 不
会 被 所 有 可 能 的 执 行 路 径 绕 过 。 用 这 些 说 明 引 入 的 名 称 具 有 局 部 范 围 。 以 下 代码 段 显 示 了 switch 语 句 是 如 何 工 作 的 :
switch ( tolower(*argv[1]) )
{
// 错 误 , 不 可 到 达 的 说 明
char szChEntered[]="Character entered was:"; case'a':
{
//szChEntered 的 说 明 是 可 行 的 , 为 局 部 范 围
char szChEntered[]="Character entered was:";
cout << szChEntered << "a\n";
}
break;
case'b':
//szChEntered 的 值 未 定 义
cout << szChEntered << "b\n";
break;
default:
//szChEntered 的 值 未 定 义
cout << szChEntered << "neither a nor b\n";
break;
}
switch 语 句 可 被 嵌 套 。 在 这 种 情 况 中 ,case 或 default 标 号 与 包 含 它 们 的 最 深层 嵌 套 的 switch 语 句 相 对 应 。 例 如 :
switch(msg)
{
case WM_COMMAND:// 窗 口 命 令 , 查 找 更 多 的
switch(wParam)
{
case IDM_F_NEW: // 文 件 创 建 菜 单 命 令
delete wfile;
wfile=new WinAppFile;
break;
c ase IDM_F_OPEN: // 文 件 打 开 菜 单 命 令
wfile->FileOpenDlg();
break;
...
}
case WM_CREATE: // 创 建 窗 口
...
break;
case WM_PAINT:// 窗 口 需 要 重 画
...
break; default:
return DefWindowProc(hWnd, Message, wParam, lParam) ;
}
上 面 的 代 码 段 来 自 Microsoft Windows 消 息 循 环 , 它 说 明 了 switch 语 句 能 够 怎样 地 被 嵌 套 。 由 wParam 的 值 进 行 选 择 的 switch 语 句 仅 当 msg 为 WM_COMMAND 时被 执 行 。 作 为 菜 单 选 择 使 用 的 case 标 号 IDM_F_NEW 和 IDM_F_OPEN 与 里 层 的switch 语 句 相 对 应 。
控 制 并 没 有 被 case 或 default 标 号 阻 止 。 要 在 复 合 语 句 的 尾 部 结 束 执 行 , 则 插入 一 个 break 语 句 。 这 将 控 制 转 给 switch 语 句 后 面 的 语 句 。 此 例 说 明 控 制 是 如何 “ 向 下 通 过 ” 的 , 除 非 用 一 条 break 语句 :
BOOL fClosing=FALSE;
...
switch(wParam)
{
case IDM_F_CLOSE: // 文 件 关 闭 命 令
fClosing=TRUE;
// 向 下 通 过
case IDM_F_SAVE: // 文 件 保 存 命 令
if (document->IsDirty())
if (document->Name()=="UNTITLED")
FileSaveAs(document);
else
FileSave(document);
if (fClosing)
document->Close();
break;
}
上 面 的 代 码 说 明 了 如 何 利 用 case 标 号 不 妨 碍 控 制 流 程 的 事 实 。 如 果 switch 语句 将 控 制 转 给 IDM_F_SAVE, 则 fClosing 为 FALSE 。 因 此 , 在 文 件 被 存 储 后 , 该 文档 未 关 闭 。 但 是 如 果 switch 语 句 将 控 制 转 给 IDM_F_CLOSE, 则 fclosing 设 置 为真 值 , 保 存 文 件 的 代 码 被 执 行 。
迭 代 语 句
迭 代 语 句 使 得 语 句 ( 或 复 合 语 句 ) 按 照 某 些 循 环 终 止 条 件 执 行 0 次 或 多 次 。 当 这些 语 句 为 复 合 语 句 时 , 它 们 被 顺 序 地 执 行 , 除 非 遇 到 break 语 句 或 continue 语 句( 这 些 语 句 的 描 述 参 见 本 章 后 面 的 “ break 语 句 ” 和 “ continue 语 句 ” ) 。
C++ 提 供 三 种 迭 代 语 句 即 while 、 do 和 for 。 每 种 都 迭 代 到 其 终 止 表 达 式 求 值 为0( 假 ) , 或 直 到 用 一 个 break 语 句 强 迫 循 环 终 止 。 表 5.2 归 纳 了 这 些 语 句 和 它 们的 作 用 ; 每 一 种 都 在 随 后 的 章 节 中 被 详 细 地 讨 论 。
表 5.2 C++ 循 环 语 句
语句 |
求值位置 |
初始化 |
增量 |
---|---|---|---|
while |
循环顶 |
无 |
无 |
do |
循环底 |
无 |
无 |
for |
循环顶 |
有 |
有 |
语 法
循 环 语 句 :
while ( 表 达 式 ) 语 句
do 语 句 while ( 表 达 式 )
for (for- 初 始 语 句 表 达 式 opt ; 表 达 式 opt) 语 句for- 初 始 语 句
表 达 式 语 句
说 明 语 句
一 个 迭 代 语 句 的 语 句 部 分 不 能 是 一 个 说 明 , 但 可 以 是 一 个 复 合 语 句 , 包 含 一 个 说明 。
while 语 句
while 语 句 重 复 执 行 一 语 句 直 到 指 定 的 终 止 条 件 ( 表 达 式 ) 求 值 为 0 。 终 止 条 件 的测 试 在 循 环 每 次 执 行 之 前 进 行 ; 因 此 ,while 循 环 执 行 0 次 或 多 次 , 这 取 决 于 终 止表 达 式 的 值 。 以 下 代 码 使 用 while 循 环 剪 切 一 个 字 符 串 的 尾 空 格 :
char *trim(char *szSource)
{
char *pszEOS;
// 设 置 字 符 串 尾 部 指 针 指 向 字 符 串 尾 部 之 前 的 那 个 字 符
pszEOS = szSource + strlen(szSource) - 1;
while (pszEOS>=szSource && *pszEOS== ′ ′ )
*pszEOS--= ′ \0 ′ ;
return szSource;
}
终 止 条 件 在 循 环 的 顶 部 被 求 值 , 如 果 没 有 尾 部 空 格 , 循 环 则 永 不 执 行 。
表 达 式 必 须 为 一 整 型 、 指 针 类 型 或 带 有 一 个 非 模 糊 的 转 换 到 一 个 整 型 或 指 针 类型 的 一 个 类 类 型 。
do 语 句
do 语 句 重 复 执 行 一 个 语 句 直 到 指 定 的 终 止 条 件 ( 表 达 式 ) 求 值 为 0 。 终 止 条 件 的测 试 在 循 环 每 次 执 行 之 后 进 行 , 因 此 ,do 循 环 执 行 一 次 或 多 次 , 这 取 决 于 终 止 表达 式 的 值 。 以 下 函 数 使 用 do 语 句 去 等 待 用 户 按 下 一 个 指 定 键 :
void WaitKey(char ASCIICode)
{
char chTemp;
do
{
chTemp=_getch();
}
while(chTemp!=ASCIICode);
}
在 前 面 的 代 码 中 使 用 一 个 do 循 环 而 不 是 while 循 环 。 使 用 do 循环 ,_getch 函数 在 终 止 条 件 被 求 值 之 前 被 调 用 以 获 取 一 个 击 键 值 。 此 函 数 可 以 用 while 循 环编 写 , 但 没 有 那 么 简 洁 :
void WaitKey(char ASCIICode)
{
char chTemp;
chTemp=_getch();
while (chTemp!=ASCIICode)
{
chTemp=_getch();
}
}
表 达 式 必 须 为 一 整 型 、 指 针 类 型 或 带 有 一 个 非 模 糊 的 转 换 到 一 个 整 型 或 指 针 类型 的 一 个 类 类 型 。
for 语 句
for 语 句 可 被 分 成 三 个 独 立 的 部 分 , 如 表 5.3 所 示 。
表 5.3 for 循 环 元 素
语法名 何时被执行 内 容
for- 初 始 语句
在 for 语 句 或 子 语 句 的 任 何 其它元素之前
常 被 用 于 初 始 化 循 环 索 引 , 可包含表达式或说明
表达式 1 在 循 环 的 给 定 重 复 、 包 括 第 一
次重复
表达式 2 在 循 环 的 每 次 重 复 的 末 尾 、 表
达式 1 在 表 达 式 2 被求值后被测试
续 表一 个 表 达 式 求 值 为 一 个 整型 或 执 行 之 前 。 一 个 具 有到 整 型 的 非 模 糊 转 换 的 类类 型
通常用于增量循环索引
for 初 始 化 语 句 常 被 用 于 说 明 及 初 始 化 循 环 指 标 变 量 , 表 达 式 1 通 常 被 用 于 测 试
循 环 终 止 标 准 。 表 达 式 2 常 被 用 于 增 量 循 环 指 标 。
for 语 句 重 复 执 行 语 句 直 到 表 达 式 1 求 值 为 0,for- 初 始 化 语 句 、 表 达 式 1 和 表达 式 2 域 均 是 可 选 的 。
以 下 for 循 环 :
for (for-init-statement;expression1;expression2)
{
// 语 句
}
等 价 于 以 下 while 循 环 : for-init-statement; while (expression1)
{
// 语 句
expression2;
}
使 用 for 语 句 指 定 一 个 无 穷 循 环 的 方 便 用 法 为 :
for(;;)
{
// 待 执 行 的 语 句
}
这 等 价 于while(1)
{
// 待 执 行 的 语 句
}
for 循 环 的 初 始 化 部 分 可 以 是 一 个 说 明 语 句 或 一 个 表 达 式 语 句 , 包 括 空 语 句 。 初始 化 可 以 包 括 任 意 顺 序 的 表 达 式 和 说 明 , 用 逗 号 隔 开 。 在 for- 初 始 化 语 句 内 说明 的 任 何 对 象 具 有 局 部 范 围 , 就 好 象 它 在 for 语 句 前 刚 说 明 。 尽 管 对 象 的 名 称 可被 用 于 相 同 范 围 内 的 局 部 不 止 一 个 的 for 循 环 中 , 但 说 明 仅 能 出 现 一 次 , 例 如 : #include <iostream.h>
void main()
{
for (int i=0;i<100;++i)
cout << i << "\n";
// 循 环 指 标 i 不 能 在 这 里 的 for- 始 化 语 句 中 说 明 , 因 为 它 还 在 该 范 围 内
for (i=100;i>=0;--i)
cout << i << "\n";
}
尽 管 for 语 句 的 三 个 域 通 常 被 用 于 初 始 化 、 终 止 测 试 和 增 量 , 但 它 们 不 限 于 这 些用 法 。 例 如 , 以 下 代 码 打 印 从 1 到 100 的数 , 子 语 句 为 空 语 句 :
#include <iostream.h>
void main()
{
for (int i=0;i<100;cout << ++i << endl)
;
}
跳 转 语 句
C++ 跳 转 语 句 执 行 一 个 控 制 的 立 即 转 移 。
语 法
跳 转 语 句 :
break;
continue;
return 表 达 式 opt ;
goto 标 识 符 ;
break 语 句
break 语 句 被 用 于 退 出 一 个 循 环 或 switch 语 句 。 它 将 控 制 转 到 紧 跟 在 循 环 子 语句 或 switch 语 句 后 的 语 句 。
break 语 句 只 终 止 最 紧 密 包 含 它 的 循 环 或 switch 语 句 。 在 循 环 中 ,break 被 用 于在 终 止 条 件 等 于 0 之 前 终 止 ; 在 switch 语 句 中 ,break 语 句 用 于 通 常 在 一 个 case 标 号 之 前 终 止 代 码 段 。
以 下 例 子 说 明 在 for 循 环 中 break 语 句 的 用 法 :
for(; ;) // 没 有 终 止 条 件
{
if(List->AtEnd())
break;
list->Next();
}
cout << "Control transfers to here.\n";
注 意 : 还 有 其 它 简 单 的 方 法 退 出 循 环 。 在 更 复 杂 的 循 环 中 最 好 使 用 break 语 句 , 因 为 循 环 可 能 很 难 说 在 几 条 语 句 被 执 行 之 前 是 否 应 终 止 。
有 关 在 switch 语 句 体 内 使 用 break 语 句 的 例 子 见 本 章 中 前 面 的“ switch 语 句 ”。
continue 语句
continue 语 句 迫 使 控 制 立 即 转 到 最 小 包 含 循 环 的 循 环 继 续 语 句 。 ( “ 循 环 继 续 ” 是 包 含 循 环 控 制 表 达 式 的 语 句 ) 。 因 此 ,continue 语 句 仅 能 在 一 个 循 环 语 句 的 依赖 语 句 ( 尽 管 它 可 能 是 那 语 句 中 的 唯 一 的 语 句 ) 中 出 现 一 次 。 在 一 个 for 循 环中 ,continue 语 句 的 执 行 导 致 表 达 式 2 的 求 值 , 然 后 是 表 达 式 3 。
以 下 例 子 显 示 如 何 使 用 continue 语 句 绕 过 代 码 段 跳 到 循 环 的 下 一 次 迭 代 : #include <conio.h>
// 从 以 0 结 尾 的 字 符 串 szLegalString 获 取 一 个 字 符 。 返 回 输 入 字 符 的 下 标int GetLegalChar(char *szLegalString)
{
char *pch;
do
{
char ch=_getch();
// 使 用 strchr 库 函 数 确 定 读 入 的 字 符 是 否 在 字 符 串 中 , 如 果 不 在 , 就 用continue 语句
// 越 过 循 环 中 的 余 下 的 语 名
if (pch=strchr(szLegalString,ch))==NULL)
continue;
// 一 个 在 字 符 串 szLegalString 中 字 符 被 输 入 , 返 回 其 下 标
return(pch-szLegalString);
//continue 语 句 将 控 制 转 到 这 里
} while(1);
return 0;
}
return 语 句
return 语 句 允 许 一 个 函 数 立 即 将 控 制 转 回 到 调 用 函 数 ( 或 者 如 果 是 主 函 数 , 则 控制 转 到 操 作 系 统 ) 。 return 语 句 接 受 一 个 表 达 式 , 即 传 回 调 用 函 数 的 值 。 void 类型 函 数 、 构 造 函 数 和 析 构 函 数 不 能 在 return 语 句 中 指 定 表 达 式 ; 其 它 所 有 类 型的 函 数 都 必 须 在 return 语 句 中 指 定 一 个 表 达 式 。
如 果 指 定 有 , 则 表 达 式 被 转 换 到 函 数 说 明 中 指 定 的 类 型 , 如 同 执 行 一 个 初 始 化 。从 表 达 式 类 型 到 函 数 的 return 类 型 的 转 换 会 导 致 临 时 对 象 的 创 建 , 有 关 临 时 量怎 样 以 及 何 时 被 创 建 的 更 多 信 息 参 见 第 11 章“ 特 殊 成 员 函 数 ”中 的“ 临 时 对 象 ”。当 控 制 流 退 出 包 含 函 数 定 义 的 块 时 , 其 结 果 与 执 行 没 有 表 达 式 的 return 语 句 的结 果 是 一 样 的 。 对 于 作 为 返 回 值 说 明 的 函 数 这 是 非 法 的 。
- 个 函 数 可 有 任 意 数 目 的 return 语 句 。
goto 语 句
goto 语 句 将 控 制 无 条 件 地 转 到 命 名 的 标 号 处 , 标 号 必 须 在 当 前 函 数 中 。
有 关 标 号 和 goto 语 句 的 更 多 信 息 参 见 本 章 开 头 的“ 标 号 语 句 ” 和 “ 使 用 带 goto 语 句 的 标 号 ”。
说 明 语 句
说 明 向 当 前 范 围 引 入 新 的 名 称 , 这 些 名 称 可 以 是 :
-
类 型 名 (class 、 struct 、 union 、 enum 、 typedef 和 成 员 指 针 )
-
对 象 名 称
-
函 数 名 称
语 法
说 明 语 句 :
说明
如 果 在 一 个 块 内 的 说 明 引 入 一 个 已 经 在 块 的 外 部 说 明 的 名 称 , 则 在 块 持 续 期 间 前面 的 说 明 被 隐 藏 , 在 块 结 束 后 , 前 面 的 说 明 再 次 可 见 。
在 同 一 块 内 对 相 同 的 名 称 作 多 个 说 明 是 非 法 的 。
关 于 说 明 和 名 称 隐 藏 的 更 多 信 息 参 见 第 2 章 “ 基 本 概 念 ” 中 的 “ 说 明 与 定 义 ” 及 “ 范 围 ”。
自 动 对 象 的 说 明
在 C++ 中 , 对 象 可 用 a uto 或 register 关 键 字 说 明 为 自 动 存 储 类 , 如 果 一 个 局 部对 象 ( 在 函 数 内 部 说 明 的 对 象 ) 没 有 使 用 任 何 存 储 类 关 键 字 , 则 被 假 定 为 auto 。C++ 对 这 些 对 象 的 初 始 化 和 说 明 与 用 静 态 存 储 类 说 明 的 对 象 是 不 同 的 。
自 动 对 象 的 初 始 化
auto 或 register 存 储 类 的 对 象 的 说 明 语 句 每 次 被 执 行 时 , 则 进 行 初 始 化 。 来 自“ continue 语 句 ” 中 的 以 下 例 子 给 出 了 do 循 环 内 部 的 自 动 对 象 ch 的 初 始 化 : #include <conio.h>
// 从 以 空 格 结 尾 的 字 符 串 s z LegalString 获 取 一 个 字 符 。 返 回 输 入 字 符 的 指 标int GetLegalChar (char *szLegalString)
{
char *pch; do
{
// 循 环 每 次 执 行 时 , 此 说 明 语 句 被 执 行 一 次
char ch=_getch();
if ((pch=strchr(szLegalString, ch))==NULL)
continue;
// 一 个 在 字 符 串 szLegalString 中 的 字 符 被 输 入 , 返 回 其 指 标
return (pch-szLegalString);
} while(1);
}
对 循 环 的 每 次 重 复 ( 每 次 都 遇 到 说 明 ), 宏 _getch 被 求 值 , 且 ch 用 结 果 进 行 初 始化 。 当 用 return 语 句 将 控 制 转 到 块 外 部 时 ,ch 被 销 毁 ( 在 此 情 况 下 , 存 储 器 被 撤销 分 配 ) 。 有 关 初 始 化 的 另 一 个 例 子 参 见 第 2 章 “ 基 本 概 念 ” 中 的 “ 存 储 类 ”。
自 动 对 象 的 析 构
定 义 在 循 环 中 的 对 象 在 循 环 的 每 次 重 复 时 从 块 中 退 出 时 , 或 当 控 制 转 到 说 明 之 前的 一 点 时 , 被 销 毁 一 次 。 在 块 内 而 不 是 循 环 中 说 明 的 对 象 在 退 出 块 时 或 当 控 制 转到 说 明 之 前 的 一 点 时 被 销 毁 。
注 意 : 析 构 函 数 可 简 单 地 意 味 着 撤 销 对 象 的 分 配 或 对 类 类 型 对 象 调 用 对 象 的 析 构函 数 。
当 跳 转 语 句 将 控 制 转 到 循 环 或 块 外 时 , 从 其 转 出 的 块 内 说 明 的 对 象 被 销 毁 在 向 其转 入 的 块 内 的 对 象 没 有 被 销 毁 。 当 控 制 转 到 说 明 之 前 的 一 点 时 , 对 象 被 销 毁 。
控 制 的 转 移
你 可 以 用 goto 语 句 或 switch 语 句 中 的 case 标 号 指 定 一 个 程 序 转 移 越 过 一 个 初始 器 。 这 样 的 代 码 是 非 法 的 , 除 非 包 含 初 始 器 的 说 明 包 括 在 跳 转 语 句 出 现 的 块中 。
以 下 例 子 给 出 了 一 个 说 明 和 初 始 化 对 象 total 、 ch 和 i 的 循 环 , 还 有 一 条 错 误 的
goto 语 句 将 控 制 越 过 一 个 初 始 化 器 。
// 读 输 入 直 到 输 入 一 个 非 数 字 字 符while(1)
{
int total=0;
char ch=_getch();
if (ch>= ′ 0 ′ || ch<= ′ 9 ′ )
{
goto label1; // 错 误 : 移 动 越 过 了 i 的 初 始 化 器
int i=ch- ′ 0 ′ ; label1:
total+=i;
} // 如果 goto 错 误 未 出 现 ,i 将 在 此 处 被 销 毁
else
//Break 语 句 将 控 制 转 到 循 环 外 , 销 毁 total 和 ch
break;
}
在 前 面 的 例 子 中 ,goto 语 句 试 图 将 控 制 移 动 越 过 i 的 初 始 化 器 。 但 是 如 果 i 被说 明 但 未 被 初 始 化 , 则 移 动 将 是 合 法 的 。
在 为 while 语 句 的 语 句 部 分 服 务 的 块 内 说 明 的 total 和 ch 的 对 象 , 在 使 用 break 语 句 退 出 块 时 被 销 毁 。
静 态 对 象 的 说 明
一 个 对 象 可 以 用 static 或 extern 关 键 字 说 明 为 静 态 存 储 类 。 局 部 对 象 必 须 显式 地 说 明 为 static 或 extern 以 具 有 静 态 存 储 类 。 所 有 全 局 对 象 ( 在 所 有 函 数 外部 说 明 的 对 象 ) 具 有 静 态 存 储 类 , 你 不 能 在 微 模 式 程 序 中 说 明 静 态 实 例 。
静 态 对 象 的 初 始 化
全 局 对 象 在 程 序 开 始 处 初 始 化 ( 关 于 全 局 对 象 的 构 造 和 析 构 的 更 多 信 息 , 参 见 第2 章 “ 基 本 概 念 ” 中 的 “ 额 外 的 启 动 考 虑 ” 和 “ 额 外 的 结 束 考 虑 ” ) 。
说 明 为 static 的 局 部 对 象 在 程 序 流 程 中 第 一 次 遇 到 其 说 明 时 被 初 始 化 , 在 第 2 章 “ 基 本 概 念 ” 中 介 绍 的 以 下 类 显 示 了 这 是 如 何 工 作 的 :
#include <iostream.h> #include <string.h>
// 定 义 一 个 类 记 录 初 始 化 和 析 构class InitDemo
{
public:
InitDemo(char *szWhat);
~ InitDemo();
private:
char *szObjName;
}:
//*InitDemo 类 的 构 造 函 数InitDemo::InitDemo(char *szWhat)
{
if (szWhat!=0 && strlen(szWhat)>0)
{
szObjName=new char[strlen(szWhat)+1];
strcpy(szObjName,szWhat);
}
else
szObjName=0;
clog << "Initializing:" << szObjName << "\n";
}
//InitDemo 的 析 构 函 数InitDemo:: ~ InitDemo()
{
if (szObjName !=0)
{
clog << "Destroying: " << szObjName << "\n";
delete szObjName;
}
}
// 主 函 数
void main(int argc,char *argv[]);
{
if(argc<2)
{
cerr << "Supply a one-letter argument.\n");
return -1;
}
if(*argv[1]== ′ a ′ )
{
cout << "*argv[1] was an ′ a ′ \n";
// 说 明 静 态 局 部 对 象
static InitDemo I1("static I1");
}
else
cout << "*argv[1] was not an ′ a ′ \n";
}
如 果 提 供 给 此 程 序 的 命 令 行 参 量 以 小 写 字 母 “ a ” 开 头 , 则 I1 的 说 明 被 执 行 , 发生 初 始 化 , 结 果 为 :
*argv[1] was an 'a' Initializing: static I1 Destroying: static I1
否 则 , 控 制 流 绕 过 I1 的 说 明 , 结 果 为 :
*arg[1] was not an 'a'
当 一 个 静 态 局 部 对 象 用 一 个 不 等 于 常 量 表 达 式 的 初 始 化 器 说 明 时 , 该 对 象 在 第 一次 进 入 块 执 行 前 一 点 被 给 定 值 0( 转 换 到 合 适 的 类 型 ) 。 但 是 该 对 象 是 不 可 见 的 , 而 且 直 到 说 明 的 实 际 点 处 才 调 用 构 造 函 数 。
在 说 明 所 在 点 处 , 对 象 的 构 造 函 数 ( 如 果 对 象 是 一 个 类 类 型 的 ) 被 如 期 调 用 ( 静 态局 部 对 象 仅 在 它 们 第 一 次 被 看 见 时 初 始 化 ) 。
静 态 对 象 的 析 构
局 部 静 态 对 象 在 由 atexi t 指 定 的 终 止 期 间 被 销 毁 。
如 果 一 个 静 态 对 象 因 为 程 序 控 制 流 绕 过 了 其 说 明 而 未 被 构 造 , 则 不 要 试 图 去 销 毁那 个 对 象 。
异 常 处 理
Microsoft C++ 支 持 两 种 异 常 处 理 ,C++ 异 常 处 理 (try 、 throw 、 catch) 和 结 构 异常 处 理 ( __ try/ _ _except 、 _ _try/ _ _finally) 。 如 果 可 能 , 应 尽 可 能 用 C++ 的 异 常 处 理 而 不 用 结 构 异 常 处 理 。
注 意 : 在 本 节 中 , 术 语 “ 结 构 异 常 处 理 ” 和 “ 结 构 的 异 常 ” ( 或 “ C 异 常 ” ) 仅 指由 Win32 提 供 的 结 构 异 常 处 理 机 制 , 所 有 其 它 的 异 常 处 理 的 引 用 ( 或“ C++ 异 常 ” ) 都 指 的 是 C++ 异 常 处 理 机 制 。
尽 管 结 构 异 常 处 理 与 C 和 C++ 源 文 件 一 起 作 用 , 但 它 不 是 专 为 C++ 设 计 的 , 对 C++ 程 序 , 你 应 当 使 用 C++ 异 常 处 理 。
try , catch 和 throw 语句
C++ 语 言 提 供 对 处 理 异 常 情 况 的 内 部 支 持 , 异 常 情 况 即 是 所 知 道 的 “ 异 常 ” , 可 能在 你 的 程 序 执 行 期 间 出 现 。
try 、 throw 和 catch 语 句 已 被 加 到 C++ 语 言 中 去 实 现 异 常 处 理 。 有 了 C++ 异 常处 理 , 你 的 程 序 可 以 向 更 高 的 执 行 上 下 文 传 递 意 想 不 到 的 事 件 , 这 些 上 下 文 能 更好 地 从 这 些 异 常 事 件 中 恢 复 过 来 。 这 些 异 常 由 正 常 控 制 流 外 的 代 码 进 行 处 理 。Microsoft C++ 编 译 器 朝 着 C++ 进 化 中 的 标 准 去 实 现 基 于 ISO WG21/ANSI X3J16 工 作 文 件 的 C++ 异 常 处 理 模 式 。
语 法
try 块 :
try 复 合 语 句 处 理 器 表处 理 器 表 :
处 理 器 处 理 器 表 opt
处 理 器 :
catch( 异 常 说 明 ) 复 合 语 句异 常 说 明 :
类 型 指 示 符 表 说 明 符
类 型 指 示 符 表 抽 象 说 明 符
类 型 指 示 符 表
... throw- 表 达 式 :
throw 赋 值 表 达 式 opt
try 子 句 后 的 复 合 语 句 是 代 码 的 保 护 段 。 throw 表 达 式 “ 丢 弃 ” ( 凸 起 ) 一 个 异常 ,catch 子 句 后 的 复 合 语 句 是 异 常 处 理 器 , “ 捕 获 ” ( 处 理 ) 由 throw 表 达 式 丢弃 的 异 常 。 异 常 说 明 语 句 指 示 子 句 处 理 的 异 常 的 类 型 , 类 型 可 以 是 任 何 有 效 的 数据 类 型 , 包 括 C++ 的 类 。 如 果 异 常 说 明 语 句 是 一 个 省 略 号 (...),catch 子 句 处 理任 何 类 型 的 异 常 , 包 括 C 的 异 常 。 这 样 的 处 理 器 必 须 是 其 try 块 的 最 后 一 个 处 理器 。
throw 的 操 作 数 语 法 上 与 return 语 句 的 操 作 数 相 似 。
注 意 : Microsoft C++ 不 支 持 函 数 throw 特 征 机 制 , 如 ANSI C++ 草 案 的 15.5 节 所描 述 的 。 此 外 , 它 也 不 支 持 ANSI C++ 草 案 的 15 节 中 描 述 的 function - try - blo ck 。执 行 过 程 如 下 :
-
控 制 通 过 正 常 的 顺 序 执 行 到 达 try 语句 , 保 护 段 ( 在 try 块内 ) 被 执 行 。
-
如 果 在 保 护 段 执 行 期 间 没 有 引 起 异 常 , 跟 在 try 块 后 的
catch 子 句 不 执 行 。从 异 常 被 丢 弃 的 try 块 后 跟 随 的 最 后 一 个 catch 子 句 后 面 的 语 句 继 续 执 行 下 去 。3. 如 果 在 保 护 段 执 行 期 间 或 在 保 护 段 调 用 的 任 何 例 行 程 序 中 ( 直 接 或 间 接 的 调用 ) 有 异 常 被 丢 弃 , 则 从 通 过 throw 操 作 数 创 建 的 对 象 中 创 建 一 个 异 常 对 象 ( 这 隐含 指 可 能 包 含 一 个 拷 贝 构 造 函 数 ) 。 在 此 点 , 编 译 器 在 能 够 处 理 丢 弃 类 型 的 异 常的 更 高 执 行 上 下 文 中 寻 找 一 个 catch 子 句 ( 或 一 个 能 处 理 任 何 类 型 异 常 的 catch 处 理 器 ) 。 catch 处 理 程 序 按 其 在 try 块 后 出 现 的 顺 序 被 检 查 。 如 果 没 有 找 到 合适 的 处 理 器 , 则 下 一 个 动 态 封 闭 的 try 块 被 检 查 。 此 处 理 继 续 下 去 直 到 最 外 层 封闭 try 块 被 检 查 完 。
- 如 果 匹 配 的 处 理 器 未 找 到 , 或 如 果 在 不 自 动 分 行 时 出 现 异 常 , 但 在 处 理 器 得
到 控 制 之 前 预 定 义 的 运 行 函 数 terminate 被 调 用 。 如 果 一 个 异 常 发 生 在 丢 弃 异常 之 后 , 则 在 循 环 展 开 开 始 之 前 调 用 terminate 。
- 如 果 一 个 匹 配 的 catch 处 理 器 被 找 到 , 且 它 通 过 值 进 行
捕 获 , 则 其 形 参 通 过 拷贝 异 常 对 象 进 行 初 始 化 。 如 果 它 通 过 引 用 进 行 捕 获 , 则 参 量 被 初 始 化 为 指 向 异 常对 象 , 在 形 参 被 初 始 化 之 后 ,“ 循 环 展 开 栈 ”的 过 程 开 始 。这 包 括 对 那 些 在 与 catch 处 理 器 相 对 应 的 try 块 开 始 和 异 常 丢 弃 地 点 之 间 创 建 的 ( 但 尚 未 析 构 的 ) 所 有 自动 对 象 的 析 构 。 析 构 以 与 构 造 相 反 的 顺 序 进 行 。 catch 处 理 器 被 执 行 , 且 程 序 恢复 到 跟 随 在 最 后 的 处 理 器 之 后 的 执 行 ( 即 不 是 catch 处 理 器 的 第 一 条 语 句 或 构造 ) 。控 制 仅 能 通 过 一 个 丢 弃 的 异 常 输 入 一 个 catch 处 理 器 , 而 永 远 不 能 通 过 goto 语 句 或 switch 语 句 中 的 case 标 号 。
以 下 是 一 个 try 块 和 其 相 应 的 catch 处 理 器 的 简 单 例 子 , 此 例 子 检 测 使 用 new 运
算 符 的 存 储 器 分 配 操 作 的 失 败 。 如 果 new 成 功 了 , 则 catch 处 理 器 永 不 执 行 : #include <iostream.h>
int main()
{
char *buf;
try
{
buf=new char[512];
if(buf==0)
throw "Memory allocation failure!";
}
catch (char *str)
{
cout << "Exception raised: " << str << ′ \n ′ ;
}
//...
return 0;
}
throw 表 达 式 的 操 作 数 指 示 一 个 char* 类 型 的 异 常 正 被 丢 弃 。 它 由 表 示 有 捕 获char* 类 型 的 一 个 异 常 的 能 力 的 catch 处 理 器 进 行 处 理 。 在 存 储 器 分 配 失 败 事 件中 , 这 是 从 前 面 例 子 得 到 的 输 出 :
Exception raised: Memory allocation failure!
C++ 异 常 处 理 的 真 正 能 力 不 仅 在 于 其 处 理 各 种 不 同 类 型 的 异 常 的 能 力 , 还 在 于 在堆 栈 循 环 展 开 期 间 为 异 常 丢 弃 前 构 造 的 所 有 局 部 对 象 自 动 调 用 析 构 函 数 的 能力 。
以 下 例 子 演 示 了 使 用 带 析 构 语 义 的 类 的 C++ 异 常 处 理 :
#include <iostream.h> void MyFunc(void);
class CTest
{
public:
CTest() {};
~ CTest() {};
const char *ShowReason() const { return "Exception in CTest class.";}
};
class CDtorDemo
{
public:
CDtorDemo();
~ CDtorDemo();
};
CDtorDemo::CDtorDemo()
{
cout << "Constructing CDtorDemo.\n";
}
CDtorDemo:: ~ CDtorDemo()
{
cout << "Destructing CDtorDemo.\n";
}
void MyFunc()
{
CDtorDemo D;
cout << "In MyFunc(). Throwing CTest exception.\n";
throw CTest();
}
int main()
{
cout << "In main.\n";
try
{
cout << "In try block, calling MyFunc().\n";
MyFunc();
}
catch (CTest E)
{
cout << "In catch handler.\n";
cout << "Caught CTest exception type:";
cout << E.ShowReason() << "\n";
}
catch (char *str)
{
cout << "Canght some other exception:" << str << "\n";
}
cout << "Back in main. Execution resumes here.\n";
return 0;
}
以 下 是 上 面 例 子 的 输 出 :
In main.
In try block, calling MyFunc(). Constructing CDtorDemo.
In MyFunc(). Throwing CTest exception. Destructing CDtorDemo.
In catch handler.
Caught CTest exception type; Exception in CTest class. Back in main. Execution resumes here.
注 意 在 此 例 中 , 异 常 参 量 (catch 子 句 的 参 量 ) 在 两 个 catch 处 理 器 中 都 被 说 明 : catch(CTest E)
//...
catch(char *str)
//...
你 不 需 要 说 明 此 参 量 ; 在 很 多 情 况 下 可 能 通 知 处 理 器 有 某 个 特 定 类 型 的 异 常 已 经产 生 就 足 够 了 。 但 是 如 果 你 在 异 常 说 明 中 没 有 说 明 一 个 异 常 对 象 , 你 将 无 法 访 问catch 处 理 程 序 子 句 中 的 那 个 对 象 。
- 个 不 带 操 作 数 的 throw 表 达 式 把 当 前 正 被 处 理 的 异 常 再 次 丢 弃 , 这 样 一 个 表 达式 仅 仅 应 该 出 现 在 一 个 catch 处 理 器 中 或 从 catch 处 理 器 内 部 被 调 用 的 函 数 中 , 再 次 丢 弃 的 异 常 对 象 是 源 异 常 对 象 ( 不 是 拷 贝 ) 。 例 如 :
try
{
throw CSomeOtherException();
}
catch(...) // 处 理 所 有 异 常
{
// 对 异 常 作 出 响 应 ( 也 许 仅 仅 是 部 分 的 )
//...
throw; // 将 异 常 传 给 某 个 其 它 处 理 器
}
未 处 理 的 异 常
如 果 为 当 前 的 异 常 找 不 到 匹 配 的 处 理 器 ( 或 省 略 的 catch 处 理 器 ), 则 预 定 义 的terminate 函 数 被 调 用 ( 你 还 可 以 在 你 的 任 何 处 理 器 中 显 式 地 调 用 terminate) 。terminate 的 缺 省 动 作 是 调 用 abort, 如 果 你 想 要 terminate 在 退 出 应 用 之 前 调用 你 程 序 中 的 某 个 其 它 的 函 数 , 用 待 调 用 的 函 数 名 作 为 单 个 参 量 去 调 用set_terminate 函 数 。 你 可 在 你 程 序 中 的 任 何 地 方 调 用 set_terminate 。terminate 例 程 总 是 调 用 给 定 的 最 后 一 个 函 数 作 为 set_terminate 的 一 个 参 量 。例 如 :
#include <eh.h> // 用 于 函 数 原 型
//...
void term_func() {//...} int main( )
{
try
{
//...
set_termin a te(term_func);
//...
throw "Out of mem o ry!"; // 对 此 异 常 无 catch 处 理 器
}
catch(int)
{
cout << "Integer exception raised.";
}
return 0;
}
term_func 函 数 应 该 终 止 程 序 或 当 前 的 线 程 , 理 想 地 是 通 过 调 用 exit 。 如 果 它 没有 且 是 返 回 其 调 用 者 , 则 abort 被 调 用 。
有 关 C++ 异 常 处 理 的 更 多 信 息 , 参 见 Margaret A.Ellis 和 Bjarne Stroustrup 所 著 的 “ 注 释 的 C++ 参 考 手 册 ”。
结 构 异 常 处 理
___ try/ __ except 和 __try/ __finally 语 句 是 Microsoft 对 C 语 言 的 扩 充 , 使 得 应 用在 将 正 常 终 止 执 行 的 事 件 之 后 获 得 程 序 的 控 制 权 。
注 意 : 结 构 异 常 处 理 与 C 和 C++ 源 文 件 一 起 工 作 。 但 是 它 不 是 特 别 为 C++ 设 计 的 。尽 管 局 部 对 象 的 析 构 函 数 将 被 调 用 , 如 果 你 在 一 个 C++ 程 序 中 使 用 结 构 异 常 处 理( 如 果 你 使 用 /GX 编 译 器 选 项 ); 但 你 用 C++ 异 常 处 理 能 确 保 你 的 代 码 具 有 更 好 的移 植 性 。 C++ 异 常 处 理 机 制 更 灵 活 , 它 可 以 处 理 任 何 类 型 的 异 常 。
更 多 的 信 息 参 见 本 卷 前 面 的“ C 语 言 参 考 ” 中 的 第 5 章“ 语 句 ”中 的“ try-finally 语 句 ”。
语 法
try-except 语 句 :
__ try 复 合 语 句
__ except ( 表 达 式 ) 复 合 语 句try-finally 语 句 :
__ try 复 合 语 句
__ finally 复 合 语 句
如 果 你 有 使 用 结 构 化 异 常 处 理 的 C 模 式 , 则 它 们 可 与 使 用 C++ 异 常 处 理 的 C++ 模式 混 用 。
当 一 个 C( 结 构 化 的 ) 异 常 产 生 时 , 它 可 以 由 C 处 理 器 处 理 , 或 由 C++ 的 catch 处 理器 捕 获 , 要 看 哪 个 与 异 常 上 下 文 更 动 态 地 接 近 。 两 种 模 式 的 一 个 主 要 区 别 是 : 当C 异 常 产 生 时 , 它 总 是 无 符 号 整 型 , 而 一 个 C++ 异 常 可 以 是 任 何 类 型 的 。 即 C 异 常由 一 个 无 符 号 整 型 值 标 识 , 而 C++ 丢 弃 异 常 则 由 数 据 类 型 标 识 。 但 是 当 一 个 C++ 的 catch 处 理 器 能 捕 获 一 个 C 异 常 时 ( 例 如 , 通 过 一 个 “ 省 略 号 的 ” catch 处 理器 ), 一个 C 异 常 也 可 以 通 过 使 用 C 异 常 绕 接 类 作 为 一 个 有 类 型 的 异 常 来 处 理 。通 过 从 此 类 派 生 , 每 个 C 异 常 可 归 属 于 一 个 特 定 的 派 生 类 。
为 了 使 用 C 异 常 wr a pper 类 , 你 可 安 装 一 个 定 制 的 C 异 常 转 换 器 函 数 , 该 函 数 在每 次 产 生 C 异 常 时 由 内 部 异 常 处 理 机 制 调 用 。 在 你 的 转 换 器 函 数 内 , 你 可 以 丢 弃任 何 有 类 型 的 异 常 , 它 们 可 由 对 应 的 匹 配 C++ 的 catch 处 理 器 捕 获 。 要 指 定 一 个定 制 翻 译 函 数 , 用 你 的 翻 译 函 数 名 作 为 单 个 参 量 去 调 用 _set_se_translator 函
数 。
第 6 章 说 明
说 明 向 一 个 程 序 中 引 入 新 的 名 称 , 本 章 的 主 题 包 括 :
-
说 明 符
-
枚 举 说 明
-
连 接 规 格
-
模 板 规 格
-
名 称 空 间
除 引 入 一 个 新 名 称 外 , 一 个 说 明 还 指 定 一 个 标 识 符 是 如 何 被 编 译 器 解 释 的 。 说 明并 不 自 动 地 保 留 标 识 符 相 关 的 存 储 , 保 留 存 储 由 定 义 完 成 。
注 意 : 大 多 数 的 说 明 也 是 定 义 。
语 法
说 明 :
说 明 指 示 符 opt 说 明 符 表 opt ;
函 数 定 义
连 接 规 格
模 板 规 格
在 说 明 符 表 中 的 说 明 符 包 含 正 被 说 明 的 名 称 , 尽 管 说 明 符 表 是 作 为 可 选 项 给 出 的但 它 也 只 能 是 在 函 数 的 说 明 或 定 义 中 被 省 略 。
注 意 : 一 个 函 数 的 说 明 通 常 被 称 为 一 个 “ 原 型 ”。 此 说 明 提 供 参 量 的 类 型 信 息 和
函 数 的 返 回 类 型 , 从 而 允 许 编 译 器 执 行 正 确 的 转 换 以 及 确 保 类 型 的 安 全 性 。
- 个 说 明 的 说 明 指 示 符 部 分 也 是 作 为 可 选 项 给 出 的 , 但 它 只 能 在 类 类 型 或 枚 举 的说 明 中 被 省 略 。
说 明 出 现 在 一 个 范 围 中 , 这 用 于 控 制 被 说 明 名 称 的 可 见 性 和 被 定 义 对 象 ( 如 果 有 ) 的 持 续 时 间 。 关 于 范 围 规 则 与 说 明 是 如 何 交 互 的 更 多 信 息 见 第 2 章 “ 基 本 概 念 ” 中 的 范 围 。
- 个 对 象 的 说 明 也 是 一 个 定 义 , 除 非 它 包 含 下 一 节 存 储 类 指 示 符 中 所 描 述 的extern 存 储 类 指 示 符 。 一 个 函 数 说 明 也 是 一 个 定 义 , 除 非 它 是 一 个 原 型 — — 不带 定 义 函 数 体 的 一 个 函 数 头 。 一 个 对 象 的 定 义 导 致 存 储 分 配 和 那 个 对 象 的 合 适的 初 始 化 。
指 示 符
本 节 解 释 说 明 中 的 说 明 指 示 符 部 分 ( 说 明 的 语 法 在 本 章 开 头 已 给 出 ) 。
语 法
多 个 说 明 指 示 符
多 个 说 明 指 示 符 opt 说 明 指 示 符说 明 指 示 符 :
存 储 类 指 示 符
类 型 指 示 符
函 数 指 示 符
friend
typedef
__ declspec( 扩 充 的 说 明 修 饰 符 序 列 )
Microsoft 特 殊 关 键 字 _ _declspec 在 附 录 B “ Microsoft 特 殊 修 饰 符 ” 中 的 “ 扩充 属 性 语 法 ” 中 讨 论 。
说 明 的 说 明 指 示 符 部 分 是 可 被 构 造 为 一 个 类 型 名 称 的 最 长 的 说 明 指 示 符 序 列 , 说明 的 剩 余 部 分 是 引 入 的 名 称 , 以 下 表 中 的 例 子 说 明 了 此 概 念 :
说明 说明指示 名称
char *lpszAppName; char* lpszAppName typedef char * LPSTR; char* LPSTR LPSTR strcyp(LPSTR,LRSTR); LPSTR strcpy
volatile void *pvvObj; volatilevoi d *
pvvObj
因 为 signed 、 unsigned 、 long 和 short 都 隐 含 了 int, 所 以 跟 随 这 些 关 键 字 之
- 的 一 个 typedef 名 称 被 作 为 说 明 符 表 中 的 一 个 成 员 , 而 不 是 作 为 说 明 指 示 符 。注 意 : 因 为 一 个 名 称 可 以 被 重 新 说 明 , 所 以 其 解 释 应 对 应 当 前 范 围 中 最 近 的 说明 。 重 说 明 能 影 响 名 称 是 如 何 被 编 译 器 解 释 的 , 特 别 是 typedef 名 称 。
存 储 类 指 示 符
C++ 存 储 类 指 示 符 告 诉 编 译 器 一 个 对 象 应 存 储 的 地 方 , 以 及 他 们 说 明 的 对 象 或 函数 的 持 续 时 间 和 可 见 性 。
语 法
存 储 类 指 示 符 :
auto
register
static
extern
自 动 存 储 类 指 示 符
auto 和 register 存 储 类 指 示 符 仅 能 被 用 于 说 明 被 用 于 块 内 的 名 称 或 说 明 函 数的 形 参 。 术 语 “ 自 动 ” 来 源 于 这 些 对 象 的 存 储 是 在 运 行 时 自 动 分 配 的 ( 通 常 在 程序 的 堆 栈 上 ) 事 实 。
auto 关 键 字
极 少 有 程 序 员 在 说 明 中 使 用 auto 关 键 字 。 因 为 所 有 具 有 块 范 围 的 对 象 在 没 有 显示 地 说 明 为 另 一 个 存 储 类 的 都 隐 含 指 为 自 动 的 。 因 此 , 以 下 两 个 说 明 是 等 价 的 :
{
auto int i; // 显 式 说 明 为 auto int j; // 隐 含 为 auto
}
register 关 键 字
Microsoft 特 殊 处 →
编 译 器 不 接 受 用 户 对 寄 存 器 变 量 的 要 求 , 而 是 在 全 局 寄 存 器 分 配 优 化 (/Oe 选 项 ) 打 开 时 作 出 其 自 己 的 寄 存 器 选 择 。 但 是 与 register 关 键 字 相 关 的 所 有 其 它 语 义
是 可 用 的 。
Microsoft 特 殊 处 结 束
ANSI C 不 允 许 取 一 个 寄 存 器 对 象 的 地 址 , 这 个 限 制 不 用 于 C++ 中 。 但 如 果 取 地 址运 算 符 (&) 用 于 一 个 对 象 , 则 编 译 器 必 须 把 对 象 放 在 一 个 可 用 一 个 地 址 表 示 的 地方 , 即 在 实 践 中 , 这 意 味 着 在 存 储 器 中 而 不 是 在 一 个 寄 存 器 中 。
静 态 存 储 类 指 示 符
静 态 存 储 类 指 示 符 static 和 extren 可 被 用 于 对 象 和 函 数 , 表 6.1 给 出 了 static 和 extern 关 键 字 可 以 使 用 的 地 方 和 不 能 使 用 的 地 方 。
表 6.1 static 和 extern 的 使 用
构造 |
static 可用否 ? |
extern 可用否 ? |
---|---|---|
在一个块内的函数说明 |
否 |
是 |
函数的形参 |
否 |
否 |
块内的对象 |
是 |
是 |
块外的对象 |
是 |
是 |
函数 |
是 |
是 |
类成员函数 |
是 |
否 |
类成员数据 |
是 |
否 |
typedef 名称 |
否 |
否 |
使 用 static 关 键 字 指 定 的 名 称 具 有 内 部 连 接 , 除 了 一 个 类 的 静 态 成 员 是 具 有 外部 连 接 , 即 名 称 在 当 前 转 换 单 元 外 面 是 不 可 见 的 。 使 用 extern 关 键 字 指 定 的 名
称 具 有 外 部 连 接 , 除 非 以 前 定 义 为 具 有 内 部 连 接 。 关 于 名 称 的 可 见 性 的 更 多 信 息参 见 第 2 章 “ 基 本 概 念 ” 中 的 “ 范 围 ” 和 “ 程 序 和 连 接 ”。
注 意 : 说 明 为 inline 而 且 不 是 类 成 员 函 数 的 函 数 与 说 明 为 static 的 函 数 给 定 的是 相 同 的 连 接 特 征 。
说 明 尚 未 被 编 译 器 碰 到 的 一 个 类 名 可 被 用 在 extern 说 明 中 。 具 有 此 种 说 明 而 引入 的 名 称 直 到 遇 到 了 类 说 明 才 能 被 使 用 。
不 带 存 储 类 指 示 符 的 名 称
没 有 显 式 的 存 储 类 指 示 符 的 文 件 范 围 名 称 具 有 外 部 连 接 , 除 非 他 们 :
-
使 用 const 关 键 字 说 明 。
-
在 前 面 说 明 具 有 内 部 连 接 。
函 数 指 示 符
在 函 数 说 明 中 , 你 可 以 使 用 inline 和 virtual 关 键 字 作 为 指 示 符 。 这 种 virtual 的 用 法 有 别 于 其 在 一 个 类 定 义 的 基 类 指 示 符 中 的 使 用 。
inline 指 示 符
inline 指 示 符 指 示 编 译 器 用 函 数 体 代 码 去 替 换 函 数 调 用 , 这 种 替 换 叫 “ 联 编 扩展 ” ( 有 时 称 为 “ 联 编 ” ) 联 编 扩 展 在 更 大 的 代 码 的 潜 在 代 价 上 减 少 函 数 调 用 的费 用 。
inline 关 键 字 告 知 编 译 器 选 用 联 编 扩 展 , 但 是 编 译 器 能 够 创 建 函 数 的 分 开 的 实例 ( 实 例 化 ), 以 及 创 建 标 准 的 调 用 连 接 , 而 不 是 联 编 插 入 代 码 。 这 能 够 发 生 的 两
种 情 况 是 :
-
递 归 函 数 。
-
在 转 换 单 元 的 别 处 通 过 指 针 指 向 的 函 数 。
注 意 被 考 虑 作 为 联 编 候 选 者 的 函 数 必 须 使 用 新 风 格 的 函 数 定 义 。被 说 明 为 inline 且 不 是 类 成 员 函 数 的 函 数 具 有 内 部 连 接 , 除 非 有 其 它 的 指 定 。
Microsoft 特 殊 处 →
__inline 关 键 字 等 价 于 inline 。
__forceinline 关 键 字 指 示 编 译 器 进 行 联 编 函 数 而 不 执 行 任 何 代 价 / 效 益 分 析 。程 序 员 在 使 用 此 关 键 字 中 必 须 有 好 的 判 断 能 力 , 不 减 少 __forceinline 的 使 用 会导 致 更 大 的 有 时 甚 至 是 更 慢 的 代 码 。 既 使 用 __forceinline, 编 译 器 也 不 能 在 所 有的 情 况 中 都 联 编 代 码 。 编 译 器 不 能 联 编 一 个 函 数 , 如 果 :
-
函 数 或 其 调 用 者 是 用 /obO 编 译 的 ( 调 试 模 块 的 缺 省 选 项 ) 。
-
函 数 及 其 调 用 者 使 用 不 同 类 型 的 异 常 处 理 ( 一 个 是 C++ 异 常 处 理 , 另一 个 是 结 构 化 的 异 常 处 理 ) 。
-
函 数 有 一 个 可 变 的 参 量 表 。
-
函 数 使 用 联 编 集 合 , 除 非 用 / o g 、 /Ox 、 /O1 或 /O2/ 编 译 。
-
函 数 用 值 返 回 一 个 非 展 开 的 对 象 , 当 用 /GX 、 /EHs 或 /EHa 编 译 时 。
-
函 数 接 到 一 个 由 值 传 递 的 拷 贝 构 成 的 对 象 , 当 用 /GX 、 /EHs 或 /EHa 编 译 时 。
-
函 数 是 递 归 的 而 且 没 有 伴 随 #pragma(inline_recursion 为 on) 。 带有 inline__recursion 编 译 指 示 , 递 归 函 数 可 被 联 编 到 8 次 调 用 的深 度 , 或 由 inline_depth 编 译 指 示 确 定 的 深 度 ( 见 下 面 内 容 ) 。
-
是 虚 函 数 。
-
程 序 取 函 数 的 地 址 。
如 果 编 译 器 不 能 联 编 一 个 用 __ forceinline 说 明 的 函 数 , 它 产 生 一 个 1 级 警 告(4714) 。
Microsoft 特 殊 处 结 束
如 果 用 正 常 的 函 数 , 一 个 联 编 函 数 的 参 量 没 有 定 义 一 个 求 值 顺 序 , 事 实 上 , 它 可 以与 使 用 正 常 函 数 调 用 拓 扑 进 行 传 递 时 的 参 量 求 值 顺 序 不 同 。
Microsoft 特 殊 处 →
递 归 函 数 可 以 被 联 编 替 换 到 由 inline_depth 编 译 指 示 指 定 的 深 度 。 在 那 个 深 度之 后 , 递 归 函 数 调 用 被 当 作 是 对 函 数 的 一 个 实 例 的 调 用 ,inline_recursion 编 译指 示 控 制 在 当 前 扩 展 之 下 的 函 数 的 联 编 扩 展 。相 关 的 信 息 参 见 联 编 函 数 扩 展 (/Ob 编 译 器 选 项 。
Microsoft 特 殊 处 结 束
联 编 类 成 员 函 数
在 一 个 类 说 明 体 内 定 义 的 函 数 是 一 个 联 编 函 数 , 考 虑 以 下 类 说 明 : class Account
{
public:
Account (double initial_balance){balance=initial_balance;}
double GetBalance();
double Deposit(Double Amount);
double Withdraw(double Amount; private;
double balance;
};
A ccount 构 造 函 数 是 一 个 联 编 函 数 , 成 员 函 数 GetBalance 、 Deposit 和 Withdraw 没 有 被 指 定 为 inline, 但 可 以 用 如 下 这 样 的 代 码 作 为 联 编 函 数 去 实 现 :
inline double Acount::GetBalance
{
return balance;
}
inline double Account::Deposit(double Amount)
{
return (balance+=Amount);
}
inline double Account::Withdraw(double Amount)
{
return (balance-=Amount);
}
注 意 : 在 类 说 明 中 , 函 数 没 有 用 inline 关 键 字 进 行 说 明 。 inline 关 键 字 可 在 类说 明 中 被 指 定 , 结 果 是 一 样 的 。
-
个 给 定 的 联 编 成 员 函 数 , 在 每 个 编 译 单 元 中 必 须 以 同 样 的 方 式 被 说 明 , 此 约 束使 得 联 编 函 数 表 现 得 如 同 是 被 实 例 化 的 函 数 , 此 外 , 一 个 联 编 函 数 必 须 只 有 一 个定 义 。
-
个 类 成 员 函 数 缺 省 为 外 部 连 接 , 除 非 那 个 函 数 的 定 义 包 含 inline 说 明 符 。 前面 的 例 子 显 示 了 这 些 函 数 不 需 要 用 inline 说 明 符 显 式 地 说 明 , 在 函 数 定 义 中 使用 inline 使 其 成 为 一 个 联 编 函 数 。 但 是 在 对 那 个 函 数 调 用 之 后 再 用 inline 重新 说 明 该 函 数 则 是 非 法 的 。
联 编 函 数 和 宏
尽 管 联 编 函 数 与 宏 相 似 ( 因 为 函 数 代 码 在 编 译 时 在 调 用 点 处 被 扩 展 ) 。 但 联 编 函数 由 编 译 器 进 行 语 义 分 析 , 而 宏 则 通 过 预 处 理 器 进 行 扩 展 。 结 果 是 存 在 以 下 几 个重 要 的 不 同 点 :
-
联 编 函 数 遵 从 强 制 在 普 通 函 数 上 的 所 有 的 类 型 安 全 性 协 议 。
-
联 编 函 数 的 说 明 与 任 何 其 它 函 数 一 样 使 用 相 同 的 语 法 , 除 了 在 函 数说 明 中 它 们 包 括 inline 关 键 字 。
作 为 参 量 传 给 联 编 函 数 的 表 达 式 只 求 值 一 次 。 在 某 些 情 况 下 , 作 为 参 量 传 给 宏 的表 达 式 可 求 值 不 止 一 次 。 以 下 列 子 给 出 了 一 个 宏 , 完 成 从 小 写 字 母 到 大 写 字 母 的转 换 :
#include <stdio.h> #include <conio.h>
#define toupper(a) ((a)>='a' && ((a))<="z") ? ((a)-('a'-'A')) : (a))
void main()
{
char ch = toupper(_getch());
printf("%c",ch);
}
表 达 式 toupper(_getch()) 的 意 图 是 一 个 字 符 应 从 控 制 台 设 备 (stdin) 读 入 , 而且 如 果 需 要 则 转 成 大 写 。
由 于 实 现 _getch 执 行 一 次 以 确 定 字 符 是 否 大 于 或 等 于 “ a ” , 且 一 次 确 定 是 否 小于 或 等 于 “ z ”。
如 果 在 那 个 范 围 内 , _getch 再 执 行 一 次 将 字 符 转 成 大 写 。 这 意 味 着 当 理 想 情 况只 应 等 待 一 个 字 符 时 , 程 序 等 待 两 个 或 三 个 字 符 。
联 编 函 数 纠 正 了 这 个 问 题 :
#include <stdio.h> #include <conio.h>
inline char toupper(char a)
{
return ((a>= ′ a ′ && a<= ′ z ′ ) ? a-( ′ a ′ - ′ A ′ ) : a);
}
void main()
{
char ch=toupper(_getch());
printf("%c",ch);
}
何 时 使 用 联 编 函 数
联 编 函 数 常 用 于 象 访 问 私 有 的 数 据 成 员 这 样 的 小 函 数 。 这 些 一 行 或 两 行 “ 访 问器 ” 函 数 的 主 要 目 的 是 返 回 对 象 的 状 态 信 息 , 短 函 数 对 函 数 调 用 的 一 般 代 价 很 敏感 。 长 些 的 函 数 在 调 用 或 返 回 顺 序 上 按 比 例 花 费 少 些 时 间 , 且 从 联 编 获 益 少 些 。在 第 4 章 “ 表 达 式 ” 中 的 “ 函 数 调 用 结 果 ” 中 介 绍 的 Point 类 可 以 优 化 为 如 下所 示 :
class Point
{
public:
// 定 义 “ 访 问 器 ” (accessor) 函 数 为 引 用 类 型
unsigned& x();
unsigned& y(); private:
unsigned _x;
unsigned _y;
};
inline unsigned& Point::x()
{
return _x;
}
inline unsigne d & Point::y()
{
return _y;
}
假 定 坐 标 操 作 在 这 样 一 个 类 的 一 个 客 户 中 是 相 对 普 通 的 操 作 , 用 inline 指 定 两个 访 问 器 函 数 ( 在 前 面 例 子 中 的 x 和 y) 典 型 地 节 省 了 如 下 开 销 :
-
函 数 调 用 ( 包 括 参 量 传 递 和 在 堆 栈 中 放 置 对 象 的 地 址 )
-
保 持 调 用 者 的 堆 栈 框 架
-
新 的 堆 栈 框 架 的 设 置
-
返 回 值 传 递
-
旧 的 堆 栈 框 架 恢 复
-
返 回
virtual 指 示 符
virtual 关 键 字 仅 被 用 于 非 静 态 类 成 员 函 数 。 它 意 味 着 将 调 用 与 函 数 结 合 在 一起 要 推 迟 到 运 行 时 , 更 多 的 信 息 见 第 9 章 “ 派 生 类 ” 中 的 “ 虚 函 数 ”。
typedef 指 示 符
typedef 指 示 符 定 义 一 个 名 称 , 可 被 用 作 一 个 类 型 或 派 生 类 型 的 同 义 词 。 你 不 能在 一 个 函 数 定 义 内 部 使 用 typedef 指 示 符 。
语 法
typedef 名 称 :
标 识 符
一 个 typedef 说 明 引 进 一 个 名 称 , 在 其 范 围 内 成 为 由 说 明 的 说 明 指 示 符 部 分 给 定的 类 型 的 同 义 词 。 与 class 、 struct 、 union 和 enum 说 明 不 同 ,typedef 说 明 没有 引 入 新 类 型 , 它 们 引 进 的 是 已 存 在 类 型 的 新 名 称 。
typedef 说 明 的 用 法 之 一 是 使 说 明 更 为 统 一 、 紧 凑 , 例 如 : typedef char CHAR;// 字 符 类 型
typedef CHAR * PSTR; // 指 向 字 符 串 (char*) 的 指 针
...
PSTR strchr(PSTR source,CHAR target); 由 上 面 说 明 引 入 的 名 称 是 以 下 的 同 义 词 :
名称 同义类型
CHAR char
PSTR char *
上 面 例 子 的 代 码 说 明 了 一 个 类 型 名 称 CHAR, 然 后 用 于 定 义 派 生 类 型 名 称 PSTR( 指向 一 个 字 符 串 的 指 针 ) 。 最 后 , 该 名 称 用 于 说 明 函 数 strchr 。 要 看 typedef 关 键
字 如 何 用 于 区 分 说 明 的 , 把 前 面 strchr 的 说 明 与 以 下 说 明 比 较 : char *srtchr(char * source,char target);
要 使 用 typedef 在 相 同 的 说 明 中 指 定 基 本 的 和 派 生 的 类 型 , 你 可 以 用 逗 号 分 开 说明 符 。
例 如 :
typedef char CHAR,*PSTR;
typedef 的 特 别 复 杂 的 用 法 是 为 一 个 “ 指 向 返 回 类 型 为 T 的 函 数 的 指 针 ” 定 义一 个 同 义 词 。 例 如 , 一 个 含 义 为 “ 指 向 不 带 参 量 且 返 回 类 型 为 void 的 函 数 的 指针 ” 的 typedef 说 明 使 用 以 下 代 码 :
typedef void (*PVFN)();
同 义 词 在 说 明 通 过 指 针 调 用 的 函 数 数 组 时 是 方 便 的 : #include <iostream.h>
#include <stdlib.h>
extern void func1(); // 说 明 4 个 函 数
extern void func2(); // 这 些 函 数 假 定 在 其 它 的 位 置 定 义 的extern void func3();
extern void func4();
typedef void (*PVFN)(); // 为 指 向 不 带 参 量 且 返 回 类 型 为 void 的 函 数
// 的 指 针 说 明 同 义 词
void main(int argc,char * argv[])
{
// 说 明 一 个 函 数 指 针 数 组
PVFN pvfn1[]={func1,func2,func3,func4];
// 调 用 在 命 令 行 指 定 的 函 数
if(argc>0 && *argv[1]>'0' && *argv[1]<='4')
(*pvfn1[atoi(argv[1])-1])();
}
typedef 名 称 的 重 说 明
typedef 说 明 可 被 用 于 重 说 明 相 同 的 名 称 以 指 向 相 同 的 类 型 。 例 如 :
//FILE1.H
typedef char CHAR;
//FILE2.H
typedef char CHAR;
//PROG.CPP
#include "file1.h" #include "file2.h" /// 可 行
...
程 序 PROG.CPP 包 括 两 个 头 文 件 , 均 包 含 CHAR 名 称 的 typedef 说 明 。 只 要 两 个 说明 指 向 相 同 的 类 型 , 这 样 的 重 说 明 是 可 接 受 的 。
- 个 typedef 不 能 对 前 面 已 说 明 为 一 个 不 同 类 型 的 名 称 重 定 义 。 因 此 , 如 果FILE2.H 包 含 :
//FILE2.H
typedef int CHAR; // 错 误
编 译 器 产 生 一 个 错 误 , 因 为 试 图 重 新 说 明 CHAR 名 称 以 指 向 一 个 不 同 的 类 型 , 这 种扩 充 去 构 造 如 下 内 容 :
typedef char CHAR;
typedef char CHAR; // 可 行 , 重 新 说 明 作 为 同 一 类 型
typedef union REGS // 可 行 ,REGS 名 称 由 具 有 相 同 含 义 的 typedef 名 称 重 新说 明
{
struct wordregs x;
struct byteregs h;
} REGS;
带 类 类 型 的 typedef 的 使 用
带 类 类 型 的 typedef 指 示 符 的 使 用 被 极 大 地 支 持 , 因为 ANSI C 提 供 typedef 说明 中 无 名 的 结 构 的 说 明 。 例 如 , 很 多 C 程 序 员 使 用 如 下 方 法 :
typedef struct // 说 明 一 个 无 名 的 结 构 并 给 它 一 个 typedef 名 称 POINT
{
unsigned x;
unsigned y;
} POINT;
这 种 说 明 的 好 处 是 它 允 许 象 :
POINT ptOrigin; 的 说 明 代 替 :
struct point_t ptOrigin;
在 C++ 中 ,typedef 名 称 和 实 际 类 型 ( 用 class 、 struct 、 union 和 enum 关 键 字 说明 的 ) 的 区 别 越 来 越 明 显 。 尽 管 在 一 个 typedef 语 句 中 说 明 一 个 无 名 的 结 构 的 C 的 实 践 还 在 工 作 , 但 它 并 没 有 提 供 在 C 中 那 样 的 值 得 称 道 的 效 益 。
在 以 下 代 码 中 ,POINT 函 数 不 是 一 个 类 型 构 造 函 数 , 它 被 解 释 为 具 有 一 个 int 返回 类 型 的 函 数 说 明 符 。
typedef struct
{
POINT(); // 不 是 一 个 构 造 函 数
unsigned x:
unsigned y:
} POINT;
上 面 的 例 子 使 用 未 命 名 的 类 typedef 语 法 说 明 了 一 个 名 为 POINT 的 类 。 POINT 被 看 作 是 一 个 类 名 ; 但 , 以 下 限 制 适 用 于 以 此 方 式 引 进 的 名 称 :
-
名 称 ( 同 义 词 ) 不 能 出 现 在 一 个 class 、 struct 或 union 前
缀 的 后 面 。
-
名 称 在 一 个 类 说 明 中 不 能 被 用 作 构 造 函 数 或 析 构 函 数 名 称 。总 之 , 此 语 法 并 未 提 供 任 何 继 承 、 构 造 函 数 或 析 构 函 数 的 机 制 。
typedef 名 称 的 名 称 空 间
使 用 typedef 说 明 的 名 称 象 其 它 标 识 符 ( 除 了 语 句 标 号 ) 一 样 占 据 相 同 的 名 称 空间 。 因 此 , 它 们 不 能 使 用 相 同 的 标 识 符 作 为 一 个 前 面 已 说 明 了 的 名 称 , 除 了 在 一个 类 类 型 说 明 中 。
考 虑 以 下 例 子 :
typedef unsigned ling UL ;// 说 明 一 个 ty p edef 名 称 UL int UL; // 错 误 , 重 定 义
附 属 于 其 它 标 识 符 的 名 称 隐 藏 规 则 也 同 样 管 理 使 用 typedef 说 明 的 名 称 的 可 见性 , 因 此 , 以 下 例 子 在 C++ 中 是 合 法 的 :
typedef unsigned long UL; // 说 明 一 个 typedef 名 称 UL
...
long Beep
{
unsigned int UL; // 重 说 明 隐 藏 typedef 名 称
}
//typedef 名 称 在 这 里 “ 未 隐 藏 ”。
友 元 指 示 符
friend 指 示 符 用 于 设 计 与 类 成 员 函 数 具 有 相 同 的 访 问 权 限 的 函 数 或 类 。 友 元 函
数 和 类 在 第 10 章 “ 成 员 访 问 控 制 ” 中 的 “ 友 元 ” 部 分 有 详 细 的 介 绍 。
类 型 指 示 符
类 型 指 示 指 定 说 明 的 名 称 的 类 型 。
语 法
类 型 指 示 符 :
简 单 类 型 名 称
类 指 示 符
枚 举 指 示 符
详 细 阐 述 的 指 示 符
:: 类 名 称
const
volatile
以 下 章 节 讨 论 简 单 类 型 名 称 , 详 细 阐 述 了 类 型 指 示 符 和 嵌 套 的 类 型 名 称 。
简 单 类 型 名 称
一 个 简 单 类 型 名 称 是 一 个 完 整 类 型 的 名 称 。
语 法
简 单 类 型 名 称 :
完 整 类 名 称
限 定 类 型 名 称
char
short
int
long
signed
unsigned
float
double
void
表 6.2 给 出 了 简 单 类 型 名 称 如 何 在 一 起 使 用 的 。
表 6.2 类 型 名 称 组 合
类型 可与以下一起出现 注释
int long 或 short, 但 不 是 两 个 均出现
int 类型隐含 long int 类型
long |
int 或 double |
long 类型隐含 long int 类 型 |
|
---|---|---|---|
short |
int |
short 类型隐含 short int 类 |
|
型 |
|||
signed |
char 、 short 、 int 或 |
long |
signed 类 型 隐 含 signed int 。 |
signed char 类 型 的 对 象 的 最 |
|||
重 要 的 位 或 有 符 号 整 型 的 位 域 |
|||
被作为符号位。 |
续 表
unsigned char 、 short 、 int 或 long unsigned 类 型 隐 含 unsigned
int 。 unsigred char 类 型 的对 象 的 最 重 要 的 位 和 无 符 号 整型的位域不作为符号位。
详 细 阐 述 的 类 型 指 示 符
详 细 阐 述 的 类 型 指 示 符 被 用 于 说 明 用 户 定 义 的 类 型 , 这 些 可 以 是 类 或 枚 举 类 型 。
语 法
详 细 阐 述 的 类 型 指 示 符 ;
类 关 键 字 类 名 称
类 关 键 字 标 识 符
enum 枚 举 名 称类 关 键 字 :
class
struct
union
如 果 指 定 了 标 识 符 , 则 它 被 作 为 一 个 类 名 称 , 例 如 :
class Window;
该 语 句 说 明 Window 标 识 符 为 一 个 类 名 称 , 此 语 法 用 于 类 的 前 面 说 明 。 关 于 类 名称 的 更 多 信 息 参 见 第 8 章 “ 类 ” 中 的 “ 类 名 称 ”。
如 果 一 个 名 称 用 union 关 键 字 说 明 , 则 它 必 须 还 使 用 union 关 键 字 进 行 定 义 , 使
用 class 关 键 字 定 义 的 名 称 可 使 用 struct 关 键 字 进 行 说 明 ( 或 反 之 ), 因此 , 以 下代 码 例 子 是 合 法 的 :
// 合 法 例 1
struct A; // A 的 前 向 说 明
class A // 定 义 A
{
public:
int i;
};
// 合 法 例 2
class A;//A 的 前 向 说 明
struct A // 定 义 A
{
private:
int i;
};
// 合 法 例 3
union A; //A 的 前 向 说 明
union A// 定义 A
{
int i;
char ch[2];
};
但 以 下 这 些 例 子 是 非 法 的 :
// 非 合 法 例 1
union A; // A 的 前 向 说 明
struct A // 定 义 A
{
int i;
};
// 非 法 例 2
union A; //A 的 前 向 说 明
class A // 定 义 A
{
public:
int i;
};
// 非 法 例 3
struct A; //A 的 前 向 说 明
union A // 定 义 A
{
int i;
char ch[2];
};
嵌 套 的 类 型 名 称
Microsoft C++ 支 持 嵌 套 的 类 型 的 说 明 , 包 括 命 名 的 和 无 名 的 。
语 法
限 定 的 类 型 名 称 :
typedef 名 称
类 名 称 :: 限 定 的 类 型 名 称完 整 的 类 名 称 :
限 定 的 类 名 称
:: 限 定 的 类 名 称限 定 的 类 名 称 :
类 名 称
类 名 称 :: 限 定 的 类 名 称
在 某 些 编 程 情 形 中 , 需 要 定 义 嵌 套 的 类 型 。 这 些 类 型 仅 对 它 们 被 定 义 所 在 的 类 类型 的 成 员 函 数 是 可 见 的 , 它 们 还 可 以 通 过 用 范 围 分 辩 运 算 符 (::) 构 造 一 个 限 定 的类 型 名 而 变 成 可 见 的 。
注 意 : 一 个 使 用 嵌 套 类 型 的 最 常 用 的 类 层 次 是 输 入 输 出 流 。 在 输 入 输 出 流 头 文件 中 , 类 ios 的 定 义 包 括 了 一 系 列 枚 举 的 类 型 , 封 装 起 来 仅 与 输 入 输 出 流 库 一 同使 用 。
以 下 例 子 定 义 了 嵌 套 的 类 :
class WinSystem
{
public:
class Window
{
public:
Window(); // 缺 省 的 构 造 函 数
~ Window(); // 析 构 函 数
int NumberOf(); // 类 对 象 的 数 目
int Count(); // 类 对 象 的 个 数
private:
static int CCount;
};
class CommPort
{
public:
CommPort();// 缺 省 的 构 造 函 数
~ CommPort(); // 析 构 函 数
int NumberOf();// 类 对 象 的 数 目
int Count(); // 类 对 象 的 个 数
private:
static int CCount;
};
};
// 初 始 化 Wi n dSystem 静 态 成 员
int WinSystem::Window::CCount=0; int WinSystem::CommPort::CCount=0;
为 了 访 问 定 义 在 一 个 嵌 套 类 中 的 名 称 , 应 使 用 范 围 分 辩 运 算 符 :: 去 构 造 一 个 完 整的 类 名 。
运 算 符 的 使 用 在 前 面 例 子 中 static 成 员 的 初 始 化 中 给 出 了 , 在 你 的 程 序 中 要 使用 一 个 嵌 套 的 类 , 使 用 如 下 所 示 的 代 码 :
WinSystem::Window Desktop; WinSystem::Window AppWindow;
cout << "Number of active Windows:" << Desktop.Count() << "\n";
嵌 套 的 无 名 类 或 结 构 可 定 义 如 下 :
class Ledger
{
class
{
public:
double PayableAmt;
unsigned PayableDays;
} Payables;
class
{
public:
double RecvableAmt;
unsigned RecvableDays;
} Receivables;
};
- 个 无 名 类 必 须 是 一 个 没 有 成 员 函 数 、 没 有 静 态 成 员 的
集 合 。
注 意 : 尽 管 一 个 枚 举 的 类 型 可 被 定 义 在 一 个 类 说 明 内 部 , 但 反 之 却 不 行 ; 类 类 型不 能 被 定 义 在 枚 举 说 明 的 内 部 。
枚 举 说 明
- 个 枚 举 是 一 种 定 义 命 名 的 常 量 的 独 特 的 整 型 。 枚 举 使 用 enum 关 键 字 进 行 说明 。
语 法
枚 举 名 称 :
标 识 符枚 举 指 示 符 :
enum 标 识 符 opt { 枚 举 表 opt } 枚 举 表 :
枚 举 器
枚 举 表 , 枚 举 器枚 举 器 :
标 识 符
标 识 符 = 常 量 表 达 式
当 一 个 对 象 采 取 的 是 一 个 已 知 的 合 理 有 限 的 值 集 合 时 , 枚 举 的 类 型 是 有 价 值 的 , 考 虑 下 面 从 一 副 纸 牌 找 同 花 色 牌 的 例 子 :
class card
{
public
enum Suit
{
Diamonds
Hearts,
Clubs,
Spades
};
// 说 明 两 个 构 造 函 数 : 一 个 缺 省 构 造 函 数 , 以 及 一 个 设 置 新 牌 的 基 数 和 同 花色 的 值 的
// 构 造 函 数
Card();
Card(int CardInit,Suit SuitInit);
//GetT 和 Set 函 数
int GetCardinal();// 获 取 牌 的 基 数 值
int SetCardinal();// 设 置 牌 的 基 数 值
Suit GetSuit (); // 获 取 同 花 色 的 牌
void SetSuit(Suit new_suit); // 设 置 同 花 色 的 牌
char *NameOf(); // 获 取 表 示 牌 的 字 符 串private:
Suit suit;
int CardinalValue;
};
// 为 Suit 定 义 一 个 后 缀 增 量 运 算 符
inline Card::Suit operator++(Card::Suit &rs,int)
{
return rs= (Card::Suit)(rs+1);
}
上 面 例 子 定 义 了 一 个 类 Card, 包 含 一 个 嵌 套 的 枚 举 类 型 Suit 。 在 程 序 中 要 创 建一 盒 牌 , 使 用 如 下 代 码 :
Card *Deck[52]; int j=0;
for(Card::Suit curSuit=Card::Diamonds,curSuit<=Card::Spades;curSuit++)
for (int i=1;i<=13;++i)
Deck[j++]=new Card(i, curSuit);
在 上 面 例 子 中 ,Suit 类 型 是 被 嵌 套 的 ; 因 此 , 类 名 称 (Card) 必 须 显 式 地 用 于 公 共的 引 用 。 但 在 成 员 函 数 中 , 类 名 称 可 被 省 略 。 在 代 码 的 第 一 段 ,Card::Suit 的 后缀 增 量 运 算 符 被 定 义 。 没 有 用 户 定 义 的 增 量 运 算 符 ,curSuit 不 能 被 增 量 。 关 于用 户 定 义 的 运 算 符 的 更 多 信 息 见 第 11 章 “ 重 载 ” 中 的 “ 重 载 的 运 算 符 ”。
考 虑 NameOf 成 员 函 数 的 代 码 ( 更 好 的 实 现 在 稍 后 给 出 ): char* Card::NameOf() // 获 取 牌 的 名 称
{
static char szName[20];
static char *Numbers[]=
{"1","2","3","4","5","6","7","8","9","10",Jack",Queen","King"
};
static char *Suits[]=
{ "Diamonds","Hearts","Clubs","Spades"}-->;
if (GetCardinal()<13)
strcpy(szName,Numbers[GetCardinal()]);
strcat(szName, "of");
switch(GetSuit())
{
//Diamo n ds,Hearts,Clubs 和 Spades 不 需 要 显 式 的 类 限 定 符
case Diamonds:strcat(stName,"Diamonds");break;
case Hearts :strcat(stName,"Hearts");break;
case Clubs :strcat(stName,"Clubs");break;
case Spades :strcat(stName,"Spades");break;
}
return szName;
}
- 个 枚 举 的 类 型 是 一 个 整 型 , 用 enum 说 明 引 进 的 标 识 符 可 被 用 于 常 量 出 现 的 任何 位 置 。 通 常 , 第 一 个 标 识 符 的 值 是 0() 在 前 面 例 子 中 的 Diamonds), 每 一 个 后 续的 标 识 符 的 值 依 次 增 1 。 因 此 ,Spades 的 值 为 3 。
表 中 的 任 何 枚 举 器 , 包 括 第 一 个 , 可 被 初 始 化 为 一 个 值 , 而 不 是 其 缺 省 值 。 假 设Suit 的 说 明 如 下 所 示 :
enum Suit
{
Diamonds=5,
Hearts,
Clubs=4,
Spades
};
则 Diamonds 、 Hearts 、 Clubs 和 Spades 的 值 分 别 为 5 、 6 、 4 、 5 。 注 意 5 被 用了 不 止 一 次 。
这 些 枚 举 器 的 缺 省 值 简 化 了 NameOf 函 数 的 实 现 :
char * Card::NameOf() // 获 取 牌 的 名 称
{
static char szName[20];
static char *Numbers[]=
{"1","2","3","4","5","6","7","8","9","10","Jack","Queen","King"
};
static char *Suits[]=
{"Diamonds","Hearts","Clubs","Spades"}-->;
if (GetCar d inal()<13)
strcpy(szName,Numbers[GetCardinal()]);
strcat(szName,"of");
strcat(szName,Suits[GetSuit()]);
return szName;
}
访 问 器 函 数 GetSuit 返回 Suit 类 型 , 一 个 枚 举 的 类 型 , 因 为 枚 举 的 类 型 是 整 型 , 它 们 可 被 用 作 数 组 下 标 运 算 符 的 参 量 ( 更 多 的 信 息 参 见 第 4 章“ 表 达 式 ”中 的“ 下标 运 算 符 ” ) 。
枚 举 器 名 称
枚 举 器 的 名 称 必 须 不 同 于 任 何 其 它 的 枚 举 器 或 相 同 范 围 中 的 变 量 。 但 是 值 可 以重 复 。
枚 举 器 常 量 的 定 义
枚 举 器 被 认 为 是 在 其 初 始 化 器 之 后 立 即 被 定 义 的 。 因 此 , 它 们 可 被 用 于 初 始 化 后续 的 枚 举 器 。 以 下 例 子 定 义 了 一 个 枚 举 的 类 型 , 确 保 任 何 两 个 枚 举 器 可 用 OR 运算 符 组 合 在 一 起 :
enum FileOpenFlags
{
OpenReadOnly =1,
OpenReadWrite = OpenReadOnly << 1,
OpenBinary= OpenReadWrite << 1,
OpenText = OpenBinary<< 1,
OpenShareable = OpenTest << 1
};
在 这 个 例 子 中 , 上 面 的 枚 举 器 初 始 化 每 个 后 续 的 枚 举 器 。
转 换 和 枚 举 的 类 型
因 为 枚 举 的 类 型 是 整 型 , 所 以 任 何 枚 举 元 通 过 整 型 升 级 可 被 转 换 到 另 一 种 整 型 。考 虑 以 下 例 子 :
enum Days
{
Sunday,
Monday,
Tuesday,
Wed n esday,
Thursday,
Friday,
Saturday
};
int i;
Days d=Thursday;
i=d; // 通 过 整 型 升 级 进 行 转 换cout << "i=" << i << "\n;
但 是 从 任 何 整 型 到 一 个 枚 举 的 类 型 没 有 隐 式 的 转 换 。 因 此 ( 继 续 前 面 的 列 子 ), 以下 语 句 是 错 的 :
D=6; // 错 误 地 试 图 把 D 设 置 为 Saturday
象 这 样 的 赋 值 , 没 有 隐 含 的 转 换 存 在 的 地 方 , 必 须 使 用 造 型 转 换 : d=(Days)6; // 显 式 造 型 转 换 到 Days 类 型
d=Days(4); // 显 式 造 型 转 换 到 Days 类 型
前 面 的 例 子 显 示 了 与 枚 举 器 一 致 的 值 的 转 换 。 没 有 任 何 机 制 能 防 止 你 转 换 一 个不 与 任 一 个 枚 举 器 一 致 的 值 。 例 如 :
d=Days(967);
某 些 这 类 转 换 可 以 工 作 , 但 是 不 保 证 结 果 值 为 枚 举 器 之 一 。 此 外 , 如 果 枚 举 器 的尺 寸 太 小 , 不 足 以 保 持 正 被 转 换 的 值 , 则 存 储 的 值 可 能 不 是 你 所 希 望 的 。
连 接 规 格
术 语 “ 连 接 规 格 ” 指 以 不 同 语 言 书 写 的 连 接 函 数 ( 或 过 程 ) 的 协 议 。 以 下 调 用 规定 受 影 响 :
-
名 称 的 大 小 写 字 母 敏 感 性 。
-
名 称 的 修 饰 在 C 中 , 编 译 器 给 名 称 加 下 划 线 作 前 缀 。 这 通 常 被 称 为“ 修 饰 ”。 在 C++ 中 , 名 称 修 饰 被 用 于 通 过 连 接 阶 段 保 留 类 型 信 息 ( 参见 联 机 “ Microsoft Visual C++ 6.0 程 序 员 指 南 ” 中 的 “ 修 饰 的 名称 ” ) 。
-
在 堆 栈 上 的 所 期 望 的 参 量 的 顺 序 。
-
在 函 数 返 回 时 调 节 堆 栈 的 责 任 , 被 调 用 的 函 数 或 调 用 函 数 应 负 责 。
-
隐 藏 的 参 量 的 传 递 ( 任 隐 藏 的 参 量 是 否 传 递 ) 。
语 法
连 接 规 格
extern 字 符 串 文 字 { 说 明 表 opt }
extern 字 符 串 文 字 说 明说 明 表 :
说明
说 明 表
连 接 规 格 通 过 使 用 已 存 在 代 码 逐 步 促 进 C 代 码 向 C++ 的 移 植 。
Microsoft 特 殊 处 →
当 前 由 Microsoft C++ 支 持 的 唯 一 连 接 规 格 是 “ C ” 和 “ C++ ”。
Microsoft 特 殊 处 结 束
以 下 代 码 用 C 连 接 规 格 函 数 atoi 和 atol: extern "C"
{
int atoi(char *string);
long atol(char *string);
}
这 些 函 数 的 调 用 使 用 C 连 接 完 成 , 用 这 两 个 说 明 可 获 得 相 同 的 结 果 : extern "C" int atoi(char *string);
extern "C" long ato l (char *string);
Microsoft 特 殊 处 →
所 有 Microsoft C 标 准 包 含 文 件 使 用 条 件 编 译 命 令 去 检 测 C++ 编 译 , 当 检 测 到 一个 C++ 编 译 时 , 则 原 型 被 包 含 在 如 下 的 一 个 extern “ C ” 命 令 中 :
//sample.h
#if defined ( __ cpluslpus) extern "C"
{
#endif
// 函 数 说 明
if defined( __ cplusplus)
}
#endif
Microsoft 特 殊 处 结 束
你 不 需 要 在 标 准 包 含 文 件 中 将 函 数 说 明 为 extern “ C ”。
如 果 一 个 函 数 被 重 载 了 , 相 同 名 称 的 函 数 仅 有 一 个 可 有 连 接 指 示 符 ( 更 多 的 信 息
参 见 第 7 章 “ 说 明 符 ” 中 的 “ 函 数 重 载 ” ) 。表 6.3 给 出 了 各 种 连 接 规 格 是 如 何 工 作 的 。
表 6.3 连 接 规 格 的 作 用
规格 作用
在一个对象上 仅影响那个对象的连接
在一个函数上 影响那个函数和在其中说明的所有函数或对象的连接在一个类上 影响类中说明的所有非成员函数和对象的连接
如 果 一 个 函 数 有 不 止 一 个 的 连 接 规 格 , 它 们 必 须 一 致 。 将 函 数 说 明 为 具 有 C 和
C++ 两 种 连 接 是 错 误 的 , 而 且 , 如 果 一 个 函 数 的 两 个 说 明 出 现 在 一 个 程 序 中 , 一 个有 连 接 规 格 , 另 一 个 没 有 , 则 带 连 接 规 格 的 说 明 必 须 在 首 位 。 已 有 连 接 规 格 的 函数 的 任 何 冗 余 的 说 明 均 给 出 在 第 一 个 说 明 中 指 定 的 连 接 。 例 如 :
extern "C" int CFunc1();
...
int CFunc1();// 重 新 说 明 是 良 性 的 ,C 连 接 被 保 留int CFunc2();
....
extern "C" cfunc2(); // 错 误 : 不 是 CFunc2 的 第 一 个 说 明 , 不 能 包 含 连 接 指 示符 。
在 一 个 复 合 连 接 规 格 符 ({}) 的 体 内 被 显 式 地 说 明 为 static 的 函 数 和 对 象 被 看 成是 静 态 函 数 或 对 象 ; 连 接 规 格 被 忽 略 , 其 它 函 数 和 对 象 表 现 得 如 同 使 用 extern 关键 字 说 明 的 一 样 ( 关 于 extern 关 键 字 详 见 “ 存 储 类 说 明 符 ” ) 。
模 板 规 格
template 说 明 指 定 一 个 参 量 化 的 类 或 函 数 集 。
注 意 : 更 多 的 信 息 参 见 联 机 “ Microsoft Visual C++6.0 程 序 员 指 南 ” 中 的 “ 模板 论 题 ”。
语 法
模 板 规 格 :
template < 模 板 参 量 表 > 说 明模 板 参 量 表 :
模 板 参 量
模 板 参 量 表 , 模 板 参 量模 板 参 量 :
类 型 参 量
参 量 说 明类 型 参 量 :
class 标 识 符
typename 标 识 符
说 明 一 个 函 数 或 一 个 类 。 对 函 数 模 板 , 每 个 模 板 参 量 在 正 被 说 明 的 函 数 模 板 参 量表 中 至 少 出 现 一 次 。
模 板 参 量 表 是 由 模 板 函 数 使 用 的 参 量 表 , 指 示 以 下 代 码 的 哪 些 部 分 将 改 变 。例 如 :
template< class T,int i> class MyStack...
在 这 种 情 况 下 template 能 接 收 的 一 个 类 型 (class T) 和 一 个 常 数 参 量 (int I) 。template 将 使 用 类 型 T 和 常 量 整 数 i 于 构 造 函 数 。 在 MyStack 说 明 体 内 , 你 必须 指 出 T 标 识 符 。
tempname 关 键 字 可 被 用 在 模 板 参 量 表 中 , 以 下 template 说 明 是 完 全 相 同 的 : template<class T1,class T2> class X...
template<typename T1,typename T2> class X... template 参 量 的 以 下 形 式 是 允 许 的 :
template<typename type> class allocator{} ; template<typename type,
typename Allocator=allocator<Type>> class stack{
};
stack<int> MyStack;
Visual C++ 支 持 模 板 参 量 表 中 的 模 板 参 量 的 重 用 。 例 如 , 以 下 代 码 现 在 是 合 法的 :
class Y {...}
template<class T,T* pT> class X1 {...} template<class,T1,class T2=T1> class X2{...};
Y aY;
X1<Y,&aY>x1;
X2<int> x2;
- 个 模 板 说 明 自 身 不 产 生 代 码 , 它 指 定 类 或 函 数 的 一 个 家 族 , 当 被 其 它 代 码 引 用时 , 其 中 的 一 个 或 多 个 将 产 生 代 码 。
模 板 说 明 具 有 全 局 或 名 称 空 间 范 围 。
Visual C++ 执 行 模 板 定 义 的 语 法 检 查 。 Visual C++ 的 这 个 版 本 可 以 检 测 错 误 , 以前 的 版 本 不 能 。 编 译 器 现 在 能 检 测 定 义 了 的 但 从 未 实 例 化 的 模 板 的 语 法 错 误 。这 是 可 用 Visual C++4.0 编 译 器 编 译 的 常 见 错 误 清 单 , 但 不 是 用 Visual C++ 5.0 或 更 新 的 编 译 器 。
- 一 个 用 户 定 义 的 类 型 在 其 被 说 明 之 前 被 用 于 一 个 模 板 说 明 中 , 但 它是 在 第 一 次 实 例 化 或 使 用 模 板 之 前 被 说 明 的 。 例 如 :
template <class T> class X{
//...
Data m_data; //Visual C++5.0 或 之 后 的 版 本 中 , 数 据 未 定 义
};
class Data {...} void g(){X<int> x1;}
移 去 模 板 X 之 前 的 Data 说 明 以 解 决 这 个 问 题 。
- 一 个 成 员 函 数 在 一 个 类 模 板 外 面 被 说 明 , 而 从 未 在 类 内 部 被 说 明 。例 如 :
template<class T> class X{
// 在 这 里 没 有 说 明 mf
// 此 定 义 用 Visual C++4.0 时 不 产 生 错 误
// 但 用 Visual C++5.0 或 更 新 版 本 则 会 产 生 错 误template <class T>void x <T>::mf(){...};
- 一 个 类 标 识 符 被 当 作 是 一 个 普 通 类 , 除 非 说 明 为 一 个 类 模 板 。 例 如 : 以 下 代 码 用 Visual C++ 5.0 或 更 新 版 本 时 产 生 一 个 错 误 , 但用 Visual C++ 4.0 则 无 错 :
template <class T> class X{
friend class Y<T>; // 语 义 分 析 为 Y 小于 T 大 于
Z<T> mf(); // 语 义 分 析 为 Z 小 于 T 大 于
};
template<class T> class Y{...}; template<class T> class Z{...}; X<int> x;
为 解 决 此 问 题 , 在 X 定 义 之 前 前 向 说 明 Y 和 Z 。template<class T> class Y{...};
template<class T> class Z{. };
引 用 一 个 模 板
要 引 用 一 个 模 板 类 或 函 数 使 用 以 下 语 法 。
语 法
模 板 类 名 称 :
模板 < 模 板 参 量 表 > 模 板 参 量 表 :
模 板 参 量
模 板 参 量 表 , 模 板 参 量模 板 参 量 :
表 达 式
类 型 名 称
所 有 模 板 参 量 必 须 为 常 量 表 达 式 , 如 果 对 一 个 前 面 产 生 的 模 板 没 有 准 确 的 匹 配 , 则 编 译 器 创 建 模 板 类 或 函 数 的 一 个 新 的 实 例 ( 称 为 一 个 实 例 化 ), 例 如 :
MyStack< unsigned long ,5> stack1; // 创 建 一 个 unsigned long 的 堆 栈MyStack< DWORD, 5> stack2; // 使 用 上 面 创 建 的 代 码
MyStack< char, 6> stack3;// 产 生 新 代 码
Mystack< MyClass, 6> stack4; // 产 生 MyClass 对 象 的 堆 栈每 个 产 生 的 模 板 化 函 数 创 建 其 自 身 的 静 态 变 量 和 成 员 。
函 数 模 板
类 模 板 定 义 一 个 相 关 的 类 家 族 , 它 们 是 基 于 实 例 化 时 传 递 给 类 的 参 量 的 。 函 数 模板 与 类 模 板 类 似 , 但 定 义 一 个 函 数 族 。 这 里 是 一 个 交 换 两 项 的 函 数 模 板 : template<class T>void MySwap(T& a ,T& b)
{
T c;
c=a,a=b,b=c;
}
尽 管 此 函 数 可 以 通 过 一 个 非 模 板 化 函 数 执 行 , 使 用 void 指 针 , 模 板 版 本 是 类 型 安全 的 。 考 虑 以 下 调 用 :
int j=10; int k=18;
CString Hello ="Hello,Windows!"; MySwap(j,k); // 可 行MySwap(j,Hello); // 错 误
第 二 个 MySwap 调 用 触 发 了 一 个 编 译 错 误 , 因 为 编 译 器 不 能 用 不 同 类 型 的 参 量 产生 一 个 MySwap 函 数 。 如 果 使 用 了 void 指 针 , 则 两 个 函 数 调 用 都 能 正 确 编 译 , 但函 数 在 运 行 时 不 能 正 常 工 作 。
- 个 函 数 模 板 属 性 参 量 的 显 式 说 明 是 允 许 的 , 例 如 : template <class T> void f(T) {...}
void g(char j) {
f<int>(j); // 产 生 f(int) 规 格
}
当 模 板 参 量 被 显 式 说 明 时 , 正 常 的 隐 式 转 换 完 成 将 函 数 参 量 转 换 到 相 应 的 函 数 模板 参 量 的 类 型 。 在 上 面 例 子 中 , 编 译 器 将 把 (char j) 转 换 到 int 类 型 。
成 员 函 数 模 板
成 员 函 数 模 板 在 说 明 了 一 个 模 板 化 类 后 , 定 义 成 员 函 数 作 为 函 数 模 板 , 例如 : template<class T,int i> class MyStack
{
T* pStack;
T StackBuffer[i];
int cItems=i * sizeof(T); public:
MyStack(void);
void push(const T item);
T& pop(void);
};
template <class T,int i> MyStack<T,i>::MyStack(void)
{...};
template <class T,int i> void MyStack<T,i>::push(const T item)
{...};
template <class T,int t> T& MyStack<T,i>::pop(void)
{...};
注 意 , 未 包 括 模 板 参 量 的 构 造 函 数 的 定 义 列 出 了 两 次 。
显 式 实 例 化
显 式 实 例 化 让 你 能 够 创 建 一 模 板 化 类 或 函 数 的 一 个 实 例 , 而 不 需 要 在 你 的 代 码 中实 际 使 用 它 , 因 为 当 你 创 建 使 用 模 板 作 分 布 的 库 文 件 (.LIB) 时 这 是 很 有 用 的 , 非实 例 化 的 模 板 定 义 不 放 入 目 标 文 件 (.OBJ) 。
以 下 为 int 变 量 和 6 项 目 显 式 实 例 化 MyStack:
template class MyStack<int, 6>;
此 语 句 创 建 一 个 不 保 留 任 何 对 象 存 储 的 MyStack 的 一 个 实 例 , 代 码 是 为 所 有 成 员产 生 的 。
以 下 仅 显 式 实 例 化 构 造 成 员 函 数 :
template MyStack<int, 6>::MyStack(void);
Visual C++6.0 支 持 函 数 模 板 的 显 式 实 例 化 。 5.0 以 前 的 版 本 仅 支 持 类 模 板 显 式实 例 化 。 例 如 , 以 下 代 码 现 在 是 合 法 的 :
template <class T>void f(T){...}
// 用 显 式 指 定 的 模 板 参 量 “ int ” 实 例 化 f template void f<int>(int);
// 用 推 导 的 模 板 参 量 “ char ” 实 例 化 f
template void f(char);
Microsoft 特 殊 处 →
你 可 以 使 用 extern 关 键 字 去 阻 止 成 员 的 自 动 实 例 化 , 例 如 : extern template class MyStack<int, 6>;
类 似 地 , 你 可 以 标 注 指 定 的 成 员 为 外 部 的 且 不 象 下 面 这 样 实 例 化 : extern template MyStack<int, 6>::MyStack(void);
注 意 : 说 明 中 的 extern 关 键 字 仅 适 用 于 类 体 外 定 义 的 成 员 函 数 。 在 类 说 明 内 部定 义 的 函 数 被 认 为 是 联 编 函 数 , 且 总 是 实 例 化 的 。
Microsoft 特 殊 处 结 束与 其 它 实 现 的 不 同
Microsoft 特 殊 处 →
模 板 没 有 正 式 地 被 标 准 化 , 因 此 不 同 的 C++ 编 译 器 销 售 商 对 它 们 的 实 现 不 同 。 以下 清 单 给 出 了 本 版 本 的 Visual C++ 和 其 它 编 译 器 之 间 的 一 些 不 同 之 处 。 注 意 此清 单 在 以 后 版 本 的 编 译 器 中 还 会 有 所 改 变 。
-
编 译 器 不 能 在 其 所 定 义 的 模 块 外 面 实 例 化 一 个 模 板 。
-
模 板 不 能 与 用 __ declspec(dllimport) 或 __ declspec(dllexport) 说 明的 函 数 一 起 使 用 。
-
所 有 模 板 参 量 必 须 是 非 模 糊 类 型 的 , 准 确 与 模 板 参 量 表 相 匹 配 。 例如 :
template<class T>T check(T); template<class S>void watch(int (*)(S));
watch(check); // 错 误
编 译 器 应 该 以 int check(int) 形 式 实 例 化 check 模 板 化 函 数 , 但 不 能 继 续 这 样推 理 。
- 友 元 函 数 必 须 在 被 用 于 模 板 类 之 前 说 明 。 你 不 能 在 一 个 类 定 义 中 定义 一 个 友 元 函 数 。 这 是 因 为 友 元 函 数 可 以 是 一 个 模 板 函 数 , 可 能 会导 致 非 法 的 嵌 套 的 模 板 定 义 。
Microsoft 特 殊 处 结 束
名 称 空 间
C++ 语 言 提 供 一 个 单 个 全 局 名 称 空 间 。 这 可 能 导 致 全 局 名 称 冲 突 问 题 。 例 如 , 考虑 这 样 两 个 C++ 头 文 件 :
//one.h
char func(char); class String {...};
//somelib.h
class String {...};
有 了 这 些 定 义 , 则 不 可 能 在 一 个 单 个 程 序 中 同 时 使 用 这 两 个 头 文 件 ;String 类 会发 生 冲 突 。
一 个 名 称 空 间 是 一 个 说 明 性 区 域 , 为 在 其 内 说 明 的 任 何 名 称 添 加 一 个 额 外 的 标 识符 。 额 外 的 标 识 符 使 得 一 个 名 称 与 程 序 中 其 它 位 置 说 明 的 名 称 冲 突 的 可 能 性 减
少 , 有 可 能 在 分 开 的 名 称 之 间 使 用 相 同 的 名 称 而 没 有 冲 突 , 即 使 名 称 出 现 在 同 一个 转 换 单 元 中 , 只 要 它 们 出 现 在 分 开 的 名 称 空 间 中 , 每 个 名 称 将 是 唯 一 的 , 因 为 有额 外 的 名 称 空 间 标 识 符 。 例 如 :
//one.h namespace one
{
char func(char);
chass String {...};
}
//somelib.h namespace Somelib
{
class String {...}
}
现 在 类 名 称 将 不 会 冲 突 , 因 为 它 们 分 别 变 为 ong:String 和 Somelib::String 。在 所 有 名 称 空 间 之 外 , 在 一 个 转 换 单 元 的 文 件 范 围 中 的 说 明 还 是 全 局 名 称 空 间 的成 员 。
名 称 空 间 说 明
namespace 说 明 将 一 个 名 称 指 定 并 赋 值 到 一 个 说 明 性 区 域 。
语 法
源 名 称 空 间 名 称 :
标 识 符
名 称 空 间 定 义 :
源 名 称 空 间 定 义
扩 充 名 称 空 间 定 义
未 命 名 的 名 称 空 间 定 义源 名 称 空 间 定 义 :
namespace 标 识 符 { 名 称 空 间 体 } 扩 充 名 称 空 间 定 义 :
namespace 源 名 称 空 间 名 称 { 名 称 空 间 体 } 未 命 名 的 名 称 空 间 定 义 :
namespace { 名 称 空 间 体 } 名 称 空 间 体 :
说 明 序 列 opt
在 一 个 源 名 称 空 间 定 义 中 的 标 识 符 在 其 被 使 用 的 说 明 区 域 内 必 须 是 唯 一 的 。 标识 符 是 名 称 空 间 中 的 名 称 并 用 于 其 成 员 的 引 用 。 随 后 , 在 那 个 说 明 性 区 域 , 它 被看 作 是 一 个 源 名 称 空 间 名 称 。
-
个 名 称 空 间 定 义 的 说 明 性 区 域 是 它 的 名 称 空 间 体 。
-
个 名 称 空 间 可 以 包 含 数 据 和 函 数 说 明 , 说 明 序 列 是 这 些 被 说 成 是 这 些 名 称 空 间的 成 员 的 说 明 的 一 个 表 。
未 命 名 的 名 称 空 间
- 个 未 命 名 的 名 称 空 间 定 义 的 行 为 好 象 它 被 :
namespace unique { 名 称 空 间 体 } using mamespace unique ;
替 换 了 。
每 个 未 命 名 的 名 称 空 间 有 一 个 标 识 符 , 用 unique 表 示 , 与 整 个 程 序 中 的 所 有 其 它标 识 符 不 同 。 例 如 :
namespace {int i;} //unique::i void f() {i++;} //unique::i++
namespace A { namespace {
int I; //A::unique::i int j; //A::unique::j
}
}
using n amespace A; void h()
{
I++; // 错 误 :unique::i 或 A::unique::i
A::i++;// 错 误 :A::i 未 定 义
j++; //A::unique::j++
}
未 命 名 的 名 称 空 间 是 变 量 的 静 态 说 明 的 替 换 。 它 们 允 许 变 量 和 函 数 在 整 个 转 换单 元 中 可 见 , 在 外 部 不 可 见 。 尽 管 在 一 个 未 命 名 的 名 称 空 间 中 的 实 体 也 许 有 外 部连 接 , 但 它 们 被 一 个 唯 一 的 名 称 有 效 地 限 定 于 它 们 的 转 换 单 元 中 , 因 此 永 远 不 能在 任 何 其 它 转 换 单 元 中 可 见 。
名 称 空 间 定 义
一 个 名 称 空 间 定 义 可 被 嵌 套 在 另 一 个 名 称 空 间 定 义 内 , 每 个 名 称 空 间 定 义 必 须 出现 在 文 件 范 围 或 立 即 在 另 一 个 名 称 空 间 内 。
例 如 : namespace A {
int j=3;
int f(int k);
}
namespace Outer {
int n=6;
int func(int num);
namespace Inner {
flont f=9.993;
}
}
void main()
{
namespace local {...} // 错 误 : 不 在 全 局 范 围
.
.
.
}
不 象 其 它 说 明 性 区 域 , 一 个 名 称 空 间 的 定 义 可 被 分 在 一 个 单 独 转 换 单 元 的 几 个 部分 中 。
namespace A {
// 说 明 namespace A 变 量
int i;
int j;
}
namespace B {
...
}
.
.
. namespace A {
// 说 明 namespace A 函 数
void func(void);
int int_func(int i);
}
当 一 个 名 称 空 间 以 这 种 方 式 继 续 , 在 其 初 始 定 义 之 后 , 连 续 被 称 为 一 个 扩 充 名 称空 间 定 义 。
定 义 名 称 空 间 成 员
- 个 名 称 空 间 的 成 员 可 被 定 义 在 那 个 名 称 空 间 内 , 例 如
:
namespace X { void f(){} }
- 个 名 称 空 间 的 成 员 可 以 通 过 定 义 名 称 的 显 式 限 定 定 义 在 该 名 称 空 间 的 外 面 。但 是 正 被 定 义 的 实 体 必 须 已 在 名 称 空 间 中 说 明 了 , 此 外 , 定 义 必 须 出 现 在 包 含 说明 的 名 称 空 间 的 一 个 名 称 空 间 中 说 明 处 之 后 。 例 如 :
namespace Q{
namespace V{
void f();
}
void V::f(){} // 可 行
void V::g(){} // 错 误 ,g() 还 不 是 V 的 一 个 成 员
namespace V{
void g();
}
}
名 称 空 间 别 名
一 个 名 称 空 间 别 名 是 一 个 namespace 的 一 个 备 用 名 称 。
语 法
名 称 空 间 别 名 :
标 识 符
名 称 空 间 别 名 定 义 :
namespace 标 识 符 = 限 定 名 称 空 间 指 示 符 ; 限 定 名 称 空 间 指 示 符 :
:: opt 嵌 套 的 名 称 指 示 符 opt 类 或 名 称 空 间 名 称
- 个 名 称 空 间 别 名 定 义 为 一 个 名 称 空 间 说 明 了 一 个 备 用 名 。 标 识 符 是 限 定 名 称空 间 指 示 符 的 同 义 词 并 成 为 一 个 名 称 空 间 别 名 , 例如 :
namespace a_very_long_namespace_name {...} namespace AVLNN=a_very_long_namespace_name;
//AVLNN 现 在 是 a_very_long_n a mespace_name 的 一 个 名 称 空 间 别 名
- 个 名 称 空 间 名 称 不 能 与 同 一 说 明 性 区 域 中 任 何 其 它 实 体 完 全 相 同 , 此 外 , 一 个全 局 名 称 空 间 名 称 不 能 与 一 个 给 定 程 序 中 的 任 何 其 它 全 局 实 体 名 称 相 同 。
using 说 明
using 说 明 向 using 说 明 出 现 的 说 明 性 区 域 中 引 进 一 个 名 称 。 名 称 成 为 一 个 别的 地 方 说 明 的 一 个 实 体 的 同 义 词 , 它 允 许 一 个 特 定 的 名 称 空 间 的 单 个 名 称 不 用 显式 限 定 而 被 使 用 。
这 与 using 命 令 相 反 , 那 个 允 许 一 个 名 称 空 间 中 的 所 有 名 称 不 用 限 制 而 被 使 用 。更 多 的 信 息 参 见 下 节 “ Using 命 令 ”。
语 法
using 说 明 :
using:: opt 嵌 套 的 名 称 指 示 符 非 限 定 的 id
using:: 非 限 定 的 id
- 个 using 说 明 可 用 于 一 个 类 定 义 中 , 例如 : class B
{
void f(char);
void g(char);
};
class D:B
using B::f;
void f(int) {f('c');} // 调 用 B::f(char)
void g(int) {g('c');} // 递 归 地 调 用 D::g(int)
// 仅 B::f 正 被 使 用
};
当 被 用 于 说 明 一 个 成 员 , 一 个 using 说 明 必 须 指 向 一 个 基 类 的 成 员 。 例 如 : class C
{
int g();
};
class D2:public B
{
using B::f; // 可行 :B 是 D2 的 基 ( 类 )
using C::g; // 错误 :C 不 是 D2 的基 ( 类 )
};
使 用 using 说 明 所 说 明 的 成 员 可 以 用 显 示 限 定 来 引 用 。 :: 前 缀 指 向 全 局 名 称 空间 。 例 如 :
void f(); namespace A
void g();
}
namesapce X
{
using::f;// 全 局 f
using A::g; //A 的 g
}
void h()
{
X::f();// 调 用 ::f
X::g();// 调 用 A::f
}
就 如 同 用 任 何 说 明 一 样 , 一 个 using 说 明 仅 在 多 重 说 明 允 许 的 地 方 可 被 重 复 性 使用 。
namespace A
{
int i;
}
void f()
using A::i;
using A::i; // 可行 : 重 复 说 明
}
class B
{
protected:
int i:
};
class X:public B
{
public:
using B::i;
using B::i; // 错误 : 类 成 员 不 能 被 多 次 说 明
};
当 作 了 一 个 using 说 明 时 , 由 说 明 创 建 的 同 义 词 仅 指 示 在 using 说 明 点 有 效 的 定义 , 在 using 说 明 之 后 加 到 一 个 名 称 空 间 的 定 义 是 无 效 的 同 义 词 。 例 如 : namespace A
{
void f(int);
using A::f; //f 仅 是 A::f(int) 的 同 义 词
namespace A
{
void f(char);
}
void f()
{
f('a'); // 指 向 A::f(int), 尽 管 A::f(char) 存 在
}
void b()
{
using A::f; // 指 A::f(int) AND A::f(char)
f('a'); // 调 用 A::f(char);
}
由 一 个 using 说 明 定 义 的 一 个 名 称 是 其 源 名 称 的 一 个 别 名 。 它 不 影 响 源 说 明 的类 型 、 连 接 和 其 它 属 性 。
如 果 一 个 单 个 名 称 的 一 组 局 部 说 明 和 using 说 明 在 一 个 说 明 性 区 域 中 给 出 , 则 它
namespace B
{
int i;
void f(int);
void f(double);
}
void g()
{
int i;
using B::i; // 错误 :i 说 明 了 两 次
void f(char);
using B::f; // 可行 : 每 个 f 是 一 个 函 数
}
在 上 面 的 例 子 中 ,using B::i 语 句 导 致 int i 在 g() 函 数 中 被 第 二 次 说 明 ,using B::f 语 句 与 f(char) 函 数 不 冲 突 , 因 为 B::f 引 进 的 函 数 各 具 有 不 同 的 参 数 类 型 。一 个 局 部 函 数 说 明 不 能 与 由 using 说 明 引 进 的 函 数 具 有 相 同 的 名 称 和 类 型 。 例如 :
namespace B
{
void f(int);
void f(double);
}
namespace C
{
void f(int);
void f(double);
void f(char);
}
void h()
{
using B::f; // 引进 B::f(int) 和 B::f(double)
using C::f; //C::f(int),C::f(double) 和 C::f(char)
f('h'); // 调 用 C::f(char)
f(1);// 错 误 ; 模 糊 的 :B::f(int) 还 是 C::f(int)?
void f(int); // 错误 : 与 B::f(int) 和 C::f(int) 冲突
}
当 一 个 using 说 明 从 一 个 基 类 向 一 个 派 生 类 范 围 引 进 一 个 名 称 , 在 派 生 类 中 的 成员 函 数 使 在 基 类 中 具 有 相 同 名 称 和 参 量 类 型 的 虚 拟 成 员 函 数 无 效 。 例 如 : struct B
{
virtual void f(int);
virtual void f(char);
void g(int);
void h(int);
};
struct D:B
{
using B::f;
void f(int); // 可 行 :D::f(int) 使 B::f(int) 无 效
using B::g;
void g(char); // 可 行 : 没 有 B::g(char)
using B::h;
void h(int); // 错 误 : D::h(int) 与 B::h(int) 冲 突
//B::h(int) 不 是 虚 拟 的
};
void f(D* pd)
{
pd->f(1); // 调 用 D::f(int)
pd->f('a') // 调 用 B::f(char)
pd->g(1) // 调 用 B::g(int)
pd->g('a') // 调 用 D::g(char)
}
在 一 个 using 说 明 中 提 到 的 一 个 名 称 的 所 有 实 例 必 须 是 可 访 问 的 。 特 别 地 , 如 果一 个 派 生 类 使 用 一 个 using 说 明 去 访 问 一 个 基 类 的 一 个 成 员 , 则 成 员 名 称 必 须 是可 访 问 的 。 如 果 名 称 是 一 个 重 载 了 的 成 员 函 数 , 则 所 有 命 令 的 函 数 必 须 是 可 访 问的 。 例 如 :
class A
{
private:
void f(char); public:
void f(int); protected:
void g();
};
class B : public A
{
using A::f; // 错 误 :A::f(char) 是 不 可 访 问 的public:
using A::g; //B::g 是 A::g 的 一 个 公 共 同 义 词
};
关 于 成 员 的 访 问 性 的 更 多 信 息 参 见 第 10 章 “ 成 员 访 问 控 制 ”。
using 命 令
using 命 令 允 许 一 个 namespace 中 的 名 称 在 使 用 时 不 用 把 名 称 空 间 名 称 作 为 一个 显 式 限 定 符 。与 using 说 明 相 反 , 那 是 允 许 一 个 单 个 的 名 称 不 用 限 定 而 被 使 用 , 而 using 命 令 允 许 一 个 名 称 空 间 中 的 所 有 名 称 不 用 限 定 而 被 使 用 。 更 多 的 信 息参 见 本 章 前 面 的 “ using 说 明 ”。
语 法
using 命 令 :
using namespace:: opt 嵌 套 的 名 称 指 示 符 opt 名 称 空 间 名 称
using 命 令 的 意 图 是 在 说 明 函 数 和 变 量 时 允 许 使 用 唯 一 的 、 说 明 性 的 名 称 , 每 次访 问 函 数 或 变 量 时 不 要 求 用 完 整 的 名 称 。 当 然 , 完 整 的 限 定 名 还 是 可 用 于 保 持 清晰 度 。 非 限 定 名 可 从 using 命 令 打 开 的 点 后 被 使 用 。 如 果 一 个 名 称 空 间 在 一 个using 命 令 给 定 后 被 扩 充 , 则 名 称 空 间 附 加 的 成 员 可 被 使 用 , 且 在 扩 充 的 名 称 空间 定 义 之 后 , 不 用 限 定 。 例 如 :
namespace M
{
int i;
}
using namespace M;
namespace N
{
int j;
double f() { return M::d } // 错 误 ;M::d 还 不 存 在
}
namespace M //namespace 扩 充
{
double d;
} // 现 在 M::d 可 使 用 了
对 一 个 using 命 令 , 当 被 用 于 另 一 个 名 称 空 间 时 有 可 能 引 进 冲 突 的 名 称 。 例 如 : namespace M
{
int i;
}
namespace N
{
int i;
using namespace M; // 这 里 没 错
}
.
.
. void f()
{
using namespace N;
i=7;// 错 误 : 模 糊 的 :M::i 还是 N::i?
}
在 此 例 中 , 将 M::i 引 进 namespace N 并 没 有 隐 藏 N::i 说 明 , 而 是 在 使 用 N::i 时产 生 了 模 糊 性 , 在 这 种 情 况 下 ,using 命 令 很 容 易 引 入 不 想 要 的 模 糊 性 。 考 虑 下面 代 码 段 :
namespace D
{
int d1;
void f(int);
void f(char);
}
using namespace D;
int d1; // 与 D::d1 不 冲 突
namespace E
{
int e;
void f(int);
}
namespace D// 名 称 空 间 扩 充
{
int d2;
using namespace E;
void f(int);
}
void f()
{
d1++; // 错 误 : 模 糊 的 : ::dl 还 是 D::d1?
::d1++; // 可 行
D::d1++; // 可 行
d2++; // 可 行 :D::d2
e++; // 可 行 :E::e
f(1); // 错 误 : 模 糊 的 :D::F(int) 还 是 E::f(int)?
f('a'); // 可 行 :D::f(char)
}
当 一 个 变 量 在 using 命 令 后 被 引 用 时 , 相 同 名 称 的 局 部 变 量 比 在 指 定 的 名 称 空 间中 说 明 的 那 个 具 有 更 高 的 优 先 级 。 例 如 :
namespace N{
int data=4;
}
void f(bool flag){
int data=0;
if(flag){
using namespace N;
printf("data=%d\n",data);
}
}
void main(){
f(true);
}
在 上 面 代 码 中 , 在 printf 语 句 中 引 用 的 data 变 量 是 一 个 局 部 变 量 , 初 始 化 为 0,
替 代 在 名 称 空 间 N 中 初 始 化 的 变 量 。 输 出 是 data=0 而 不 是 data=4 。using 命 令 名 称 空 间 的 出 现 , 在 下 面 的 例 子 中 给 出 了 限 定 名 查 找 的 方 法 : namespace A{
int flag=0;
}
namespace B{
using namespace A;
}
namespace C{
using namespace A;
using namespace B;
}
void main(){
printf("C::flag=%d\n",C::flag);
}
由 于 在 名 称 空 间 C 中 的 名 称 空 间 using 命 令 使 C::flag 限 定 名 为 A::flag 。
显 式 限 定 符
在 一 个 类 或 名 称 空 间 中 的 名 称 可 通 过 使 用 一 个 显 式 限 定 符 进 行 访 问 。
语 法
id 表 达 式 :
未 限 定 的 id
限 定 的 id
嵌 套 的 名 称 指 示 符 :
类 或 名 称 空 间 名 称
:: 嵌 套 的 名 称 指 示 符 opt
类 或 名 称 空 间 名 称 :
类名
名 称 空 间 名 称名 称 空 间 名 称 :
源 名 称 空 间 名 称
名 称 空 间 别 名
这 与 使 用 范 围 运 算 符 去 解 决 一 个 类 成 员 的 访 问 是 非 常 相 似 的 。 更 多 的 信 息 , 参 见第 4 章 “ 表 达 式 ” 中 的 限 定 名 。