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 ))

给 定 。 由 表 达 式 产 生 的 地 址 不 是 距 离 地 址 e1e2 字 节 , 而 是 地 址 被 定 位 以 产 生数 组 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 [ indexindex [ 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, 包 含 表 示 xy 坐 标 的 私 有 数 据 对 象 , 这些 数 据 对 象 必 须 被 修 改 且 它 们 的 值 被 检 索 。 该 程 序 只 是 这 样 的 一 个 类 的 几 种 设计 之 一 , 使 用 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- nametype - 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 或

  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 表 达 式 求 值 顺 序

图 4.1 中 给 出 的 表 达 式 求 值 顺 序 由 运 算 符 的 优 先 级 和 结 合 律 确 定 :

  1. 在 这 个 表 达 式 中 乘 法 (*) 具 有 最 高 优 先 级 , 因 此 ,b*c 子 表 达 式 首 先 被 求 值 。

  2. 加 法 (+) 具 有 次 级 优 先 级 , 所 以 a 被 加 到 b 和 c 的 积 中 。

  3. 左 移 (<<) 在 表 达 式 中 具 有 最 低 优 先 级 , 但 有 两 次

    出 现 , 由 于 左 移 运 算 符 从 左 到右 分 组 , 左 子 表 达 式 首 先 被 求 值 , 而 后 是 右 子 表 达 式 。

当 括 号 被 用 于 分 组 子 表 达 式 时 , 它 们 改 变 表 达 式 被 求 值 的 优 先 级 以 及 求 值 顺 序 , 如 图 4.2 所 示 。

第 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

图 4.3 类 层 次

  • 个 类 型 C 的 对 象 , 如 图 4.4 中 所 示 被 可 视 化 。

第 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 中 给 出 的 类 层 次 :

图 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 所 示 的 类 层 次 :

图 4.6 类 层 次 显 示 虚 拟 基 类

在 此 层 次 中 ,A 是 一 个 虚 拟 基 类 , 关 于 一 个 虚 拟 基 类 的 定 义 见 第 9 章 “ 派 生 类 ” 中 的 “ 虚 拟 基 类 ”。 给 定 E 类 的 一 个 实 例 及 指 向 A 子 对 象 的 一 个 指 针 , 转 到 指 向B 的 指 针 的 dynamic_cast 将 因 为 模 糊 性 而 失 败 。 你 必 须 先 造 型 回 到 完 整 的 E 对象 , 然 后 沿 层 次 向 上 , 以 一 种 非 模 糊 的 方 式 到 达 正 确 的 B 对 象 。

考 虑 图 4.7 所 示 的 类 层 次 。

第 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 。执 行 过 程 如 下 :

  1. 控 制 通 过 正 常 的 顺 序 执 行 到 达 try 语句 , 保 护 段 ( 在 try 块内 ) 被 执 行 。

  2. 如 果 在 保 护 段 执 行 期 间 没 有 引 起 异 常 , 跟 在 try 块 后 的

    catch 子 句 不 执 行 。从 异 常 被 丢 弃 的 try 块 后 跟 随 的 最 后 一 个 catch 子 句 后 面 的 语 句 继 续 执 行 下 去 。3. 如 果 在 保 护 段 执 行 期 间 或 在 保 护 段 调 用 的 任 何 例 行 程 序 中 ( 直 接 或 间 接 的 调用 ) 有 异 常 被 丢 弃 , 则 从 通 过 throw 操 作 数 创 建 的 对 象 中 创 建 一 个 异 常 对 象 ( 这 隐含 指 可 能 包 含 一 个 拷 贝 构 造 函 数 ) 。 在 此 点 , 编 译 器 在 能 够 处 理 丢 弃 类 型 的 异 常的 更 高 执 行 上 下 文 中 寻 找 一 个 catch 子 句 ( 或 一 个 能 处 理 任 何 类 型 异 常 的 catch 处 理 器 ) 。 catch 处 理 程 序 按 其 在 try 块 后 出 现 的 顺 序 被 检 查 。 如 果 没 有 找 到 合适 的 处 理 器 , 则 下 一 个 动 态 封 闭 的 try 块 被 检 查 。 此 处 理 继 续 下 去 直 到 最 外 层 封闭 try 块 被 检 查 完 。

  1. 如 果 匹 配 的 处 理 器 未 找 到 , 或 如 果 在 不 自 动 分 行 时 出 现 异 常 , 但 在 处 理 器 得

到 控 制 之 前 预 定 义 的 运 行 函 数 terminate 被 调 用 。 如 果 一 个 异 常 发 生 在 丢 弃 异常 之 后 , 则 在 循 环 展 开 开 始 之 前 调 用 terminate 。

  1. 如 果 一 个 匹 配 的 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 章 “ 表 达 式 ” 中 的 限 定 名 。