7 章 说 明 符

一 个 “ 说 明 符 ” 是 一 个 说 明 的 一 部 分 , 用 以 命 名 一 个 对 象 、 类 型 或 函 数 。 说 明 符是 出 现 在 说 明 中 的 以 一 个 或 多 个 被 逗 号 分 开 的 名 称 , 每 个 名 称 可 有 一 个 相 关 的 初始 化 器 。

语 法

说 明 符 表 :

初 始 说 明 符

说 明 符 表 , 初 始 说 明 符初 始 说 明 符 :

说 明 符 初 始 化 器 opt

本 章 包 括 以 下 主 题 :

  • 说 明 符 概 述

  • 类 型 名 称

  • 抽 象 说 明 符

  • 函 数 定 义

  • 初 始 化 器

说 明 符 概 述

说 明 符 是 指 定 名 称 的 一 个 说 明 的 组 成 成 分 。 说 明 符 还 可 修 饰 基 本 的 类 型信 息 , 使 得 名 称 成 为 函 数 或 者 对 象 或 函 数 的 指 针 ( 在 第 6 章 “ 说 明 ” 中 讨论 的 指 示 符 传 送 诸 如 类 型 和 存 储 类 属 性 。 在 本 章 以 及 附 录 B “ M icrosoft 特 殊 修 饰 符 ” 中 讨 论 的 修 饰 符 用 以 修 饰 说 明 符 )。

图 7.1 给 出 了 两 个 名 称 szBuf 和 strcpy 的 一 个 完 整 说 明 , 并 提 出 了 说 明 的各 组 成 部 件 。

第 7 章 说 明 符 - 图1

图 7.1 指 示 符 、 修 饰 符 和 说 明 符

Microsoft 特 殊 处 →

大 多 数 Microsoft 扩 充 关 键 字 可 被 用 作 形 成 派 生 类 型 的 修 饰 符 ; 它 们 不 是 指 示 符或 说 明 符 ( 见 附 录 B “ Microsoft 特 殊 修 饰 符 ” ) 。

Microsoft 特 殊 处 结 束语 法

说 明 符 :

dname

ptr 运 算 符 说 明 符

说 明 符 ( 参 量 说 明 表 ) cv 修 饰 符 表

说 明 符 [ 常 量 表 达 式 opt]

( 说 明 符 )

ptr 运 算 符 :

* cv 限 定 符 表 opt

& cv 限 定 符 表 opt

完 整 的 类 名 称 ::* cv 限 定 符 表 opt

cv 限 定 符 表 :

cv 限 定 符 cv 限 定 符 表 opt

cv 限 定 符 :

const

volatile cv 修 饰 符 表 :

cv 限 定 符 cv 修 饰 符 表 opt

pmodel cv 修 饰 符 表 opt

dname:

名称

类 名 称

~ 类 名 称

typedef 名 称

限 定 类 型 名 称

说 明 符 出 现 在 说 明 语 法 中 可 选 的 指 示 符 表 ( decl-specifiers) 后 面 , 这 些 指 示 符 在 第

6 章 “ 说 明 ” 中 讨 论 。 一 个 说 明 可 包 含 不 止 一 个 说 明 符 , 但 每 个 说 明 符 仅 说 明 一个 名 称 。 以 下 说 明 样 本 显 示 了 指 示 符 和 说 明 符 是 如 何 组 合 构 成 一 个 完 整 的 说 明的 :

const char *pch,ch;

在 前 面 这 个 说 明 中 , 关 键 字 const 和 char 组 成 了 指 示 符 表 。 列 出 了 两 个 说 明 符

*pch 和 ch 。

然 后 一 个 说 明 简 化 的 语 法 如 下 , 其 中 const char 是 类 型 ,*pch 和 ch 是 说 明 符 : type declarator 1 [,declarator 2 [...,declaratorn ]];

当 联 结 于 一 个 说 明 符 表 的 元 素 没 有 产 生 想 要 的 结 果 , 你 可 以 使 用 括 号 使 其 表 达 清晰 。 但 一 个 更 好 的 技 术 是 使 用 typedef 或 括 号 组 合 和 typedef 关 键 字 。

考 虑 说 明 一 个 函 数 的 指 针 数 组 。 每 个 函 数 必 须 遵 从 相 同 的 协 议 , 从 而 参 量 和 返 回值 是 已 知 的 :

// 函 数 返 回 类 型 int, 带 一 个 char * 类 型 参 量typedef int(*PIFN)(char*);

// 说 明 一 个 7 个 函 数 指 针 的 数 组 , 返 回 int, 带 一 个 char* 类 型 参 量PIFN pifnDispatchArray[7];

等 价 的 说 明 可 以 不 用 typedef 说 明 书 写 , 但 它 太 复 杂 了 , 而 使 得 错 误 的 潜 在 性 超出 了 它 的 任 何 效 益 :

int (*pifnDispatchArray[7](char*);

类 型 名 称

类 型 名 称 按 以 下 方 式 被 用 于 某 些 说 明 符 中 :

  • 在 显 式 转 换 中

  • 作 为 sizeof 运 算 符 的 参 量

  • 作 为 new 运 算 符 的 参 量

  • 在 函 数 原 型 中

  • 在 typedef 语 句 中

  • 个 类 型 名 称 构 成 类 型 指 示 符 , 如 第 6 章 “ 说 明 ” 中 所 描

    述 以 及 下 节 “ 抽 象 说 明符 ” 所 讨 论 的 。

在 以 下 例 子 中 , 函数 strcpy 的 参 量 通 过 使 用 它 们 的 类 型 名 而 提 供 。 在 source 参量 情 况 中 ,const char 是 指 示 符 ,* 是 抽 象 说 明 符 :

static char *szBuf, *strcpy(char *dest, const char *source);

语 法

类 型 名 称 :

类 型 指 示 符 表 抽 象 说 明 符 opt

类 型 指 示 符 表 :

类 型 指 示 符 类 型 指 示 符 表 opt

抽 象 说 明 符 :

ptr 运 算 符 抽 象 说 明 符 opt

抽 象 说 明 符 opt ( 参 量 说 明 表 ) cv 限 定 符 表 opt

抽 象 说 明 符 opt [ 常 量 表 达 式 opt]

( 抽 象 说 明 符 )

抽 象 说 明 符

  • 个 抽 象 说 明 符 是 一 个 说 明 符 , 其 中 省 略 了 标 识 符 ( 相 关 的 信 息 参 见 前 一 节 “ 类型 名 称 ” ) 。

本 节 讨 论 以 下 抽 象 说 明 符 :

  • 指 针

  • 引 用

  • 成 员 指 针

  • 数 组

  • 函 数

  • 缺 省 参 量

  • 个 抽 象 说 明 符 是 一 个 不 说 明 一 个 名 称 的 说 明 符 , 即 标

    识 符 被 省 略 。 例 如 : char *

说 明 类 型 “ 指 向 char 类 型 的 指 针 ”。 这 个 抽 象 说 明 符 可 被 用 于 如 下 所 示 的 一 个函 数 原 型 中 :

char *strcmp(char *, char *);

在 此 原 型 ( 说明 ) 中 , 函 数 的 参 量 被 指 定 为 抽 象 说 明 符 。 以 下 是 一 个 更 复 杂 的 抽 象说 明 符 , 说 明 “ 指 向 一 个 带 两 个 参 量 , 且 都 是 char* 类 型 的 函 数 的 指 针 ” , 返 回 类型 char *:

char *(*)(char*, char*)

由 于 抽 象 说 明 符 完 全 地 说 明 了 一 个 类 型 , 所 以 构 成 下 面 形 式 的 表 达 式 是 合 法 的 :

// 获 取 一 个 char 类 型 的 10 个 指 针 的 数 组 的 尺 寸size_t nSize=sizeof(char *[10]);

// 分 配 一 个 指 向 没 有 返 回 值 且 不 带 参 量 的 函 数 的 指 针typedef void(PVFN *)();

PVFN *pvfn=new PVFN;

// 分 配 一 个 返 回 类 型 为 WinStatus 且 带 一 个 WinHandle 类 型 的 参 量 的

// 函 数 的 指 针 数 组

typedef WinStatus (PWSWHFN *)(WinHandle); PWSWHFN pwswhfnArray[]=new PWSWHFN[10];

模 糊 性 解 决 方 法

要 执 行 从 一 个 类 型 到 另 一 个 类 型 的 显 式 转 换 , 你 必 须 使 用 强 制 转 换 , 指 出 所 希 望的 类 型 名 称 。 有 些 类 型 造 型 转 换 导 致 语 法 模 糊 性 , 以 下 函 数 形 式 类 型 强 制 转 换 是模 糊 的 :

char *aName(String(s));

不 清 楚 它 是 一 个 函 数 说 明 , 还 是 一 个 将 函 数 形 式 强 制 转 换 作 为 初 始 化 器 的 一 个 对象 说 明 : 它 可 能 说 明 一 个 返 回 类 型 为 char*, 且 带 一 个 String 类 型 参 量 函 数 , 或是 可 能 说 明 对 象 aName, 并 用 s 造 型 转 换 到 类 型 string 的 值 对 其 进 行 初 始 化 。 如 果 一 个 说 明 可 被 认 为 是 一 个 有 效 的 函 数 说 明 , 则 它 被 这 样 对 待 。 仅 当 它 不 可 能

是 一 个 函 数 说 明 — — 即 , 如 果 它 是 语 法 错 误 的 — — 是 一 个 语 句 检 测 它 是 否 是 一 个函 数 形 式 类 型 造 型 转 换 。 因 此 , 编 译 器 把 语 句 作 为 一 个 函 数 的 说 明 , 并 忽 略 标 识符 S 周 围 的 括 号 。 另 一 方 面 , 语 句 :

char *aName((String)s); 和

char *aName=String(s);

是 对 象 的 清 楚 的 说 明 , 且 一 个 用 户 定 义 的 从 String 类 型 到 char* 类 型 的 转 换 被调 用 以 执 行 aName 的 初 始 化 。

指 针

指 针 是 使 用 说 明 符 语 法 进 行 说 明 的 :

*cv 限 定 符 表 opt d name

一 个 指 针 拥 有 一 个 对 象 的 地 址 。 那 么 一 个 完 全 的 说 明 是 : 说 明 指 示 符 *cv 限 定 符 表 opt dname;

这 个 说 明 的 一 个 简 单 例 子 为 :

char *pch;

上 面 的 说 明 指 定 pch 指 向 一 个 char 类 型 的 对 象 。

const 和 volatile 指 针

const 和 volatile 关 键 字 改 变 指 针 是 如 何 被 对 待 的 。 const 关 键 字 指 定 指 针 在初 始 化 之 后 不 能 被 修 改 , 指 针 从 那 以 后 是 被 保 护 的 , 不 能 被 修 改 。

volatile 关 键 字 指 定 其 后 的 名 称 相 关 的 值 可 以 通 过 不 是 用 户 应 用 中 的 那 些 动 作

而 进 行 修 改 , 因此 ,volatile 关 键 字 对 在 可 被 多 个 过 程 访 问 的 共 享 存 储 器 中 或 与中 断 服 务 例 程 通 讯 用 的 全 局 数 据 区 域 中 说 明 对 象 是 非 常 有 用 的 。

当 一 个 名 称 被 说 明 为 volatile 时 , 编 译 器 在 它 每 次 被 程 序 访 问 时 从 存 储 器 中 重新 装 入 其 值 , 这 极 大 地 降 低 了 可 能 的 优 化 。 但 是 当 一 个 对 象 的 状 态 可 能 出 乎 意 料地 改 变 时 , 它 是 确 保 程 序 可 预 言 的 执 行 的 唯 一 方 法 。

要 说 明 由 const 或 volatile 指 针 指 向 的 对 象 , 使 用 这 种 形 式 的 说 明 : const char *cpch;

volatile char *vpch;

要 说 明 指 针 值 , 即 指 针 中 存 储 的 实 际 地 址 为 const 或 volatile, 使 用 这 种 形 式 的说 明 :

char * const pchc; char * volatile pchv;

C++ 语 言 不 允 许 对 声 明 为 const 的 指 针 或 对 象 的 修 改 赋 值 , 这 种 赋 值 将 去 除 对 象或 指 针 被 说 明 时 用 的 信 息 , 因 此 与 源 说 明 的 意 图 相 冲 突 , 考 虑 以 下 说 明 :

const char cch='A'; charch='B';

给 定 前 面 的 两 个 对 象 的 说 明 (cch 为 const char 类型 ,ch 为 char 类 型 ), 则 以 下说 明 / 初 始 化 是 有 效 的 :

const char *pch1=&cch;

const char *const pch4=&cch; const char *pch5=&ch;

char *pch6=&ch;

char *constpch7=&ch; const char *const pch8=&ch; 以 下 说 明 / 初 始 化 是 错 误 的 :

char *pch2=&cch; // 错 误 char *const pch3=&cch; // 错 误

pch2 的 说 明 通 过 一 个 可 能 被 修 改 而 因 此 是 不 允 许 的 常 量 对 象 说 明 了 一 个 指 针 。pch3 的 说 明 指 定 指 针 是 常 量 , 而 不 是 对 象 , 此 说 明 以 与 pch2 说 明 不 被 允 许 相 同的 理 由 而 不 被 允 许 。

以 下 8 个 赋 值 显 示 了 通 过 指 针 的 赋 值 和 对 前 面 说 明 改 变 指 针 值 的 情 况 ; 现 在 , 假定 从 pch1 到 pch8 的 初 始 化 是 正 确 的 :

*pch1='A'; // 错 误 : 对 象 说 明 为 常 量pch1=&ch; // 可 行 : 指 针 未 被 说 明 为 常 量

*pch2='A'; // 可 行 : 正 常 的 指 针pch2=&ch; // 可 行 : 正 常 的 指 针

*pch3='A'; // 可 行 : 对 象 未 被 说 明 为 常 量pch3=&ch; // 错 误 : 指 针 说 明 为 常 量

*pch4='A'; // 错 误 : 对 象 说 明 为 常 量pch4=&ch; // 错 误 : 指 针 说 明 为 常 量

说 明 为 volatile 或 const 和 volatile 混 合 的 指 针 遵 从 相 同 的 规 则 。 指 向 const 对 象 的 指 针 常 被 用 于 如 下 的 函 数 说 明 中 :

char *strcpy(char *szTarget, const char *szSource);

前 面 的 语 句 说 明 了 一 个 函 数 strcpy, 带 两 个 类 型 为 “ char 指 针 ” 的 参 量 , 且 返 回

一 个 指 向 char 类 型 的 指 针 。 由 于 参 量 是 由 引 用 传 递 的 , 而 不 是 通 过 值 传 递 的 , 如果 szSource 没 有 被 说 明 为 const, 则 函 数 可 自 由 修 改 szTarget 和 szSource 。 把szSource 说 明 为 const 确 保 调 用 者 szSource 不 能 被 调 用 的 函 数 改 变 。

注 意 : 由 于 存 在 一 个 从 typenam e* 到 const typenam e* 的 标 准 转 换 , 因 此 向 strcpy 传 递 一 个 类 型 char* 的 参 量 是 合 法 的 。 但 是 , 反 之 不 然 , 不 存 在 隐 式 的 转 换 从 一个 对 象 或 指 针 去 除 const 属 性 。

给 定 类 型 的 const 指 针 可 被 赋 给 相 同 类 型 的 一 个 指 针 。 但 是 , 一 个 不 是 const 的指 针 不 能 赋 给 一 个 const 指 针 , 以 下 代 码 给 出 了 正 确 和 错 误 的 赋 值 :

int *const cpObject=0; int *pObject;

void main()

{

pObject=cpObject; // 可 行

cpObject=pObject; // 错 误

}

引 用

引 用 使 用 说 明 符 语 法 进 行 说 明 :

语 法

& cv 限 定 符 表 opt d name

一 个 引 用 拥 有 一 个 对 象 的 地 址 , 但 语 法 上 表 现 得 象 一 个 对 象 。 一 个 引 用 说 明 包 括

  • 个 ( 可 选 的 ) 指 定 符 表 , 后 跟 一 个 引 用 说 明 符 。

语 法

说 明 指 示 符 &cv 限 定 符 表 opt dname;

考 虑 用 户 定 义 的 类 型 Date:

struct Date

{

short DayOfWeek;

short Month;

short Day;

short Year;

};

以 下 语 句 说 明 一 个 Date 类 型 对 象 和 那 个 对 象 的 一 个 引 用 : Date Today; // 说 明 对 象

Date& TodayRef=Today;// 说 明 引 用

对 象 名 称 Today 和 对 象 的 引 用 TodayRef 在 程 序 中 可 完 全 相 同 地 被 使 用 : Today.DayOfWeek=3; //Tuesday

TodayRef.Month=7;//July

引 用 类 型 函 数 参 量

通 常 向 一 个 大 对 象 传 递 引 用 比 传 递 函 数 更 有 效 。 这 允 许 编 译 器 在 保 持 已 被 用 于访 问 对 象 的 语 法 时 传 递 对 象 的 地 址 。 考 虑 下 面 使 用 Date 结 构 的 例 子 :

// 从阳历 ( 格里历 ) 日期创建一个 DDDYYYY 形式的 Julian 日期

long JulianFromGregorian(Date& GDate)

{

static int cDaysInMonth[]={

31,28,31,30,31,30,31,31,30,31,30,31

};

long JDate;

// 为 已 经 过 去 的 月 份 加 天 数

for (int i=0;i<GDate.Month-1;++i)

JDate+=cDaysInMonth[i];

// 为 本 月 加 天 数

JDate+=GDate.Day;

// 检 查 闰 年

if (GDate.year%100!=0 && GDate.Year%4==0)

JDate++;

// 加 年

JDate*=10000;

JDate+=GDate.Year;

return JDate;

}

前 面 的 代 码 显 示 了 通 过 引 用 传 递 的 结 构 成 员 使 用 成 员 选 择 运 算 符(.) 而 不 是 指 针选 择 运 算 符 (->) 访 问 的 。

尽 管 作 为 引 用 类 型 传 递 的 指 针 遵 从 非 指 针 类 型 语 法 , 但 它 们 保 留 了 指 针 类 型 的 一个 重 要 特 征 : 它 们 是 可 修 改 的 , 除 非 被 说 明 为 const 。 因 为 前 面 代 码 的 意 图 不 是修 改 对 象 GDate, 所 以 一 个 更 适 宜 的 函 数 原 型 为 :

long JulianFromGregorian(const Date& GDate);

此 原 型 保 证 函 数 JulianFromGregorian 不 改 变 其 参 量 。

任 何 采 用 带 引 用 类 型 原 型 化 的 函 数 可 在 其 位 置 接 受 相 同 的 类 型 的 一 个 对 象 , 因 为从 typename 到 typename& 有 一 个 标 准 的 转 换 。

引 用 类 型 函 数 返 回

函 数 可 被 说 明 为 返 回 一 个 引 用 类 型 。 作 这 种 说 明 有 两 个 原 因 :

  • 正 在 返 回 的 信 息 是 一 个 足 够 大 的 对 象 , 返 回 一 个 引 用 比 返 回 一 个 拷贝 效 率 更 高 。

  • 函 数 的 类 型 必 须 为 一 个 l 值。

正 如 通 过 引 用 向 函 数 传 递 大 的 对 象 可 以 更 有 效 一 样 , 通 过 从 函 数 返 回 大 对 象 也 可以 更 为 有 效 。 引 用 返 回 协 议 去 除 了 拷 贝 对 象 到 返 回 之 前 的 临 时 地 点 的 必 要 性 。引 用 返 回 类 型 在 函 数 必 须 求 值 为 一 个 l 值 时 也 是 很 有 用 的 。 大 多 数 重 载 的 运 算符 属 于 此 类 , 特 别 是 赋 值 运 算 符 。 重 载 的 运 算 符 包 含 在 第 12 章“ 重 载 ” 中 的“ 重载 的 运 算 符 ” 中 , 考 虑 第 4 章 “ 表 达 式 ” 中 的 Point 例 子 :

class Point

{

public:

// 定 义 “ 访 问 器 ” 函 数 为 引 用 类 型

unsigned& x();

unsigned& y(); private:

unsigned obj_x;

unsigned obj_y;

};

unsigned& Point::x()

{

return obj_x;

}

unisgned& Point::y()

{

return obj_y;

}

void main()

{

Point ThePoint;

// 使 用 x() 和 y() 作为 l 值

ThePoint.x()=7;

ThePoint.y()=9;

// 使 用 x() 和 y() 作为 r 值

cout << "x=" << ThePoint.x() << "\n"

<< "y=" << ThePoint.y() << "\n"

}

注 意 函 数 x 和 y 说 明 为 返 回 引 用 类 型 。 这 些 函 数 可 用 于 一 个 赋 值 语 句 的 任 意 一边 。 引 用 类 型 的 说 明 必 须 包 含 初 始 化 器 , 除 了 以 下 情 形 之 外 :

  • 显 示 extern 说 明 。

  • 一 个 类 成 员 的 说 明 。

  • 在 一 个 类 内 的 说 明 。

  • 一 个 函 数 的 参 量 或 函 数 返 回 类 型 的 说 明 。

指 针 引 用

指 针 引 用 和 对 象 引 用 相 同 的 方 式 进 行 说 明 。 说 明 一 个 指 针 的 引 用 产 生 一 个 象 正常 指 针 一 样 使 用 的 可 修 改 的 值 。 以 下 代 码 例 子 说 明 使 用 一 个 指 针 的 指 针 和 一 个指 针 的 引 用 之 间 的 差 别 :

#include <iostream.h> #include <string.h>

// 定 义 一 个 二 叉 树 结 构struct BTree

{

char *szText;

BTree *Left;

BTree *Right;

};

// 定 义 一 个 指 向 树 根 的 指 针BTree *btRoot=0;

int Add1(BTree **Root, char *szToAdd); int Add2(BTree*& Root, char *szToAdd); void PrintTree(BTree* btRoot);

int main(int argc, char *argv[])

{

if(argc<2)

{

cerr << "Usage:Refptr[1|2]" << "\n";

cerr << "\n\twhere:\n";

cerr << "\t1 uses double indirection\n";

cerr << "\t2 uses a reference to a pointer.\n";

cerr << "\n\tInput is from stdin.\n";

return 1;

}

char *szBuf=new char[132];

// 从 标 准 输 入 设 备 读 一 文 本 文 件 , 并 构 造 一 棵 二 叉 树

while(!cin.eof())

{

cin.get(szBuf,132,'\n');

cin.get();

if(strlen(szBuf))

switch(*argv[1])

{

// 方 法 1: 使 用 双 重 间 接 引 用

case '1':

Add1(btRoot,szBuf);

break;

// 方 法 2: 使 用 指 针 引 用

case '2':

Add2(btRoot,szBuf);

break;

default:

cerr << "Illegal value "<< *argv[1]

<< " l supplied for add method.\n"

<< "Choose 1 or 2.\n";

return -1;

}

}

// 显 示 排 序 表

PrintTree(btRoot);

return 0;

}

//PrintTree: 按 序 显 示 二 叉 树void PrintTree(BTree* btRoot)

{

// 递 归 地 遍 历 左 子 树

if(btRoot->Left)

PrintTree(btRoot->Left);

// 打 印 当 前 结 点

cout << btRoot->szText << "\n";

// 递 归 地 遍 历 右 子 树

if (btRoot->Right)

PrintTree(btRoot->Right);

}

//Add1: 向 二 叉 树 中 加 入 一 个 结 点

// 使 用 双 重 间 接 引 用

int Add1(BTree **Root,char *szToAdd)

{

if ((*Root)==0)

{

(*Root)=new BTree;

(*Root)->Left=0;

(*Root)->Right=0;

(*Root)->szText=new char[strlen(szToAdd)+1];

strcpy((*Root)->szText,szToAdd);

return 1;

}

else if (strcmp((*Root)->szText,szToAdd)>0)

return Add1(&((*Root)->Left),szToAdd); else

return Add1(&((*Root)->Right),szToAdd);

}

//Add2: 向 二 叉 树 中 加 入 一 个 结 点

// 使 用 指 针 引 用

int Add2(BTree*& Root, char *szToAdd)

{

if (Root==0)

{

Root=new BTree;

Root->Left=0;

Root->Right=0;

Root->szText=new char[strlen(szToAdd)+1];

strcpy(Root->szText,szToAdd);

return 1;

}

else if(strcmp(Root->szText,szToAdd)>0)

return Add2(Root->Left, szToAdd);

else

return Add2(Root->Right, szToAdd);

}

在 上 面 程 序 中 , 函数 Add1 和 Add2 在 功 能 上 是 等 价 的 ( 尽 管 它 们 以 不 同 的 方 式 被调 用 ) 。 差 别 在 于 Add1 使 用 双 重 间 接 引 用 而 Add2 则 使 用 方 便 的 指 针 引 用 。

成 员 指 针

成 员 指 针 的 说 明 是 指 针 说 明 的 特 殊 情 况 。

语 法

明 指 示 符 类 名 称 ::*cv 限 定 符 表 opt dname;

指 向 一 个 类 成 员 的 指 针 与 一 个 正 常 指 针 不 同 , 因 为 它 具 有 成 员 类 型 以 及 所 属 类 的类 型 信 息 。 一 个 正 常 指 针 仅 仅 标 识 ( 拥 有 地 址 ) 存 储 器 中 的 单 个 对 象 。 一 个 类 成员 指 针 标 识 类 的 任 何 实 例 中 的 那 个 成 员 。 以 下 例 子 说 明 了 一 个 类 Window 和 一 些成 员 数 据 指 针 :

class Window

{

public:

Window(); // 缺 省 的 构 造 函 数

Window(int x1,int y1, // 构 造 函 数 指 定 窗 口 大 小

int x2,int y2);

BOOL SetCaption(const char *szTitle); // 设 置 窗 口 标 题

const char *GetCaption(); // 获 取 窗 口 标 题

char *szWinCaption; // 窗 口 标 题

};

// 说 明 一 个 指 向 数 据 成 员 szWinCaption 的 指 针

char *Window::*pwCaption=&Window::szWinCaption;

在 上 面 的 例 子 中 ,pwCaption 是 一 个 指 向 类 Window 的 具 有 char* 类 型 的 任 何 成 员的 指 针 。 pwCaption 的 类 型 是 char *Window::* 。 下 面 的 代 码 段 说 明 指 向SetCaption 和 GetCaption 成 员 函 数 的 指 针 。

const char * (Window::*pfnwGC)()=&Window::GetCaption; BOOL (Window::*pfnwSC)(const char *)=&Window::SetCaption;

指 针 pfnwGC 和 pfnwSC 分 别 指 向 Window 类 的 GetCaption 和 SetCaption 。 该 代码 通 过 使 用 成 员 pwCaption 指 针 直 接 拷 贝 信 息 到 窗 口 标 题 中 :

Window wMainWindow;

Window *pwChildWindow=new Window; char *szUntitled="Untitled-";

int cUntitledLen=strlen(szUntitled);

strcpy(wMainWindow.*pwCaption,szUntitled); (wMainWindow.*pwCaption)[cUntitledLen-1]='1'; // 与 下 面 一 样

//wMainWindow.SzWinCaption[]='1'; strcpy(pwChildWindow->*pwCaption,szUntitled);

(pwChildWindow->*pwCaption)[szUntitledLen-1]='2';// 与 下 面 一 样

//pwChildWindow->szWinCaption[]='2';

.* 和 ->* 运 算 符 ( 成 员 指 针 运 算 符 ) 的 差 别 是 :.* 运 算 符 通 过 给 定 一 个 对 象 或 对 象

引 用 选 取 成 员 , 而 ->* 运 算 符 通 过 一 个 指 针 选 取 成 员 ( 关 于 这 些 运 算 符 的 更 多 信 息参 见 第 4 章 “ 表 达 式 ” 中 的 “ 带 成 员 指 针 运 算 符 的 表 达 式 ” ) 。

成 员 指 针 运 算 符 的 结 果 是 成 员 的 类 型 , 在 此 例 情 形 中 为 char* 。

以 下 代 码 段 使 用 成 员 指 针 调 用 成 员 函 数 GetCaption 和 SetCaption:

/ / 分 配 一 个 缓 冲 区

char szCaptionBase[100];

// 拷 贝 主 窗 口 标 题 到 缓 冲 区 中 , 并 添 加 “ [View 1] ” . strcpy(szCaptionBase,(wMainWindow.*pfnwGC)()); strcat(szCaptionBase,"[View 1]");

// 设 置 子 窗 口 标 题

(pwChildWindow->*pfnwSC)(szCaptionBase);

成 员 指 针 的 限 制

  • 个 静 态 成 员 的 地 址 不 是 一 个 成 员 指 针 , 它 是 一 个 静 态 成 员 实 例 的 规 则 指 针 。 但对 一 个 给 定 类 的 所 有 对 象 仅 存 在 一 个 静 态 成 员 实 例 , 普 通 的 取 地 址 (&) 和 间 接 引用 (*) 运 算 符 可 以 使 用 。

成 员 指 针 和 虚 拟 函 数

通 过 一 个 成 员 指 针 函 数 调 用 一 个 虚 拟 函 数 就 如 同 函 数 已 被 直 接 调 用 一 样 : 正 确 的函 数 在 v 表 中 查 找 并 调 用 。 以 下 代 码 显 示 了 这 是 如 何 完 成 的 :

class Base

{

public:

virtual void Print();

};

void (Base::*bfnPrint)()=&Base::Print;

void Base::Print()

{

cout << "Print function for class 'Base'\n";

}

class Derived:public Base

{

public:

void Print();//Print 还 是 一 个 虚 拟 函 数

};

void Derived::Print()

{

cout << "Print function for class 'Derived'\n;

}

void main()

{

Base *bPtr;

Base bObject;

Derived dObject;

bPtr=&bObject; // 设 置 指 向 bObject 的 地 址 的 指 针

(bPtr->*bfnPrint)();

bPtr=&dObject; // 设 置 指 向 dObject 的 地 址 的 指 针

(bPtr->*bfnPrint)();

}

该 程 序 的 输 出 为 :

Print function for Class 'Base' Print function for Class 'Derived'

虚 拟 函 数 工 作 的 关 键 , 和 通 常 一 样 , 是 通 过 指 向 一 个 基 类 的 指 针 调 用 它 们 ( 关 于 虚拟 函 数 的 更 多 信 息 参 见 第 9 章 “ 派 生 类 ” 中 的 “ 虚 拟 函 数 ” ) 。

使 用 继 承 表 示 类 成 员 指 针

在 类 定 义 之 前 说 明 类 成 员 指 针 影 响 产 生 可 执 行 文 件 的 大 小 和 速 度 。 表 示 一 个 类

成 员 指 针 所 需 要 的 字 节 数 和 解 释 所 要 求 的 代 码 依 赖 于 类 是 否 定 义 为 不 带 、 带 单个 和 多 个 或 虚 拟 的 继 承 性 。

总 而 言 之 , 一 个 类 用 的 继 承 越 复 杂 , 表 示 类 成 员 指 针 所 需 的 字 节 数 就 越 多 , 解 释 指针 所 需 的 代 码 就 越 大 。

如 果 你 需 要 在 类 定 义 前 说 明 一 个 类 成 员 指 针 , 你 必 须 要 么 使 用 /vmg 命 令 行 选 项 , 要 么 使 用 相 关 的 pointers_to_members 编 译 指 示 , 或 者 你 可 以 通 过 使 用

__single_inheritance 、 __ multiple_inheritance 或 __ virtual_inheritance 关键 字 指 定 在 类 说 明 中 用 到 的 继 承 性 , 因 而 允 许 在 每 个 类 基 础 上 产 生 代 码 的 控 制 。这 些 选 项 在 下 面 进 行 解 释 。

注 意 : 如 果 你 总 是 在 类 定 义 之 后 说 明 一 个 类 成 员 指 针 , 则 你 不 需 要 使 用 任 何 这 些选 项 。

Microsoft 通 过 选 择 最 紧 凑 的 表 示 可 能 意 图 优 化 成 员 指 针 的 表 示 和 产 生 的 代 码 。这 要 求 定 义 成 员 指 针 类 是 基 于 成 员 指 针 被 说 明 的 地 点 的 。 pointers_to_members 编 译 指 示 允 许 放 宽 此 限 制 , 并 控 制 指 针 大 小 和 解 释 指 针 所 需 的 代 码 。

语 法

#pragma pointers_to_members( 指 针 说 明 ,[ 最 通 用 的 表 示 ])

指 针 说 明 参 量 是 指 定 你 是 否 已 在 相 关 的 函 数 定 义 之 前 或 之 后 说 明 了 一 个 成 员 指针 。 指 针 说 明 参 量 可 以 是 full_generality 或 best_case 。

最 通 用 的 表 示 参 量 指 定 编 译 器 可 安 全 地 用 于 引 用 一 个 转 换 单 元 中 任 一 类 成 员 指针 的 最 小 指 针 表 示 。此 参 量 可 以 是 single_inheritance 、multiple_inheritance 或 virtual_inheritance 。 带 best - case 参 量 的 pointers_to_members 编 译 指 示是 编 译 器 的 缺 省 值 , 如 果 你 总 是 在 说 明 一 个 类 成 员 指 针 之 前 定 义 类 , 则 你 可 以 使

用 此 缺 省 值 。 当 编 译 器 遇 到 一 个 类 成 员 指 针 的 说 明 时 , 它 已 经 知 道 类 所 用 的 继 承种 类 。 因 而 , 编 译 器 可 以 使 用 指 针 的 最 小 可 能 表 示 , 并 产 生 为 每 种 继 承 操 作 于 指针 上 所 要 求 的 最 少 的 代 码 量 。 这 等 价 于 在 命 令 行 使 用 /vmb 去 为 转 换 单 元 中 所 有的 类 指 定 最 佳 情 形 的 表 示 。

如 果 你 需 要 在 定 义 类 之 前 说 明 一 个 类 成 员 指 针 , 则 使 用 带 full_generality 参 量的 pointers_to_members 编 译 指 示 ( 如 果 你 在 两 个 不 同 的 类 中 定 义 成 员 并 使 用 成员 指 针 相 互 引 用 , 会 引 起 这 种 需 要 。 对 这 种 相 互 引 用 类 的 情 况 , 一 个 类 必 定 会 在其 定 义 之 前 被 引 用 ) 。 编 译 器 为 成 员 指 针 使 用 最 通 用 的 表 示 。 这 等 价 于 /vmg 编译 器 选 项 。 如 果 你 指 定 full_generality, 你 必 须 还 指 定 single_inheritance 、multiple_inheritance 或 virtual_inheritance 。 这 等 价 于 使 用 带 /vms 、 /vmm 或 /vmv 选 项 的 /vmg 编 译 器 选 项 。

带 full_generality 、 single_inheritance 参 量 的 pointers_to_members 编 译指 示 (/vms 选 项 和 /vmg 选 项 一 起 ) 指 定 类 成 员 指 针 的 最 通 用 的 表 示 是 使 用 无 继 承或 单 个 继 承 的 那 种 。 这 是 类 成 员 指 针 最 小 的 可 能 表 示 。 如 果 一 个 成 员 指 针 被 说明 的 类 定 义 的 继 承 性 模 式 是 多 继 承 或 虚 拟 继 承 , 则 编 译 器 产 生 一 个 错 误 。 例 如 , 将 下 面 语 句 :

#pragma pointers_to_members(full_generality,single_inheritance)

放 在 一 个 类 定 义 之 前 , 说 明 随 后 的 所 有 类 定 义 仅 仅 使 用 单 继 承 。 一 旦 指 定 了 , 用pointers_to_members 编 译 指 示 指 定 的 选 项 就 不 能 改 变 了 。

带 full_generality 、 multiple_inheritance 参 量 的 pointers_to_members 编译 指 示 (/vmm 选 项 和 /vmg 选 项 一 起 ) 指 定 类 成 员 指 针 的 最 通 用 的 表 示 是 使 用 多 继承 的 那 种 。 这 种 表 示 比 单 继 承 所 需 要 的 大 些 。 如 果 一 个 成 员 指 针 被 说 明 的 类 定

义 的 继 承 模 式 是 虚 拟 继 承 , 则 编 译 器 产 生 一 个 错 误 。

带 full_generality 、 virtual_inheritance 参 量 的 pointers_to_members 编 译指 示 (/vmv 选 项 和 /vmg 选 项 一 起 ) 指 定 类 成 员 指 针 的 最 通 用 的 表 示 是 使 用 虚 拟 继承 的 那 种 。 根 据 指 针 的 大 小 和 解 释 指 针 所 需 要 的 代 码 看 , 这 是 最 昂 贵 的 选 项 。 但是 此 选 项 从 不 产 生 错 误 , 并 且 当 full_generality 参 量 被 指 定 给pointers_to_members 或 /vmg 命 令 行 选 项 被 使 用 时 , 它 是 一 个 缺 省 的 。

语 法

等 价 的 语 言 结 构 使 用 此 语 法 :

类 说 明 :

类 继 承 类 型 opt 类 名 称 ; 继 承 类 型 :

__ single_inheritance

__ multiple_inheritance

__ virtual_inheritance 正 如 这 个 例 子 中 所 示 :

class __single_inhertance S; int S::p;

不 管 编 译 器 选 项 或 编 译 指 示 , 类 S 的 成 员 指 针 将 使 用 最 小 的 可 能 表 示 。你 还 可 以 显 式 给 出 具 有 前 向 说 明 的 类 的 成 员 指 针 表 示 的 一 个 前 向 说 明 。

注 意 : 类 成 员 指 针 表 示 的 相 同 的 前 向 说 明 应 该 出 现 在 说 明 那 个 类 的 成 员 指 针 的 每个 转 换 单 元 中 , 而 且 说 明 应 该 出 现 在 成 员 指 针 被 说 明 之 前 。

数 组

一 个 数 组 是 相 似 对 象 的 一 个 集 合 。 一 个 数 组 最 简 单 的 情 况 是 一 个 向 量 。 C++ 为 固定 大 小 数 组 的 说 明 提 供 一 种 方 便 的 语 法 :

语 法

说 明 指 示 符 dname [ 常 量 表 达 式 opt];

数 组 中 元 素 个 数 由 常 量 表 达 式 给 定 , 数 组 的 第 一 个 元 素 是 第 0 号 元 素 , 最 后 一 个元 素 是 第 (n-1) 号 元 素 , 其 中 n 是 数 组 的 大 小 。 常 量 表 达 式 必 须 是 一 个 整 型 , 且 必须 大 于 0 。 一 个 0 尺 寸 数 组 仅 当 数 组 是 一 个 struct 或 union 的 最 后 一 个 域 , 且Microsoft 扩充 (/Ze) 时 才 是 合 法 的 。

数 组 是 派 生 类 型 , 因 此 可 以 由 除 函 数 、 引 用 和 void 以 外 的 任 何 其 它 派 生 的 或 基本 的 类 型 构 成 。

从 其 它 数 组 构 成 的 数 组 是 多 维 数 组 。 这 些 多 维 数 组 通 过 按 顺 序 放 置 多 个 [ 常 量 表达 式 ] 指 定 , 例 如 , 考 虑 这 个 说 明 :

int i2[5][7];

它 指 定 了 一 个 int 类 型 数 组 , 概 念 上 安 排 在 一 个 5 行 7 列 的 二 维 矩 阵 中 , 如 图 7.2 所 示 。

第 7 章 说 明 符 - 图2

图 7.2 多 维 数 组 的 概 念 性 布 局

在 带 有 初 始 化 器 表 ( 如 初 始 化 器 中 所 描 述 的 ) 的 多 维 数 组 说 明 中 , 第 一 维 指 定 边 界的 常 量 表 达 式 可 以 省 略 。 例 如 :

const int cMarkets=4;

// 说 明 一 个 浮 点 类 型 表 示 运 输 费 用double TransportCosts[][cMarkets]=

{ {32.19,47.29,31.99,19.11},

{11.29,22.49,33.47,17.29},

{41.97,22.09,9.76,22.55} };

上 面 的 说 明 定 义 了 一 个 3 行 4 列 的 数 组 ; 行 表 示 工 厂 , 列 表 示 工 厂 输 送 到 的 市 场 , 值 是 从 工 厂 到 市 场 的 运 输 费 用 。 数 组 的 第 一 维 省 略 了 , 但 编 译 器 通 过 检 查 初 始 化器 将 它 填 上 。

多 维 数 组 的 第 一 维 的 省 略 边 界 指 定 的 技 术 还 可 用 于 函 数 说 明 中 , 如 下 所 示 : #include <float.h> // 包 括 DBL_MAX

#include <iostream.h> const int cMkts=4;

// 说 明 一 个 浮 点 类 型 表 示 运 输 费 用

double TransportCosts[][cMkts]=

{ {32.19,47.29,31.99,19.11},

{11.29,22.49,33.47,17.29},

{41.97,22.09,9.76,22.55} };

// 计 算 未 指 定 的 维 数 的 大 小

const int cFactories=sizeof TransportCosts /

sizeof (double[cMkts]);

double FindMinToMkt(int Mkt,double TransportCosts[][cMkts],int cFacts);

void main(int argc,char *argv[])

{

double MinCost;

MinCost=FindMinToMkt(*argv[1]-'0',TransportCosts,cFacts);

cout << "The minimum cost to Market" << argv[1] << "is:"

<< MinCost << "\n";

}

double FindMinToMkt(int Mkt,double TransportCosts[][cMkts],int cFacts)

{

double MinCost=DBL_MAX;

for(int i=0;i<cFacts;++i)

MinCost=(MinCost<TransportCosts[i][Mkt])?

MinCost:TransportCosts[i][Mkt];

return MinCost;

}

函 数 FindMinToMkt 编 写 成 添 加 新 工 厂 , 不 需 要 改 动 任 何 代 码 , 只 要 一 次 重 新 编译 。

使 用 数 组

数 组 中 的 单 个 元 素 使 用 数 组 下 标 运 算 符 ([]) 进 行 访 问 。 如 果 是 一 个 单 维 数 组 被用 于 不 带 下 标 的 表 达 式 中 , 数 组 名 称 求 值 为 指 向 数 组 第 一 个 元 素 的 指 针 。 例 如 : char chArray[10];

...

char *pch=chArray; // 指 向 第 一 个 元 素 的 指 针char ch=chArray[0]; // 第 一 个 元 素 的 值

ch=chArray[3]; // 第 四 个 元 素 的 值

当 使 用 多 维 数 组 时 , 表 达 式 中 对 各 种 组 合 都 可 接 受 。 以 下 例 子 说 明 了 这 点 : double multi[4][4][3];// 说 明 一 个 数 组

double (*p2multi)[3]; double (*p1multi);

cout << multi[3][2][3] << "\n"; // 使 用 三 个 下 标p2multi=multi[3]; // 使 p2multi 指 向 multi 的 第 四 个 “ 平 面 ”

p1multi=multi[3][2]; // 使 p1multi 指 向 multi 的 第 四 个 “ 平 面 ” ,

//multi 的 第 二 行

在 上 面 代 码 中 ,mutli 是 一 个 double 类 型 的 三 维 数 组 。 p2multi 指 针 指 向 一 个double 类 型 大 小 为 3 的 一 个 数 组 。 在 此 例 中 数 组 带 1 个 、 2 个 和 3 个 下 标 使 用 。尽 管 通 常 指 定 所 有 的 下 标 , 如在 cout 语 句 中 , 但 有 时 选 取 数 组 元 素 的 一 个 特 定 的子 集 是 很 有 用 的 , 如 后 续 的 语 句 所 示 。

表 达 式 中 的 数 组

当 一 个 数 组 类 型 的 标 识 符 出 现 在 一 个 表 达 式 中 而 不 是 在 sizeof 、 取 地 址 (&) 或引 用 的 初 始 化 中 , 则 它 被 转 换 为 指 向 数 组 第 一 个 元 素 的 指 针 , 例如 :

char szError1[]="Error:Disk drive not ready."; char *psz=szError1;

psz 指 针 指 向 数 组 szError1 的 第 一 个 元 素 。 注 意 , 数 组 不 象 指 针 , 数 组 是 不 可 修改 的 l 值 。 因 此 , 以 下 赋 值 是 非 法 的 :

szError1=psz;

下 标 运 算 符 的 解 释

象 其 它 运 算 符 一 样 , 下 标 运 算 符 ([]) 可 由 用 户 重 新 定 义 。 如 果 没 有 被 重 载 , 则 下标 运 算 符 的 缺 省 动 作 是 使 用 以 下 方 法 将 数 组 和 下 标 组 合 在 一 起 :

*(( 数 组 名 )+( 下 标 ))

和 包 含 指 针 类 型 的 所 有 加 法 中 一 样 , 定 位 自 动 被 执 行 以 调 整 类 型 的 尺 寸 。 因 此 , 结 果 值 不 是 从 数 组 名 起 始 的 下 标 字 节 数 , 而 是 数 组 的 以 下 标 为 序 数 的 那 个 元 素( 关 于 此 转 换 的 更 多 信 息 见 第 4 章 “ 表 达 式 ” 中 的 “ 加 法 运 算 符 ” ) 。

类 似 地 , 对 多 维 数 组 , 地 址 是 使 用 以 下 方 法 派 生 的 :

*(( 数 组 名 )+( 下 标 1 *max 2*max 3...max n )

+ 下 标 2 *max 3...max n)

...+ 下 标 n ))

数 组 类 型 上 的 间 接 操 作

在 一 个 n 维 数 组 类 型 上 使 用 间 接 运 算 符 (*) 产 生 一 个 n-1 维 数 组 。 如 果 n 是 1, 则 产 生 一 个 标 量 ( 或 数 组 元 素 ) 。

数 组 的 排 序

C++ 数 组 按 行 主 顺 序 排 序 。 行 主 顺 序 意 味 着 最 后 的 下 标 改 变 得 最 快 。

函 数 说 明

本 节 包 括 以 下 主 题 :

  • 函 数 说 明 语 法

· 可 变 的 参 量 表

· 说 明 不 带 参 量 的 函 数

· 函 数 重 载

· 函 数 上 的 限 制

· 参 量 说 明 表

· 函 数 原 型 ( 未 定 义 说 明 ) 中 的 参 量 表

· 函 数 定 义 中 的 参 量 表

  • 缺 省 参 量

· 缺 省 参 量 表 达 式

· 其 它 考 虑

函 数 定 义 包 含 在 “ 函 数 定 义 ” 节 中 。

函 数 说 明 语 法

语 法

说 明 指 示 符 dnam e( 参 量 说 明 表 ) cv 修 饰 符 表 opt

参 量 说 明 表 :

参 量 说 明 表 ,... 参 量 说 明 表 :

参 量 说 明

参 量 说 明 表 , 参 量 说 明参 量 说 明 表 :

说 明 指 示 符 说 明 符

说 明 指 示 符 说 明 符 = 表 达 式

说 明 指 示 符 抽 象 说 明 符 opt

说 明 指 示 符 抽 象 说 明 符 opt= 表 达 式

dname 给 定 的 标 识 符 具 有 类 型 “ cv 修 饰 表 函 数 , 带 参 量 说 明 表 且 返 回 类 型 说明 指 示 符 ”。

注 意 , const 、 volatile 和 许 多 Microsoft 特 殊 关 键 字 可 以 出 现 在 cv 修 饰 符 表和 名 称 的 说 明 中 。 以 下 例 子 给 出 了 两 个 简 单 的 函 数 说 明 :

char *strchr(char *dest,char *src);

static int atoi(const char *ascnum) const; 以 下 语 法 解 释 了 函 数 说 明 的 细 节 :

语 法

参 量 说 明 表 :

参 量 说 明 表 opt ... opt

参 量 说 明 表 ,... 参 量 说 明 表 :

参 量 说 明

参 量 说 明 表 , 参 量 说 明参 量 说 明 :

说 明 指 示 符 说 明 符

说 明 指 示 符 说 明 符 , 表 达 式

说 明 指 示 符 抽 象 说 明 符 opt

说 明 指 示 符 抽 象 说 明 符 opt , 表 达 式

可 变 的 参 量 表

参 量 说 明 表 的 最 后 一 个 成 员 是 省 略 号 (...) , 其 函 数 说 明 可 以 带 可 变 数 量 的 参量 。 在 这 些 情 况 中 ,C++ 仅 为 显 式 说 明 的 参 量 提 供 类 型 检 查 。 当 你 需 要 一 个 通 用函 数 其 参 量 的 个 数 和 类 型 是 可 变 的 , 你 可 以 使 用 可 变 的 参 量 表 。 printf 函 数 簇便 是 一 个 使 用 可 变 的 参 量 表 的 函 数 例 子 。

为 了 在 那 些 说 明 之 后 访 问 参 量 , 使 用 在 标 准 包 含 文 件 STDARG.H 中 包 含 的 宏 , 如 本章 后 面 “ 带 可 变 参 量 的 函 数 ” 中 所 描 述 的 。

Microsoft 特 殊 处 →

Microsoft C++ 允 许 省 略 号 指 定 为 一 个 参 量 , 如 果 省 略 号 是 第 一 个 参 量 且 省 略 号前 面 为 一 个 逗 号 。 因 此 , 说 明 int Func(int i,...); 是 合 法 的 , 但 int Func(int i...); 则 不 是 。

Microsoft 特 殊 处 结 束

带 可 变 数 目 的 参 量 的 函 数 说 明 需 要 至 少 一 个 “ 位 置 占 有 者 ” 参 量 , 即 使 它 未 被 使用 。 如 果 没 有 提 供 这 个 占 位 参 量 , 则 没 有 办 法 去 访 问 剩 余 的 参 量 。

当 char 类 型 的 参 量 被 作 为 可 变 参 量 传 递 时 , 它 们 被 转 换 成 int 类 型 。 类 似 地 , 当float 类 型 的 参 量 被 作 为 可 变 参 量 传 递 时 , 它 们 被 转 换 成 double 类 型 。 其 它 类型 的 参 量 属 于 常 用 的 整 型 和 浮 点 提 升 。 更 多 的 信 息 参 见 第 3 章 “ 标 准 转 换 ” 中的 “ 整 型 提 升 ”。

说 明 不 带 参 量 的 函 数

在 参 量 说 明 表 中 用 单 个 关 键 字 void 说 明 的 函 数 不 带 参 量 , 只 要 关 键 字 void 是 第一 个 且 是 唯 一 的 参 量 说 明 表 中 的 一 个 成 员 。在 参 量 说 明 表 中 任 何 其 它 位 置 的 void 类 型 参 量 将 产 生 错 误 。 例 如 :

long GetTickCount(void); // 可 行long GetTickCount(int Reset, void); // 错 误long GetTickCount(void,int Reset); // 错 误

在 C++ 中 , 显 式 指 定 一 个 函 数 不 需 要 参 数 的 函 数 与 说 明 一 个 不 带 参 量 说 明 表 的 函数 是 一 样 的 , 因 此 , 以 下 两 个 语 句 是 完 全 相 同 的 :

long GetTickCount(); long GetTickCount(void);

注 意 : 除 了 此 处 提 出 的 , 其 它 的 指 定 一 个 void 参 量 是 非 法 的 ; 但 从 void 类 型 派 生的 类 型 ( 如 指 向 void 的 指 针 和 void 数 组 ) 可 出 现 在 参 量 说 明 表 中 的 任 何 位 置 。

函 数 重 载

C++ 允 许 在 相 同 的 范 围 内 指 定 不 止 一 个 的 相 同 名 称 的 函 数 。 这 些 被 称 为 “ 重 载 的函 数 ” , 在 第 12 章 “ 重 载 ” 中 有 详 细 的 描 述 。 重 载 函 数 使 程 序 员 能 够 为 一 个 函数 提 供 不 同 的 语 义 , 这 取 决 于 参 量 的 类 型 和 数 目 。

例 如 , 带 一 个 字 符 串 ( 或 char*) 参 量 的 print 函 数 与 带 一 个 double 类 型 参 量 的函 数 执 行 极 为 不 同 的 任 务 。 重 载 允 许 统 一 的 命 名 , 防 止 程 序 员 去 发 明 诸 如print_sz 或 print_d 这 样 的 名 称 。 表 7.1 显 示 了 C++ 使 用 函 数 说 明 的 什 么 部 分去 区 分 在 相 同 范 围 中 具 有 相 同 名 称 的 函 数 组 。

表 7.1 重 载 考 虑

函数说明元素

用于重载 ?

函数返回类型

参量的个数

参量的类型

省略号的出现或不出现

t y pedef 名称的使用

未指定的数组边界

const 或 volatile( 在

cv 修饰

符表中 )

尽 管 函 数 可 以 在 返 回 类 型 基 础 上 加 以 区 分 , 但 它 们 却 不 能 在 此 基 础 上 重 载 。

以 下 例 子 说 明 了 重 载 是 如 何 被 使 用 的 。 解 决 此 同 一 问 题 的 另 一 方 法 在 本 章 后 面部 分 “ 缺 省 参 量 ” 中 给 出 。

#include <iostream.h> #include <math.h> #include <stdlib.h>

// 三 个 print 函 数 原 型

int print(char *s); // 打 印 一 个 字 符 串

int print(double dvalue); // 打 印 一 个 double 值

int print(double dvalue,int prec); // 打 印 一 个 具 有 给 定 精 度 的 double 值

void main(int argc,char *argv[])

{

const double d=893094.2987;

if(argc<2)

{

// 这 些 调 用 用 于 打 印 invoke print(char *s)

print("this program requires one argument.");

print("the argu ment specifies the number of");

print("digits precision for the second number");

print("printed.");

}

// 调 用 print(double dvalue).

print(d);

// 调 用 print(double dvalue,int prec).

print(d,atoi(argv[1]));

}

// 打 印 一 个 字 符 串

int print(char *s)

{

cout << s << endl;

return cout.good();

}

// 以 缺 省 精 度 打 印 一 个 double 数

int print (double dvalue)

{

cout << dvalue << endl;

retarn cout.good();

}

// 以 指 定 的 精 度 打 印 一 个 double 数

// 正 数 精 度 指 示 小 数 点 后 显 示 几 位 数 字 精 度

// 负 数 精 度 指 示 小 数 点 左 边 进 行 数 的 舍 入 的 地 方

int print(double dvalue,int prec)

{

// 为 舍 入 或 截 断 使 用 表 查 找

static const double rgPow10[]={

10E-7,10E-6,10E-5,10E-4,10E-3,10E-2,1E-1,10E0,

10E1, 10E2, 10E3, 10E4, 10E5, 10E6

};

const int iPowZero=6;

// 如 果 精 度 在 范 围 外 , 则 就 打 印 此 数

if (prec < -6 || prec>7)

return print(dvalue);

// 定 位 , 截断 , 然 后 再 定 位

dvalue=floor(dvalue/rgPow10[iPowZero-prec])*

rgPow10[ipowZero-prec];

cout << dvalue << endl;

return cout.good();

}

上 面 的 代 码 给 出 了 在 文 件 范 围 中 对 print 函 数 的 重 载 。

关 于 重 载 的 限 制 和 重 载 如 何 影 响 其 它 C++ 元 素 的 信 息 , 参 见 第 12 章 “ 重 载 ”。

函 数 的 限 制

函 数 不 能 返 回 数 组 或 函 数 , 但 是 它 们 可 以 返 回 引 用 或 指 向 数 组 或 函 数 的 指 针 。 返回 数 组 的 另 一 个 方 法 是 仅 用 那 个 数 组 作 为 一 个 成 员 去 说 明 一 个 结 构 :

struct Address

{ char szAddress[31]; };

Address GetAddress();

在 函 数 说 明 的 返 回 类 型 部 分 或 在 函 数 的 任 何 参 量 的 说 明 中 去 定 义 一 个 类 型 都 是非 法 的 。

以 下 合 法 的 C 代 码 在 C++ 中 是 非 法 的 :

enum Weather{ Cloudy,Rainy,Sunny } GetWeather(Date Today)

上 面 的 代 码 是 不 允 许 的 , 因 为 Weather 类 型 具 有 相 对 GetWeather 局 部 的 函 数 范围 , 则 不 适 合 使 用 返 回 值 。 因 为 函 数 的 参 量 具 有 函 数 范 围 , 所 以 在 参 量 表 内 作 的说 明 如 果 不 被 允 许 则 将 有 同 样 的 问 题 。

C++ 不 支 持 函 数 数 组 。 但 指 向 函 数 的 指 针 数 组 可 能 非 常 有 用 。 在 类 Pascal 语 言的 语 法 分 析 中 , 代 码 通 常 分 开 进 入 一 个 语 言 符 号 分 析 用 的 词 法 分 析 器 和 一 个 将 语义 附 于 语 言 符 号 用 的 语 法 分 析 器 中 。 如 果 分 析 器 为 每 个 符 号 返 回 一 个 特 定 的 普通 值 , 则 代 码 可 被 写 成 如 下 例 子 所 示 以 执 行 适 当 的 处 理 :

int ProcessFORTOken(char *szText); int ProsessWHILEToken(char *szText); int ProcessBEGINTOken(char *szText); int ProcessENDToken(char *szText); int ProcessIFToken(char *szText); int Pro c essTHENToken(char *stText); int Pro c essELSEToken(char *szText); int (*ProcessToken[])(char *)={

ProcessFORToken,ProcessWHILEToken,ProcessBEGINToken,

ProcessENDToken,ProcessIFToken,ProcessTHENToken,

ProcessELSEToken};

const int MaxTOkenTD=sizeof ProcessTOken / sizeof(int(*)());

...

int DoProcessToken(int TokenID, char *szText)

{

if (TOkenTD < MaxTokenID)

return (*ProcessToken[TokenID])(szText);

else

return Error(szText);

}

参 量 说 明 表

  • 个 函 数 说 明 的 参 量 说 明 表 部 分 :

    • 允 许 编 译 器 的 检 查 函 数 所 需 参 量 与 调 用 中 提 供 的 参 量 之 间 的 类 型 一致 性 。

    • 允 许 从 提 供 的 参 量 类 型 转 换 到 所 需 的 参 量 类 型 , 转 换 过 程 或 者 为 隐式 的 或 者 为 用 户 定 义 的 。

    • 检 查 函 数 指 针 的 初 始 化 或 赋 值 。

    • 检 查 函 数 引 用 的 初 始 化 或 赋 值 。

函 数 原 型 ( 非 定 义 说 明 ) 中 的 参 量 表

参 量 说 明 表 形 式 是 参 量 的 类 型 名 称 的 一 个 表 。 考 虑 函 数 func 的 参 量 表 , 带 三 个

参 量 : 指 向 char 的 指 针 、 char 和 int 类 型 。这 样 一 个 参 量 说 明 表 的 代 码 可 写 为 :

char*,char,int

因 此 函 数 说 明 ( 原型 ) 可 写 成 :

void func (char*,char,int);

尽 管 上 面 的 说 明 包 含 了 足 够 的 信 息 供 编 译 器 执 行 类 型 检 查 和 转 换 , 但 没 有 提 供 参量 是 什 么 的 更 多 的 信 息 。 一 个 书 写 函 数 说 明 的 更 好 的 方 法 是 包 含 标 识 符 , 因 为 它们 将 在 函 数 定 义 中 出 现 , 如 以 下 所 示 :

void func(char *szTarget, char chSearchChar,int nStartAt);

原 型 中 的 这 些 标 识 符 仅 对 缺 省 参 量 有 用 , 因 为 它 们 直 接 超 出 了 范 围 。 但 是 它 们 提供 了 有 意 义 的 程 序 文 档 。

函 数 定 义 中 的 参 量 表

在 一 个 函 数 定 义 中 的 参 量 表 与 原 型 中 的 不 同 之 处 仅 在 于 : 如 果 存 在 标 识 符 , 则 它表 示 函 数 的 形 参 。 标 识 符 名 不 需 要 与 原 型 中 的 那 些 匹 配 ( 如 果 存 在 ) 。

注 意 : 可 以 定 义 带 未 命 名 的 参 量 的 函 数 。 但 是 这 些 参 量 对 它 们 定 义 的 函 数 是 不 可访 问 的 。

缺 省 参 量

在 很 多 情 况 中 , 函 数 拥 有 的 参 量 使 用 的 频 度 太 小 , 只 需 要 一 个 缺 省 值 就 够 了 。 要用 这 个 , 缺 省 参 量 仅 允 许 用 于 指 定 在 一 个 给 定 的 调 用 中 是 有 意 义 的 函 数 的 那 些 参量 , 为 了 说 明 这 个 概 念 , 考 虑 本 章 前 面 “ 函 数 重 载 ” 中 给 出 的 例 子 :

// 三 个 print 函 数 原 型

int print(char *s); // 打 印 一 个 字 符 串

int print(double dvalue); // 打 印 一 个 double 值

int print(double dvalne, int prec); // 打 印 一 个 给 定 精 度 的 double 值 在 很 多 应 用 中 , 可 以 用 prec 提 供 合 理 的 缺 省 值 ,而 不 必 需 要 两 个 函 数 。

// 两 个 print 函 数 原 型

int print(char *s); // 打 印 一 个 字 符 串

int print(double dvalue,int prec=2); // 打 印 一 个 给 定 精 度 的 double 值print 函 数 的 实 现 稍 稍 改 动 以 反 映 出 这 个 事 实 :即 对 于 double 类 型 只 存 在一 个 这 样 的 函 数 :

// 打 印 一 给 定 精 度 的 double 值

// 正 精 度 值 指 示 小 数 点 后 显 示 多 少 位 数 字

// 负 精 度 值 指 示 小 数 点 左 边 舍 入 的 位 置int print (double dvalue,int prec)

{

// 为 舍 入 或 截 断 使 用 查 找 表

static const double rgPow10[]={

10E-7,10E-6,10E-5,10E-4,10E-3,10E-2,10E-1,10E0,

10E1, 10E2, 10E3, 10E4, 10E5, 10E6

};

const int iPowZero=6;

// 如 果 精 度 超 出 了 范 围 , 则 就 打 印 这 个 数

if (prec>=-6 || prec<=7)

// 定位 , 截 断 , 然 后 再 定 位

dvalue=floor(dvalue / rgPow10[iPowZero-prec])*

rgPow10[iPowZero-prec] ;

cout << dval u e << endl;

return cout.good();

}

为 了 调 用 新 的 print 函 数 , 使 用 如 下 代 码 :

print(d); // 精 度 为 2, 由 缺 省 参 量 提 供print(d,0); // 绕 过 缺 省 参 量 以 获 取 其 它 的 值 。在 使 用 缺 省 参 量 时 要 注 意 以 下 几 点 :

  • 缺 省 参 量 仅 用 在 尾 部 参 量 被 省 略 的 函 数 调 用 中 , 即 它 们 必 须 是 最 后的 参 量 。 因 此 , 以 下 代 码 是 非 法 的 :

int print(double dvalue=0.0, int prec);

  • 一 个 缺 省 的 参 量 在 后 面 的 说 明 中 不 能 定 义 , 即 使 再 定 义 与 原 来 的 完全 相 同 。 因 此 , 以 下 代 码 产 生 一 个 错 误 :

//print 函 数 原 型

int print(double dvalue,int prec=2);

...

//print 函 数 定 义

int print (double dvalue ,int prec=2)

{

...

}

此 代 码 的 问 题 是 在 定 义 中 的 函 数 说 明 为 prec 重 定 义 了 缺 省 值 。

  • 额 外 的 缺 省 参 量 可 由 后 面 的 说 明 添 加 。

  • 可 以 为 函 数 指 针 提 供 缺 省 的 参 量 。 例 如 : int

    (*pShowIntVal)(int i=0);

缺 省 的 参 量 表 达 式

缺 省 参 量 使 用 的 表 达 式 通 常 是 常 量 表 达 式 , 但 这 不 是 要 求 的 。 表 达 式 可 以 组 合 当前 范 围 中 可 见 的 函 数 、 常 量 表 达 式 和 全 局 变 量 。 表 达 式 不 能 包 含 局 部 变 量 或 非静 态 类 成 员 变 量 。

以 下 代 码 说 明 这 点 :

BOOL CreateVScrollBar(HWND hWnd, short nWidth=

GetSystemMetrics(SM_CXVSCROLL));

上 面 的 说 明 指 定 了 一 个 函 数 , 为 一 个 窗 口 创 建 一 个 给 定 宽 度 的 垂 直 滚 动 条 。 如 果没 有 提 供 宽 度 参 量 , 则 Windows API 函 数 GetSystemMetrics 被 调 用 为 一 个 滚 动条 查 找 缺 省 宽 度 。

在 函 数 调 用 之 后 缺 省 表 达 式 被 求 值 , 但 求 值 在 函 数 调 用 实 际 发 生 之 前 完 成 。

因 为 函 数 的 形 参 是 在 函 数 范 围 内 , 并 且 因 为 缺 省 参 量 的 求 值 在 进 入 此 范 围 之 前 发

生 , 所 以 你 不 能 在 缺 省 参 量 表 达 式 中 使 用 形 参 或 局 部 变 量 。

注 意 在 一 个 缺 省 参 量 表 达 式 之 前 说 明 的 任 何 形 参 可 在 函 数 范 围 内 隐 藏 一 个 全 局名 称 , 这 可 能 导 致 错 误 。 以 下 代 码 是 非 法 的 :

const int Categories = 9;

void Enum Categories(char *Categories[],int n=Categories);

在 前 面 代 码 中 , 全 局 名 称 Categories 在 函 数 范 围 内 被 隐 藏 , 使 得 缺 省 参 量 表 达 式无 效 。

其 它 考 虑

缺 省 参 量 不 被 认 为 是 函 数 类 型 的 一 部 分 , 因 此 , 不 被 用 于 选 择 重 载 的 函 数 。 两 个函 数 若 仅 在 其 缺 省 参 量 上 不 同 , 则 被 认 为 是 多 个 定 义 而 不 是 重 载 的 函 数 。

不 能 为 重 载 的 运 算 符 提 供 缺 省 参 量 。

函 数 定 义

函 数 定 义 与 函 数 说 明 的 不 同 点 在 于 它 们 提 拱 函 数 体 , 即 组 成 函 数 的 代 码 。

语 法

函 数 定 义 :

说 明 指 示 符 opt 说 明 符 ctor 初 始 化 器 opt 函 数 体函 数 体 :

复 合 语 句 正 如 “ 函 数 ” 中 讨 论 的 语 法 中 说 明 符 的 格 式 为 :

dname( 参 量 说 明 表 ) cv 修 饰 符 表 opt

在 参 量 说 明 表 中 说 明 的 形 参 是 在 函 数 体 的 范 围 内 。

图 7.3 显 示 了 一 个 函 数 定 义 的 各 个 部 分 。 阴 影 区 域 是 函 数 体 。

第 7 章 说 明 符 - 图3

图 7.3 函 数 定 义 的 各 个 部 分

说 明 符 语 法 的 cv 修 饰 符 表 元 素 指 示 了 如 何 对 待 this 指 针 的 ; 它 仅 与 类 成 员 函 数一 起 使 用 。

语 法 中 的 ctor 初 始 化 器 元 素 仅 被 用 于 构 造 函 数 中 , 它 的 目 的 是 允 许 对 基 类 和 包 含的 对 象 进 行 初 始 化 ( 有 关 ctor 初 始 化 器 的 更 多 信 息 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 初 始 化 基 类 和 成 员 ” ) 。

带 可 变 的 参 量 表 的 函 数

要 求 可 变 表 的 函 数 在 参 量 表 中 使 用 省 略 号 (...) 进 行 说 明 , 如 本 章 前 面 “ 可 变 的参 量 表 ” 中 所 描 述 。 要 访 问 使 用 此 方 法 传 递 给 函 数 的 参 量 , 则 使 用 STDARG.H 标准 包 括 文 件 中 所 描 述 的 类 型 和 宏 。

以 下 例 子 给 出 了 va_start 、 va_arg 和 va_end 宏和 va_list 类 型 ( 在 STDARG.H 中 说 明 的 ) 是 如 何 一 起 工 作 的 。

#include <stdio.h> #include <stdarg.h>

//showVar 的 说 明 , 但 不 是 定 义

int ShowVar(char *szTypes,...);

void main()

{

ShowVar("fcsi",32.4f,'a',"Test string",4);

}

//showvar 带 一 个 “ fcs i ” 形 式 的 格 式 化 字 符 串 , 其 中 每 个 字 符 指 示 那 个 位 置 的

// 参 量 类 型

//

//i=int

//f=float

//c=char

//s=string(char*)

//

// 以 下 格 式 说 明 是 一 个 n 个 参 量 的 表 , 其 中 n==strlen(szTypes) void ShowVar(char *szTypes,...)

{

va_list vl;

int i;

//szTypes 是 指 定 的 最 后 的 参 量 ; 所 有 其 它 的 必 须 使 用 可 变 的 参 量 宏 进 行 访问

va_start(vl,szTypes);

// Step through the list

for(i=0;szTypes[i]!='\0';++i)

{

union printable_t

{

int i;

float f;

char c;

char *s;

} Printable;

switch (szTypes[i]) // 预 期 的 类 型

{

case 'i':

Printable.i=va_arg(vl,int);

printf("%i\n",Printable.i);

break;

case 'f':

Printable.f=va_arg(vl,float);

printf("%f\n",Printable.f);

break;

case 'c':

Printable.c=va_arg(vl,char);

printf("%f\n",Printable.f);

break;

case 's':

Printable.s=va_arg(vl,char *);

printf("%s\n",Printable.s);

break;

default:

break;

}

}

va_end(vl);

}

前 面 的 例 子 说 明 了 以 下 这 些 重 要 概 念 :

  • 一 个 表 标 记 将 必 须 在 任 何 可 变 的 参 量 被 访 问 之 前 建 立 为 一 个va_list 类 型 的 变 量 , 在 前 面 的 例 子 中 , 标 识 符 被 叫 做 vl 。

  • 单 个 参 量 使 用 va_arg 宏 进 行 访 问 ,va_arg 宏 需 要 被 告 知 要 检 索 的 参量 的 类 型 , 这 样 它 才 能 从 堆 栈 中 传 递 正 确 数 目 的 字 节 。 如 果 一 个 与调 用 程 序 提 供 的 大 小 不 同 的 不 正 确 的 类 型 被 指 定 给 va_arg, 则 结 果是 难 以 预 料 的 。

  • 使 用 va_arg 宏 获 得 的 结 果 应 该 被 显 式 地 造 型 转 换 到 所 希 望 的 类 型 。

  • va_end 宏 必 须 被 调 用 以 终 止 可 变 的 参 量 处 理 。

初 始 化 器

说 明 符 可 指 定 对 象 的 初 始 值 。 为 const 类 型 的 对 象 指 定 一 个 值 的 唯 一 方 法 是 在说 明 符 中 指 定 。 说 明 符 中 指 定 这 个 初 始 值 的 部 分 被 称 为 “ 初 始 化 器 ”。

语 法

初 始 化 器

= 赋 值 表 达 式

={ 初 始 化 器 表 , opt }

( 表 达 式 表 ) 初 始 化 器 表 :

表 达 式

初 始 化 器 表 , 表 达 式

{ 初 始 化 器 表 , opt}

初 始 化 器 有 两 个 基 本 类 型 :

  • 使 用 等 号 语 法 调 用 的 初 始 化 器 。

  • 使 用 函 数 形 式 调 用 的 初 始 化 器 。

仅 仅 只 有 构 造 函 数 的 类 的 对 象 可 以 用 函 数 形 式 语 法 进 行 初 始 化 。 两 个 语 法 格 式的 不 同 还 在 于 访 问 控 制 和 临 时 对 象 的 潜 在 使 用 。 考 虑 以 下 代 码 , 用 初 始 化 器 说 明一 些 说 明 符 :

int i=7;// 使 用 等 号 语 法

Customer Cust ("Taxpayer, Joe", // 使 用 函 数 形 式 语 法

"14 Cherry Lane", // 构 造 函 数 的 要 求

"Manteca",

"CA");

自 动 的 、 寄 存 器 的 、 静 态 的 和 外 部 变 量 的 说 明 可 以 包 含 初 始 化 器 。 但 是 外 部 变量 的 说 明 仅 当 变 量 没 有 被 说 明 为 extern 时 可 以 包 含 初 始 化 器 。

这 些 初 始 化 器 可 以 包 含 包 括 在 当 前 范 围 内 的 常 量 和 变 量 。 初 始 化 器 在 程 序 流 遇到 说 明 的 地 方 被 求 值 , 或 对 全 局 静 态 对 象 和 变 量 在 程 序 开 始 处 求 值 ( 有 关 全 局 静态 对 象 的 初 始 化 的 更 多 信 息 , 参 见 第 2 章“ 基 本 概 念 ”中 的“ 额 外 的 开 始 考 虑 ”) 。

指 向 const 对 象 的 指 针 的 初 始 化

一 个 指 向 const 对 象 的 指 针 可 以 用 一 个 指 向 非 const 对 象 的 指 针 进 行 初 始 化 , 但反 之 不 可 。

例 如 , 以 下 初 始 化 是 合 法 的 :

Window StandardWindow;

const Window* pStandard Window(&Standard Window);

在 上 面 代 码 中 , 指针 pStandardWindow 被 说 明 为 指 向 一 个 const 对 象 的 指 针 。 尽管 StandardWindow 没 有 被 说 明 为 const, 但 该 说 明 是 可 接 受 的 , 因 为 它 不 允 许 被说 明 为 const 的 对 象 访 问 一 个 const 对 象 。 与 此 相 反 的 如 下 :

const Window StandardWindow;

Window* pStandardWindow(StandardWindow);

上 面 的 代 码 显 式 说 明 StandardWindow 为 一 个 const 对 象 。 用 StandardWindow 的 地 址 初 始 化 非 常 量 指 针 pStandardWindow 会 产 生 一 个 错 误 , 因 为 它 允 许 通 过 指针 访 问 const 对 象 , 即 它 允 许 从 对 象 中 移 走 const 属 性 。

未 初 始 化 的 对 象

没 有 用 初 始 化 器 说 明 的 static 存 储 类 的 对 象 和 简 单 变 量 被 确 保 初 始 化 为 一 个 位模 式 0 。 对 自 动 的 或 寄 存 器 存 储 类 的 未 初 始 化 的 对 象 , 则 不 产 生 此 种 特 殊 处 理 。它 们 有 未 定 义 的 值 。

初 始 化 静 态 成 员

静 态 成 员 初 始 化 出 现 在 类 范 围 中 。 因 此 , 它 们 可 以 访 问 其 它 成 员 数 据 或 函 数 。 例如 :

class DialogWindow

{

public:

static short GetTextHeight(); private:

static short nTextHeight;

};

short DialogWindow::nTextHeight=GetTextHeight();

注 意 在 前 面 的 静 态 成 员 nTextHeight 的 定 义 中 ,GetTextHeight 隐 含 指DialogWindow::GetTextHeight 。

初 始 化 集 合

  • 个 集 合 类 型 是 一 个 这 样 的 数 组 、 类 或 结 构 类 型 :

    • 没 有 构 造 函 数

    • 没 有 非 公 有 成 员

    • 没 有 基 类

    • 没 有 虚 拟 函 数

集 合 的 初 始 化 器 可 以 指 定 为 括 在 花 括 号 中 的 以 逗 号 分 隔 的 值 表 。 例 如 , 此 代 码 说明 了 一 个 10 个 int 的 数 组 , 并 初 始 化 为 :

int rgiArray[10]={9,8,4,6,5,6,3,5,6,11};

初 始 化 器 以 递 增 的 下 标 顺 序 存 储 在 数 组 元 素 中 。 因 此 ,rgiArray[0] 是 9 、rgiArray[1] 是 8, 等 等 , 直 到 rgiArray[9] 是 11 。 要 初 始 化 一 个 结 构 , 使 用 这 样的 代 码 :

struct RCPrompt

{

short nRow;

short nCol;

char *szPrompt;

};

RCPrompt rcContinueYN={24,0,"Continue (Y/N ? )"}-->;

集 合 初 始 化 器 表 的 长 度

如 果 一 个 集 合 初 始 化 器 表 比 正 被 初 始 化 的 数 组 或 类 类 型 短 , 则 未 指 定 初 始 化 器 的

0 被 存 储 在 元 素 中 。 因 此 , 以 下 两 个 说 明 是 等 价 的 :

// 显 式 初 始 化 所 有 元 素

int rgiArray[5]={3,2,0,0,0};

// 允 许 保 留 元 素 为 0 初 始 化 的int rgiArray[5]={3,2}

正 如 这 所 看 到 的 , 初 始 化 器 可 被 截 断 , 但 提 供 太 多 的 初 始 化 器 将 产 生 一 个 错 误 。

初 始 化 包 含 集 合 的 集 合

某 些 集 合 包 含 其 它 的 集 合 , 例 如 , 数 组 的 数 组 、 结 构 的 数 组 或 由 其 它 结 构 组 成 的结 构 , 对 这 种 结 构 可 以 通 过 用 带 花 括 号 的 表 按 其 出 现 的 顺 序 对 每 个 初 始 化 而 提 供初 始 化 器 , 例 如 :

// 说 明 一 个 RCPrompt 类 型 的 数 组RCPrompt rgRCPrompt[4]=

{{4,7,"Options Are:"}-->,

{6, 7,"1. Main Menu"}-->,

{8, 7,"2. Print Menu"}-->,

{10,7,"3. File Menu"}-->};

注 意 , rgRCPrompt 被 用 花 括 号 包 围 的 表 的 花 括 号 包 围 表 初 始 化 。 封 闭 的 花 括 号不 是 语 法 上 要 求 的 , 但 它 们 使 说 明 更 清 晰 , 以 下 程 序 例 子 显 示 了 一 个 二 维 数 组 是如 何 用 这 样 一 个 初 始 化 器 填 充 的 :

#include <iostream.h>

void main( )

{

int rgI[2][4]={1,2,3,4,5,6,7,8};

for(int i=0;i<2; ++i)

for(int j=0;j<4; ++j)

cout << "rgI[" << i << "][" << j << "]="

<< rgI[i][j] << endl;

}

程 序 的 输 出 为 :

rgI[0][0]=1

rgI[0][1]=2

rgI[0][2]=3

rgI[0][3]=4

rgI[1][0]=5

rgI[1][1]=6

rgI[1][2]=7

rgI[1][3]=8

短 初 始 化 表 仅 与 显 式 子 集 合 初 始 化 器 一 起 且 被 花 括 号 括 住 时 才 是 可 用 的 。 如 果rgI 说 明 为 :

int rgI[2][4]={{1,2},{3,4}};

则 程 序 的 输 出 将 是 :

rgI[0][0]=1

rgI[0][1]=2

rgI[0][2]=0

rgI[0][3]=0

rgI[1][0]=3

rgI[1][1]=4

rgI[1][2]=0

rgI[1][3]=0

不 完 整 类 型 的 初 始 化

不 完 整 类 型 , 如 没 有 限 定 边 界 的 数 组 类 型 , 可 以 初 始 化 如 下 :

char HomeRow[]={ ′ a ′ , ′ s ′ , ′ d ′ , ′ f ′ , ′ h ′ , ′ i ′ , ′ k ′ , ′ l

′ }

编 译 器 从 提 供 的 初 始 化 器 数 目 计 算 数 组 的 大 小 。

不 完 整 类 型 , 如 已 说 明 但 未 定 义 的 指 向 类 类 型 的 指 针 , 其 说 明 如 下 : class DefinedElseWhere; // 类 定 义 在 别 处

class DefinedHere

{

...

friend cl ass definedElsewhere;

}

使 用 构 造 函 数 初 始 化

类 类 型 对 象 通 过 调 用 类 的 合 适 的 构 造 函 数 进 行 初 始 化 。 关 于 类 类 型 初 始 化 的 完整 信 息 参 见 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 显 式 初 始 化 ”。

初 始 化 器 和 联 合

union 类 型 的 对 象 用 一 个 单 值 进 行 初 始 化 ( 如 果 联 合 没 有 一 个 构 造 函 数 ) 。 这 是以 这 两 种 方 法 之 一 完 成 的 :

  • 用 同 一 个 union 类 型 的 另 一 个 对 象 初 始 化 联 合 , 例如 : struct Point

{

unsigned x;

unsigned y;

}

union PtLong

{

long l;

Point pt;

};

...

PtLong ptOrigin;

PtLong ptCurrentT=ptOrigin;

在 上 面 代 码 中 ,ptCurrent 用 ptOrigin 值 相 同 类 型 的 一 个 对 象 进 行 初 始 化 。

  • 将 第 一 个 成 员 用 花 括 号 括 起 的 初 始 化 器 联 合 起 来 初 始 化 。 例 如 : PtLong ptCurrent={0x0a000aL};

初 始 化 字 符 数 组

字 符 数 组 可 用 以 下 两 种 方 法 之 一 进 行 初 始 化 :

  • 单 个 地 , 如 :

char chABCD[4]={ ′ a ′ , ′ b ′ , ′ c ′ , ′ d ′ };

  • 用 一 个 字 符 串 , 如 ; char chABCD[5]="abcd";

在 第 二 种 情 况 下 , 即 字 符 数 组 用 一 个 字 符 串 进 行 初 始 化 , 编 译 器 添 加 一 个 尾 部 的'\0'( 字 符 串 结 束 字 符 ) 。 因 此 , 数 组 必 定 至 少 比 串 中 字 符 的 个 数 大 1 。

因 为 大 多 数 字 符 串 处 理 使 用 标 准 库 函 数 或 依 赖 于 尾 部 字 符 串 结 束 字 符 的 出 现 , 所以 常 见 用 字 符 串 初 始 化 未 限 定 边 界 的 数 组 说 明 如 下 :

char chABCD[]="ABCD";

初 始 化 引 用

引 用 的 类 型 变 量 必 须 用 派 生 引 用 类 型 对 象 或 用 可 以 转 换 到 派 生 引 用 类 型 派 对 对象 进 行 初 始 化 。 例 如 :

int iVar; long lVar;

long& LongRef1=lVar; // 不 需 要 转 换long& LongRef2=iVar; // 错 误

const LongRef3=iVar; // 可 行

longRef1=23L; // 通 过 一 个 引 用 改 变 lVar longRef2=11L; // 通 过 一 个 引 用 改 变 iVar longRef3=11L; // 错 误

使 用 一 个 临 时 对 象 去 初 始 化 一 个 引 用 的 唯 一 方 法 是 去 初 始 化 一 个 常 量 临 时 对象 。 一 旦 初 始 化 了 , 一 个 引 用 类 型 变 量 总 是 指 向 相 同 的 对 象 ; 它 不 能 被 改 为 指 向另 一 个 对 象 。

尽 管 语 法 可 以 相 同 , 但 引 用 类 型 变 量 的 初 始 化 和 引 用 类 型 变 量 的 赋 值 在 语 义 上 是不 同 的 。 在 前 面 的 例 子 中 , 改变 iVar 和 lVar 的 赋 值 看 上 去 与 初 始 化 相 似 , 但 有不 同 的 效 果 。 初 始 化 指 定 引 用 类 型 变 量 指 向 的 对 象 , 赋 值 通 过 引 用 赋 给 被 指 向 的对 象 。

因 为 传 递 给 一 个 函 数 一 个 引 用 类 型 的 参 量 和 从 一 个 函 数 返 回 一 个 引 用 类 型 值 都是 初 始 化 , 所 以 函 数 的 形 参 被 正 确 地 初 始 化 , 正 如 返 回 的 引 用 。

引 用 类 型 变 量 仅 在 以 下 情 况 可 以 不 用 初 始 化 器 进 行 初 始 化 :

  • 函 数 说 明 ( 原 型 ) 。 例 如 : int func(int&);

  • 函 数 返 回 类 型 说 明 , 例 如 : int & func(int&);

  • 引 用 类 型 类 成 员 的 说 明 , 例 如 :

class C

{

public:

int& i;

};

  • 被 显 式 指 定 为 extern 的 变 量 的 说 明 , 例如 : extern int& iVal;

当 初 始 化 一 个 引 用 类 型 变 量 时 , 编 译 器 使 用 图 7.4 中 所 示 的 决 策 图 , 以 在 创 建 一个 对 象 的 引 用 还 是 创 建 一 个 引 用 指 向 的 临 时 对 象 之 间 进 行 选 择 。

volatile 类 型 的 引 用 ( 说 明 为 :volatile 类 型 名 称 & 标 识 符 ) 可 以 用 相 同 类 型 的volatile 对 象 或 用 没 有 被 说 明 为 volatile 的 对 象 进 行 初 始 化 。 但 是 它 们 不 能用 那 个 类 型 的 const 对 象 进 行 初 始 化 。 类 似 地 ,const 类 型 的 引 用 ( 说 明 为 :const 类 型 名 称 & 标 识 符 ) 可 以 用 相 同 类 型 的 const 对 象 ( 或 任 何 可 转 换 到 那 个 类 型 的或 没 有 被 说 明 为 const 的 对 象 ) 进 行 初 始 化 。 但 是 它 们 不 能 用 那 个 类 型 的volatile 对 象 进 行 初 始 化 。 没 有 用 const 或 volatile 关 键 字 限 定 的 引 用 , 仅 能用 既 不 是 说 明 为 const 也 不 是 说 明 为 volatile 的 对 象 进 行 初 始 化 。

第 7 章 说 明 符 - 图4

图 7.4 引 用 类 型 初 始 化 的 决 策 图

8 章 类

这 一 章 介 绍 C++ 的 类 , 类 在 程 序 中 引 入 了 用 户 自 定 义 的 类 型 , 类 可 以 包 含 数 据 和函 数 。

在 传 统 程 序 设 计 语 言 中 用 户 自 定 义 类 型 是 数 据 的 集 合 。 它 们 放 在 一 起 用 以 描 述对 象 的 属 性 和 状 态 。 C++ 中 的 类 类 型 使 用 户 不 仅 能 够 描 述 对 象 的 属 性 和 状 态 , 还可 以 定 义 对 象 的 行 为 。

本 章 包 括 下 面 一 些 主 题 :

  • 类 的 概 述

  • 类 名 称

  • 类 成 员

  • 成 员 函 数

  • 静 态 数 据 成 员

  • 联 合

  • 位 域

  • 嵌 套 类 说 明

  • 类 范 围 中 的 类 型 名 称

类 的 概 述

类 类 型 用 关 键 字 class , struct 和 union 定 义 。 简 单 地 说 , 用 这 三 个 关 键 字 定 义的 类 型 都 称 作 类 说 明 (class declaration) 。 但 是 在 讨 论 语 言 成 分 时 , 用 不 同 的关 键 字 定 义 的 类 , 其 行 为 是 不 同 的 。

语 法

类 名 称 :

标 识 符

  • 个 类 的 变 量 和 函 数 称 为 类 的 成 员 。 在 定 义 一 个 类 的 时 候 通 常 也 提 供 一 些 如 下的 成 员 ( 尽 管 都 是 任 选 的 ):

    • 类 的 数 据 成 员 , 定 义 一 个 类 类 型 的 某 个 对 象 的 属 性 和 状 态 。

    • 一 个 或 多 个 构 造 函 数 , 用 来 初 始 化 类 类 型 的 对 象 。 构 造 函 数 在 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 构 造 函 数 ” 一 节 中 介 绍 。

    • 一 个 或 多 个 析 构 函 数 , 主 要 完 成 一 些 清 除 工 作 , 如 : 回 收 动 态 分 配 的存 储 器 或 关 闭 文 件 。 析 构 函 数 在 第 11 章 “ 特 殊 成 员 函 数 ” 的 “ 析构 函 数 ” 一 节 中 详 述 。

    • 一 个 或 多 个 成 员 函 数 用 来 定 义 对 象 的 行 为 。

定 义 类 类 型

类 类 型 的 定 义 用 类 指 示 符 (class-specifiers) 。 类 类 型 的 说 明 使 用 复 杂 类 型 指示 符 。 在 第 6 章 “ 说 明 ” 中 的 “ 类 型 说 明 符 ” 一 节 介 绍 。

语 法

类 说 明 符 :

类头 { 成 员 表 opt } 类 头 :

类 关 键 字 imodel opt 标 识 符 opt 基 类 说 明 opt

类 关 键 字 imode l opt 类 名 称 opt 基 类 说 明 opt

类 关 键 字 :

class

struct

union imodel:

__ declspec

当 编 译 器 处 理 完 类 名 称 以 后 ( 在 处 理 类 体 之 前 ), 类 名 称 立 即 被 当 作 标 识 符 , 因 此类 名 称 可 以 用 来 说 明 类 成 员 。 这 使 得 用 户 可 以 说 明 自 引 用 型 的 数 据 结 构 。 如 下 : class Tree

{

public:

void *Data;

Tree *Left;

Tree *Right;

};

结 构 、 类 和 联 合

三 种 类 类 型 分 别 是 结 构 、 类 和 联 合 。 他 们 的 说 明 是 用 struct 、 class 和 union 关 键 字 ( 见 关 键 字 语 法 ) 。 表 8.1 显 示 了 三 种 类 类 型 之 间 的 不 同 。

表 8.1 结 构 、 类 和 联 合 的 访 问 控 制 和 约 束

结构 类 联合

类关键字 struct 类关键字是 class 类 关 键 字 是 union

缺省访问控制是公有的 缺 省 访 问 控 制 是 私 有

缺省访问控制是公有的

无使用约束 无使用约束 任 何 时 候 只 能 使 用 一 个成员

无 名 类 类 型

类 可 以 是 无 名 的 。也 即 , 你 可 以 说 明 一 个 不 带 标 识 符 的 类 。这 在 你 用 一 个 typedef 名 称 去 代 替 一 个 类 的 类 名 称 时 很 有 用 。 如 下 :

typedef struct

{

unsigned x;

unsigned y;

} POINT;

注 意 : 上 面 例 子 中 无 名 类 的 使 用 对 保 持 已 有 C 代 码 的 兼 容 性 是 很 有 用 的 。 在 很 多C 代 码 中 ,typedef 与 无 名 结 构 一 起 使 用 是 非 常 普 遍 的 。 当 你 要 引 用 一 个 类 的 成

员 , 并 表 现 出 这 个 成 员 并 不 是 被 包 含 在 一 个 单 独 的 类 中 时 , 无 名 类 也 同 样 是 很 有用 的 , 如 下 所 示 :

struct PTValue

{

POINT ptLoc;

union

{

int iValue;

long lValue;

};

};

PTValue ptv;

在 上 面 的 代 码 中 ,iValue 可 以 用 对 象 成 员 选 择 符 (.) 来 访 问 。 如 下 : int i=ptv.iValue;

无 名 类 要 服 从 一 些 特 定 的 限 制 ( 有 关 无 名 union 的 详 情 见 本 章 后 面 的 “ 联 合 ” 一节 ) 。 一 个 无 名 类 :

  • 不 能 有 构 造 函 数 或 析 构 函 数

  • 不 能 作 为 参 数 传 递 给 函 数 ( 除 非 用 “ ... ” 避 开 类 型 检 查 )

  • 也 不 能 作 为 函 数 的 返 回 值

类 的 定 义 点

  • 个 类 的 定 义 是 在 其 类 说 明 符 之 后 。 成 员 函 数 不 必 因 为 类 的 详 细 定 义 而 也 要 马上 定 义 。

研 究 下 面 的 例 子 :

class Point //Point 类

{ // 详 细 定 义public:

Point()

{cx=cy=0;}// 定 义 构 造 函 数

Point(int x,int y)

{cx=x,cy=y;} // 定 义 构 造 函 数

unsigned &x(unsigned); // 定 义 访 问 器

unsigned &y(unsigne d ); // 定 义 访 问 器private:

unsigned cx,cy;

};

尽 管 两 个 访 问 器 函 数 (x 和 y) 还 没 有 被 定 义 , 但 类 Point 已 经 详 细 定 义 了 的 ( 访 问器 函 数 主 要 用 来 提 供 对 数 据 成 员 的 安 全 访 问 ) 。

类 类 型 对 象

  • 个 对 象 在 执 行 环 境 中 是 一 块 类 型 化 的 存 储 区 域 ; 它 不 但 保 留 了 状 态 信 息 , 也 定

义 了 行 为 。 类 类 型 对 象 用 类 名 称 来 定 义 。 考 察 下 面 的 代 码 段 : class Account // 类 名 是 Account

{

public:

Account(); // 缺 省 构 造 函 数

Account(double) ; // 从 double 类 型 来 构 造

double& Deposit(double);

double& Withdraw(double,int);

...

};

Account CheckingAccount; // 定 义 类 类 型 对 象

面 的 代 码 说 明 了 一 个 类 ( 新 的 类 型 ) 称 为 Account, 然 后 用 这 种 新 的 类 型 定 义 了 一个 对 象 叫 CheckingAccount 。

C++ 为 类 型 对 象 提 供 了 如 下 一 些 操 作 :

  • 赋 值 。 一 个 对 象 能 够 赋 值 给 另 一 个 。 这 一 操 作 的 缺 省 行 为 是 成 员 方式 (memberwise) 的 拷 贝 。 用 户 可 以 提 供 一 个 自 定 义 的 赋 值 操 作 以 取代 缺 省 行 为 。

  • 用 拷 贝 构 造 函 数 进 行 初 始 化

下 面 是 用 户 自 定 义 的 拷 贝 构 造 函 数 进 行 的 初 始 化 的 例 子 :

  • 对 某 对 象 进 行 明 确 地 初 始 化 :

Point myPoint=thatPoint:

把 myPoint 说 明 为 一 个 Point 类 型 的 对 象 并 把 它 初 始 化 为 thatPoint 的 值 。

  • 作 为 传 递 参 数 而 引 起 的 初 始 化 。 对 象 能 以 传 值 或 引 用 形 式 传 递 给 函数 。 如 果 它 们 是 以 传 值 形 式 传 递 给 函 数 的 , 则 每 个 对 象 的 拷 贝 会 传递 给 函 数 。 创 建 此 拷 贝 的 缺 省 办 法 是 一 个 成 员 方 式 (memberwise) 的拷 贝 。 当 然 也 可 以 用 自 定 义 的 拷 贝 构 造 函 数 来 代 替 缺 省 的 拷 贝 构 造函 数 ( 是 一 种 构 造 函 数 , 它 带 有 唯 一 的 对 类 的 引 用 型 的 参 数 ) 。

  • 由 于 初 始 化 函 数 返 回 值 而 引 起 的 初 始 化 , 对 象 有 能 够 以 传 值 或 引 用方 式 从 函 数 返 回 。 对 于 以 传 值 方 式 返 回 对 象 的 缺 省 方 法 也 是 成 员 方式 的 拷 贝 ; 当 然 同 样 可 以 由 自 定 义 的 拷 贝 构 造 函 数 所 代 替 。 由 引 用方 式 ( 用 指 针 或 引 用 类 型 ) 返 回 的 对 象 , 对 于 调 用 函 数 来 说 是 不 能 作为 自 动 型 或 局 部 型 对 象 的 。 如 果 这 样 的 话 , 由 返 回 值 所 指 引 的 对 象在 其 能 被 使 用 之 前 就 超 出 了 其 范 围 。

第 12 章 “ 重 载 ” 中 的 “ 重 载 操 作 符 ” 一 节 , 解 释 如 何 在 基 于 类 方 式 下 重 定 义 这

些 操 作 符 。

空 类

你 可 以 说 明 一 个 空 类 , 但 是 这 种 类 型 的 对 象 有 非 零 的 值 。 下 面 的 例 子 显 示 了 这 一点 :

#include(iostream.h)

class NoMembers

{

};

void main()

{

NoMembers n; //NOMembers 型 对 象

cout << "The size of an object of empty class is:"

<< sizeof n << endl;

}

上 面 程 序 的 输 出 是 :

The size of an object of empty class is:1.

为 这 种 对 象 分 配 的 存 储 器 是 非 零 的 。 因 而 , 不 同 的 对 象 有 不 同 的 地 址 。 由 于 具 有不 同 的 地 址 , 就 有 可 能 通 过 比 较 对 象 指 针 鉴 别 不 同 的 对 象 。 同 样 , 在 数 组 中 每 个成 员 数 组 必 须 有 不 同 的 地 址 。

Microsoft 特 殊 处 →

  • 个 空 基 类 在 其 派 生 类 中 占 用 零 个 字 节 。

Microsoft 特 殊 处 结 束

类 名 称

在 程 序 中 类 说 明 引 入 了 新 的 类 型 ( 以 类 名 称 来 称 呼 ), 这 些 类 说 明 在 给 定 的 转 换 单元 中 就 像 类 定 义 一 样 。 在 每 个 转 换 单 元 中 , 对 于 给 定 的 类 类 型 仅 仅 只 可 以 有 一 个该 类 型 的 定 义 。 用 这 些 新 的 类 型 , 用 户 可 以 定 义 对 象 。 同 时 , 编 译 器 也 可 以 通 过

类 型 检 查 发 现 对 这 些 对 象 的 不 兼 容 的 类 型 操 作 。下 面 是 类 型 检 查 的 例 子 :

class Point

{

public:

unsigned x,y;

};

class Rect

{

public:

unsigned x1,y1,x2,y2;

}

// 带 有 两 个 参 数 的 函 数 原 型 , 一 个 是 Point 类型 , 另 一 个 是 Rect 型 的 引 用int PtInRect (Point,Rect &);

...

Point pt;

Rect rect;

rect=pt; // 错 误 , 类 型 不 匹 配pt=rect; // 错 误 , 类 型 不 匹 配

// 错 误 ,PtInRect 的 参 数 反 了

cout << "Point is" << PtInRect(rect,pt) ? "" : "not"

<<"in rectangle "<< endl;

正 如 上 面 例 子 所 显 示 的 , 对 于 一 个 类 类 型 对 象 上 的 操 作 ( 如 赋 值 和 参 量 传 递 ) 和 内部 类 型 对 象 一 样 遵 循 着 同 一 种 类 型 检 测 约 束 。

因 为 编 译 器 能 够 区 分 不 同 的 类 类 型 , 函 数 可 以 在 基 于 类 类 型 参 数 以 及 内 置 参 量 的原 则 下 被 重 载 。 有 关 重 载 函 数 的 更 多 信 息 参 见 第 7 章 “ 说 明 ” 中 的 “ 函 数 重 载 ” 以 及 第 12 章 的 “ 重 载 ”。

说 明 及 访 问 类 名 称

可 以 在 全 局 范 围 或 在 类 范 围 中 说 明 类 名 。 如 果 在 类 范 围 中 说 明 类 名 称 , 它 们 实 际上 指 的 是 “ 嵌 套 类 ”。

Microsoft 特 殊 处 →

在 Microsoft C++ 的 局 部 类 说 明 中 不 允 许 有 函 数 定 义 。

Microsoft 特 殊 处 结 束

在 类 的 范 围 中 引 入 的 新 的 类 名 称 会 隐 藏 在 同 一 封 闭 块 中 的 同 名 元 素 。 要 引 用 因上 述 说 明 隐 藏 的 名 称 , 只 能 通 过 全 类 型 指 示 符 , 下 面 的 例 子 是 一 个 用 全 类 型 指 示符 引 用 一 个 隐 藏 名 称 的 例 子 :

struct A// 全 局 范 围 中 定 义 A

{

int a;

};

void main()

{

char A='a';// 把 名 称 A 重 定 义 为 一 个 对 象

struct A AObject;

...

}

因 为 名 称 A 所 指 示 的 结 构 被 由 A 所 指 示 的 char 对 象 所 隐 藏 了 , 因 而 定 义 一 个 类型 A 的 对 象 AObject 时 必 须 用 类 关 键 字 struct 。

你 当 然 可 以 用 类 关 键 字 说 明 一 个 类 而 不 提 供 该 类 的 定 义 。 这 种 无 定 义 的 类 说 明只 是 为 超 前 引 用 而 引 入 了 一 个 类 名 称 。 这 种 技 术 在 设 计 要 在 友 元 说 明 中 引 用 其它 的 类 的 类 中 非 常 有 用 , 当 然 在 类 名 称 必 须 在 头 文 件 中 出 现 , 而 其 定 义 又 不 需 要时 , 这 种 技 术 也 很 有 用 。 例 如 :

//RECT.H

class Point; // 类 Point 的 无 定 义 说 明class Line

{

public:

int Draw(Point &ptFrom,Point *ptTo);

...

};

在 上 面 的 例 子 中 , 必 须 提 供 Point 类 名 称 , 但 不 必 引 入 该 名 称 的 定 义 性 说 明 。

typedef 语 句 和 类

使 用 typedef 语 句 去 命 名 一 个 类 类 型 是 把 一 个 typedef 名 称 变 成 一 个 类 名 称 。更 多 的 详 情 参 见 第 6 章 “ 说 明 ” 中 的 “ 类 型 指 示 符 ”。

类 成 员

类 可 以 有 如 下 类 型 的 成 员 :

  • 成 员 函 数

  • 数 据 成 员

  • 类 ( 包 括 类 、 结 构 和 联 合 ), 参 见 “ 嵌 套 类 说 明 和 联 合 ”

  • 枚 举

  • 位 域

  • 友 元

  • 类 型 名 称

注 意 : 友 元 是 被 类 说 明 所 包 含 的 , 它 们 包 括 在 一 个 预 先 的 列 表 中 , 然 而 它 们 并 不 是真 正 的 类 成 员 , 因 为 它 们 不 在 类 的 范 围 之 中 。

语 法

成 员 表 :

成 员 说 明 成 员 表 o pt

访 问 指 示 符 : 成 员 表 opt

成 员 说 明 :

decl 指示 opt 成 员 说 明 符 表 opt ;

函 数 定 义 opt;

限 定 名 ; 成 员 说 明 符 表 :

成 员 说 明 符

成 员 说 明 符 表 , 成 员 说 明 符成 员 说 明 符 ;

说 明 符 纯 指 示 符 opt

标 识 符 opt: 常 量 表 达 式纯 指 示 符 :

=0

成 员 表 的 目 的 :

  • 说 明 给 定 类 的 全 套 成 员

  • 说 明 同 各 个 类 成 员 相 联 系 的 访 问 ( 公 有 的 、 私 有 的 或 保 护 的 )

在 成 员 表 的 说 明 中 , 一 个 成 员 只 能 说 明 一 次 。 成 员 的 再 次 说 明 会 引 出 错 误 消 息 。因 为 一 个 成 员 表 就 是 一 整 套 的 类 成 员 , 故 不 能 在 随 后 的 类 说 明 中 为 给 定 类 增 加 成员 。

成 员 说 明 符 中 也 不 能 包 含 有 初 始 化 器 , 提 供 初 始 化 器 会 产 生 一 个 错 误 消 息 如 下 : class CantInit

{

public:

long l=7;// 错 误 : 试 图 初 始 化 类 成 员

static int i=9;// 错 误 : 必 须 在 类 说 明 之 外 定 义 并 初 始 化

};

因 为 对 每 个 给 定 类 类 型 的 对 象 都 要 单 独 创 建 静 态 成 员 实 例 。 正 确 的 初 始 化 办 法是 用 类 的 构 造 函 数 去 初 始 化 成 员 的 数 据 ( 构 造 函 数 在 第 11 章 “ 特 殊 成 员 函 数 ” 中 的 “ 构 造 函 数 ” 一 节 中 论 述 ) 。 对 于 一 个 给 定 类 类 型 的 所 有 对 象 仅 有 一 个 共 享的 静 态 数 据 成 员 拷 贝 , 静 态 数 据 成 员 必 须 在 文 件 范 围 中 定 义 才 能 初 始 化 ( 有 关 静态 数 据 成 员 的 详 情 见 本 章 后 面 的 “ 静 态 数 据 成 员 ” ) 。 下 面 的 例 子 显 示 了 如 何 进行 这 些 初 始 化 :

class CanInit

{

public:

CanInit(){l=7;} // 当 新 的 CanInit 对 象 创 建 时 初 始 化 l

long l;

static int i:

static int j;

}

int CanInit::i=15; //i 在 文 件 范 围 中 定 义 并 初 始 化 为 15

// 初 始 化 是 在 CanInit 的 范 围 中 定 值 的

int CanInit::j=i; // 初 始 化 器 的 右 边 在 要 被 初 始 化 的 对 象 范 围 中

注 意 : 类 名 称 CanInit 必 须 前 导 于 i 以 说 明 i 被 定 义 为 CanInit 的 成 员 。

类 成 员 说 明 语 法

成 员 数 据 不 能 说 明 为 auto 、 extern 和 register 存 储 类 型 。 然 而 它 们 能 够 被 说明 为 static 存 储 类 型 。

decl 指 示 符 在 成 员 函 数 的 说 明 中 可 以 被 省 略 ( 有关 decl 指 示 符 的 情 况 见 第 6 章“ 说 明 ” 中 的 “ 指 示 符 ” 一 节 , 以 及 本 章 后 面 的 成 员 函 数 和 第 7 章 “ 说 明 ” 中 的“ 函 数 ” 一 节 ) 。 因 而 下 面 的 代 码 合 法 地 说 明 了 一 个 返 回 整 型 的 函 数 :

class NoDeclspec

{

public;

NoSpecifiers();

};

当 你 在 成 员 表 中 说 明 一 个 友 元 类 时 , 你 可 以 省 略 成 员 说 明 符 表 。 有 关 友 元 的 更 多信 息 参 见 第 6 章 “ 说 明 ” 中 的 “ 友 元 指 示 符 ” 以 及 第 10 章 “ 成 员 访 问 控 制 ” 中的“ 友 元 ” 一 节 。 甚 至 当 一 个 类 名 称 还 没 有 被 定 义 时 , 它 也 可 以 作 为 友 元 来 说 明 , 正 是 友 元 说 明 引 入 了 该 类 名 称 。

然 而 在 这 种 类 的 成 员 说 明 中 , 必 须 使 用 全 类 型 指 示 符 语 法 。 见 如 下 的 例 子 : class HasFriends

{

public:

friend class NotDeclaredYet;

};

在 上 面 的 例 子 中 , 类 说 明 的 后 面 没 有 成 员 说 明 符 表 。 因 为 对 NotDeclaredYet 的说 明 还 没 有 被 处 理 到 , 故 使 用 了 全 类 型 指 示 符 的 使 用 形 式 :class NotDeclaredYet 。 一 个 已 经 说 明 过 的 类 型 可 以 在 友 元 成 员 说 明 的 说 明 中 使 用 一般 的 说 明 形 式 :

class AlreadyDeclared

{

...

};

class HasFriends

{

public;

friend AlreadyDeclared;

}

纯 指 示 符 ( 见 下 面 的 例 子 ) 表 明 对 此 说 明 的 虚 拟 函 数 不 提 供 其 函 数 实 现 。 因 此 纯指 示 符 仅 仅 只 能 用 在 虚 函 数 之 上 指 示 , 考 察 如 下 的 代 码 :

class StrBase //strings 的 基 类

{

public:

virtual int IsLessThan (StrBase&)=0;

virtual int IsEqualT o (StrBase&)=0;

virtual strBase& Copy O f (StrBase&)=0;

...

};

上 面 的 代 码 中 说 明 了 一 个 抽 象 类 , 也 即 这 个 类 设 计 为 用 作 基 类 以 派 生 出 更 多 的 特有 的 类 。 通 过 用 纯 指 示 符 说 明 一 个 或 多 个 虚 拟 函 数 为 纯 虚 拟 函 数 , 这 种 基 类 能 够强 制 执 行 一 个 特 殊 的 协 议 (protocol) 或 机 制 。

从 StrBase 继 承 的 类 必 须 为 这 些 纯 虚 拟 函 数 提 供 实 现 。 否 则 它 们 也 会 被 认 为 是抽 象 基 类 。

抽 象 基 类 不 能 用 来 说 明 对 象 。 例 如 , 在 一 个 从 StrBase 类 继 承 的 类 的 对 象 被 说 明之 前 , 必 须 提 供 函 数 IsLessThan 、 IsEqualTo 以 及 CopyOf 的 实 现 ( 有 关 抽 象 基 类的 更 多 信 息 参 见 第 9 章 “ 派 生 类 ” 中 的 “ 抽 象 类 ” ) 。

在 成 员 表 说 明 变 长 数 组Microsoft 特 殊 处 →

如 果 一 个 程 序 未 用 ANSI 兼 容 选 项 (/Za) 来 编 译 , 则 变 长 数 组 可 以 在 类 成 员 表 中 说

明 为 最 后 一 个 数 据 成 员 。 因 为 这 是 Microsoft 的 扩 充 , 故 而 用 这 种 方 式 使 用 变 长数 组 会 降 低 你 的 代 码 的 可 移 植 性 。 说 明 一 个 变 长 数 组 , 要 省 略 第 一 个 维 数 , 如 : class Symbol

{

public:

int SymbolTYpe;

char SymbolText[];

};

限 制

如 果 一 个 类 含 有 变 长 数 组 , 它 就 不 能 用 作 其 它 类 的 基 类 , 而 且 一 个 含 有 变 长 数 组的 类 也 不 能 随 意 说 明 为 其 它 类 的 成 员 , 只 能 说 明 为 其 它 类 的 最 后 一 个 成 员 。 一 个含 有 变 长 数 组 的 类 也 不 能 含 有 直 接 或 间 接 虚 拟 基 类 。 当 sizeof 运 算 符 运 用 于 一个 含 有 变 长 数 组 的 类 时 , 将 返 回 除 变 长 数 组 以 外 的 所 有 成 员 的 存 储 总 和 。 对 于 含有 变 长 数 组 的 类 的 实 现 , 应 该 提 供 一 个 替 换 的 办 法 以 获 得 正 确 的 类 的 大 小 。

不 能 够 把 含 有 变 长 数 组 组 成 成 分 的 对 象 说 明 为 某 数 组 的 成 员 , 对 于 指 向 这 类 对 象的 指 针 进 行 数 学 运 算 也 会 产 生 错 误 。

类 成 员 数 据 的 存 储

非 静 态 成 员 数 据 以 如 下 方 式 存 储 : 所 有 在 访 问 说 明 符 之 间 的 项 是 连 续 地 向 存 储 器地 址 增 加 的 方 向 存 放 的 。 越 过 访 问 说 明 符 的 成 员 的 存 放 顺 序 不 作 保 证 。Microsoft 特 殊 处 →

依 赖 于 /Zp 编 译 选 项 或 包 含 pragma 伪 指 令 , 可 以 引 入 干 预 空 间 以 对 成 员 数 据 进行 按 字 或 双 字 边 界 对 齐 。 在 Microsoft C++ 中 , 类 成 员 是 连 续 地 按 地 址 增 加 的 方向 存 储 的 , 尽管 C++ 语 言 并 不 要 求 这 一 点 。 有 关 这 种 顺 序 的 基 本 假 设 都 会 引 起 不可 移 植 的 代 码 。

Microsoft 特 殊 处 结 束

在 类 说 明 中 跟 类 名 称 有 同 样 名 称 的 函 数 是 构 造 函 数 。 当 该 类 的 一 个 对 象 创 建 的时 候 , 隐 含 地 调 用 了 构 造 函 数 ( 有 关 构 造 函 数 的 更 多 信 息 参 见 第 11 章 “ 特 殊 成 员函 数 ” 中 的 “ 构 造 函 数 ” 一 节 。

下 面 各 项 在 其 说 明 的 范 围 中 , 不 能 跟 类 名 称 有 同 样 的 名 称 : 数 据 成 员 ( 静 态 或 非 静态 ) 、 封 闭 的 枚 举 、 无 名 联 合 成 员 及 嵌 套 类 。

成 员 函 数

类 能 够 包 含 数 据 和 函 数 , 这 些 函 数 称 为 成 员 函 数 。 任 何 在 类 说 明 中 说 明 的 一 个 非静 态 函 数 视 为 成 员 函 数 , 并 用 成 员 选 择 符 (. 和 ->) 调 用 。 当 在 其 它 的 成 员 函 数 中调 用 本 类 的 成 员 函 数 时 , 对 象 和 成 员 选 择 符 可 以 省 略 。 例 如 :

class Point

{

public:

short x() { return _x; }

short y() { return _y; }

void Show(){ cout<<x()<<","<<y()<<"\n"; } private:

short _x,_y;

};

void main()

{

Point pt;

pt.Show();

}

注 意 : 在 成 员 函 数 Show 中 调 用 了 其 它 成 员 函 数 x 和 y, 并 未 使 用 成 员 选 择 符 。 这些 调 用 的 隐 含 意 义 是 this->x() 和 this->y() 。 然 而 在 主 函 数 main 中 调 用 成 员函 数 Show 必 须 使 用 对 象 pt 和 成 员 选 择 符 (.) 。

在 类 中 说 明 的 静 态 成 员 函 数 的 调 用 可 以 使 用 成 员 选 择 符 或 使 用 全 限 定 函 数 名 称( 包 括 类 名 称 ) 。

注 意 : 用 friend 关 键 字 说 明 的 函 数 并 不 认 为 是 类 的 成 员 。 在 类 中 此 函 数 只 是 说明 为 友 元 ( 尽 管 此 函 数 可 以 是 别 的 类 的 成 员 ) 。

  • 个 友 元 说 明 控 制 着 非 成 员 函 数 对 类 数 据 的 访 问 。下 面 的 例 子 显 示 了 如 何 说 明 成 员 函 数 :

class Point

{

public:

unsigned GetX();

unsigned GetY();

unsigned SetX(unsigned x);

unsigned SetY(unsigned y); private:

unsigned ptX, ptY;

}

在 上 面 的 类 说 明 中 , 说 明 了 四 个 函 数 :GetX,GetY,SetX 和 SetY 。 下 面 的 例 子 显 示如 何 在 程 序 中 调 用 这 些 函 数 :

void main()

{

// 说 明 一 个 Point 类 型 的 对 象

Point ptOrigin;

// 用 成 员 选 择 符 (.) 调 用 成 员 函 数

ptOrigin.SetX(0);

ptOrigin.SetY(0);

// 说 明 一 个 指 向 Point 类 型 对 象 的 指 针

Point *pptCurrent=new Point;

// 用 成 员 选 择 符 (->) 调 用 成 员 函 数

pptCurrent->SetX(ptOrigin.GetX()+10);

pptCurrent->SetY(ptOrigin.GetY()+10);

}

在 上 面 的 代 码 中 , 对 象 ptOrigin 成 员 函 数 的 调 用 是 用 成 员 选 择 符 (.) 来 调 用 的 。而 由 p p t C u r r e n t 指 向 的 对 象 的 成 员 函 数 的 调 用 使 用 的 是 成 员 选 择 符(->) 。

成 员 函 数 概 述

成 员 函 数 可 以 是 静 态 的 或 非 静 态 的 。 静 态 成 员 函 数 的 行 为 同 其 它 成 员 函 数 的 行为 是 不 同 的 , 因 为 静 态 成 员 函 数 没 有 隐 含 的 this 参 数 。 非 静 态 成 员 函 数 有 一 个this 指 针 。 无 论 静 态 成 员 函 数 还 是 非 静 态 成 员 函 数 成 员 均 可 在 类 说 明 之 内 或 之外 定 义 。

如 果 是 在 类 说 明 之 内 定 义 的 , 则 它 被 视 为 嵌 入 函 数 , 也 没 有 必 要 用 类 名 称 来 限 定函 数 名 称 。 尽 管 定 义 在 类 说 明 之 中 的 函 数 已 经 是 作 为 嵌 入 函 数 对 待 , 你 仍 可 用 关键 字 inline 来 文 档 化 代 码 。

下 面 是 在 一 个 类 说 明 中 定 义 一 个 函 数 的 例 子 : class Account

{

public:

// 在 类 Account 说 明 之 中 说 明 函 数 Deposit

double Deposit(double HowMuch)

{

balance += HowMuch;

return balance;

}

private:

double balance;

} ;

当 在 类 说 明 的 外 面 定 义 成 员 函 数 时 , 只 有 明 确 地 把 它 说 明 为 inline 的 , 此 成 员 函数 才 视 为 嵌 入 型 成 员 函 数 。 而 且 在 定 义 时 函 数 名 必 须 用 类 名 和 范 围 分 辨 符 (::) 加 以 限 定 。

下 面 的 例 子 除 了 在 类 说 明 之 外 定 义 函 数 Deposit 外 , 同 前 面 对 类 Account 说 明 是等 同 的 。

class Account

{

public:

// 说 明 成 员 函 数 Deposit, 但 不 定 义 它 。

double Deposit(double HowMuch); private:

double balance;

};

inline double Accou n t::Deposit (double HowMuch)

{

balance += HowMuch;

return balance;

}

注 意 : 尽 管 成 员 函 数 既 可 以 在 类 说 明 之 中 定 义 也 可 以 在 类 说 明 之 外 单 独 定 义 , 但当 类 定 义 了 以 后 , 就 不 能 再 为 它 增 加 成 员 函 数 了 。

含 有 成 员 函 数 的 类 可 以 有 多 个 说 明 , 但 成 员 函 数 本 身 在 程 序 中 仅 有 一 次 定 义 。 多重 定 义 会 在 链 接 时 引 出 错 误 消 息 。 如 果 一 个 类 含 有 联 编 函 数 的 定 义 , 该 函 数 的 定义 同 样 要 满 足 “ 唯 一 定 义 ” 原 则 。

非 静 态 成 员 函 数

非 静 态 成 员 函 数 有 一 个 隐 含 的 参 数 ,this, 它 是 一 个 指 向 调 用 此 函 数 的 对 象 的 指针 。

this 指 针 的 类 型 是 type * const 。 这 些 函 数 被 认 为 具 有 类 的 范 围 , 可 以 直 接 使用 处 在 同 一 类 范 围 中 的 类 数 据 和 成 员 函 数 。 在 前 面 的 例 子 中 , 表 达 式 balance += HowMuch 把 HowMuch 的 值 加 到 类 成 员 balance 中 。 考 察 下 面 的 语 句 :

Account Checking;

Checking Deposit(57.00);

在 上 面 的 例 子 中 , 说 明 了 一 个 Account 类 型 的 对 象 , 并 调 用 其 成 员 函 数 Deposit 给 它 加 上 $57.00 。 在 函 数 Account::Deposit 中 ,balance 作 为Checking.balance( 该 对 象 的 balance 成员 ) 。

非 静 态 成 员 函 数 趋 向 于 在 它 们 自 己 的 类 类 型 对 象 之 上 操 作 。 通 过 明 确 的 类 型 转换 在 别 的 不 同 类 型 的 对 象 上 , 调 用 此 成 员 函 数 会 引 起 不 确 定 的 行 为 。

this 指 针

所 有 的 非 静 态 成 员 函 数 能 用 this 关 键 字 ,this 是 一 个 常 量 指 针 ( 不 可 修 改 ) 。 它指 向 的 对 象 是 成 员 函 数 的 调 用 者 。 成 员 数 据 可 以 由 表 达 式 this-> 成 员 名 称 的 值来 确 定 ( 尽 管 这 种 技 术 很 少 使 用 ) 。 在 成 员 函 数 中 , 在 表 达 式 中 使 用 成 员 名 称 隐 含着 使 用 方 式 this-> 成 员 名 称 来 选 择 正 确 的 函 数 和 数 据 成 员 。

注 意 : 因 为 this 指 针 是 不 可 修 改 的 , 因 而 不 允 许 对 this 赋 值 。 在 早 期 的 C++ 的实 现 中 允 许 对 this 指 针 赋 值 。

偶 尔 可 以 直 接 使 用 this 指 针 。 例 如 在 操 纵 自 引 用 型 数 据 结 构 时 , 在 这 种 情 况 下要 取 得 当 前 对 象 的 地 址 。

this 指 针 的 类 型

在 函 数 说 明 中 通 过 const 和 volatile 关 键 字 可 以 修 改 this 指 针 的 类 型 。 要 说明 一 个 具 有 这 些 关 键 字 属 性 的 函 数 , 可 使 用 cv- 修 饰 符 表 语 法 。

语 法 :

cv- 修 饰 符 表 :

cv- 修 饰 符 cv- 修 饰 符 表 opt

cv- 修 饰 符 :

const

volatile

考 虑 下 面 的 例 子 : class Point

{

unsig n ed X() const;

};

上 面 的 例 子 中 说 明 了 一 个 成 员 函 数 X, 其中 this 指 针 被 当 作 一 个 指 向 常 量 对 象的 常 量 指 针 。 可 以 混 合 使 用 cv- 修 饰 符 表 选 项 , 但 它 们 通 常 修 饰 的 是 this 指 针所 指 的 对 象 , 而 不 是 this 指 针 本 身 。 因 此 , 下 面 的 说 明 中 说 明 了 函 数 , 它 的 this 指 针 是 一 个 指 向 常 量 对 象 的 常 量 指 针 :

class point

{

unsigned X()__far const;

};

下 面 的 语 法 描 述 了 this 指 针 的 类 型 , 其 中 cv- 修 饰 符 表 可 以 是 const 或 volatile, 类 名 称 是 类 的 名 称 :

cv- 修 饰 符 表 opt 类 类 型 * const this

表 8.2 解 释 了 有 关 这 些 修 饰 符 的 更 多 的 作 用 。

表 8.2

修饰符 意义

const 不能改变成员数据 , 也不能调用非常量型成员函数volatile 成 员 数 据 每 次 访 问 时 是 从 存 储 器 中 调 入 的 ; 并 关 闭 了 优 化

工作

把 一 个 常 量 对 象 传 递 给 一 个 非 常 量 的 成 员 函 数 会 产 生 错 误 。 同 样 地 , 把 一 个volatile 对 象 传 递 给 一 个 非 volatile 型 的 函 数 同 样 会 产 生 错 误 。

成 员 函 数 说 明 为 const 型 , 则 不 能 改 变 成 员 数 据 。 在 这 种 函 数 中 ,this 指 针 是 一

个 指 向 常 量 的 指 针 。

注 意 : 构 造 函 数 和 析 构 函 数 是 不 能 说 明 为 const 或 volatile 的 。 然 而 它 们 可 以被 const 或 volatile 对 象 调 用 。

静 态 成 员 函 数

静 态 成 员 函 数 被 认 为 具 有 类 的 范 围 。 同 非 静 态 成 员 相 比 , 这 些 函 数 没 有 隐 含 的this 参 数 ; 因 而 , 它 们 只 能 直 接 使 用 静 态 的 数 据 成 员 、 枚 举 或 嵌 套 类 型 。 静 态 成员 函 数 的 存 储 可 以 不 必 有 相 应 的 类 类 型 对 象 。 考 虑 下 面 的 代 码 :

class WindowManager

{

public:

static int CountOf(); // 返 回 打 开 窗 口 的 个 数

void Minimize();// 最 小 化 当 前 窗 口

WindowManager SideEffects(); // 边 界 效 果 ( 副 作 用 ) 函 数

... private:

static int wmWindowCount;

};

int WindowManager::wmWindowCount=0;

...

// 最 小 化 ( 显 示 图 标 ) 所 有 窗 口

for(int i=0;i<WindowManager::CountOf();++i)

rgwmWin[i].Minimize();

在 上 面 的 代 码 中 , 类 WindowManager 包 含 有 静 态 成 员 函 数 CountOf 。 该 函 数 返 回打 开 窗 口 的 个 数 , 但 却 不 必 同 某 个 给 定 的 WindowManager 类 对 象 相 联 系 。 这 一 概念 在 循 环 中 得 以 体 现 。 在 循 环 中 CountOf 用 于 控 制 表 达 式 。 因 为 CountOf 是 一个 静 态 成 员 函 数 , 因 此 无 需 引 用 对 象 , 此 函 数 即 可 调 用 。

静 态 成 员 函 数 有 外 部 连 接 。 这 些 函 数 没 有 this 指 针 ( 下 一 章 中 论 述 ) 。 结 果 , 这些 函 数 必 须 遵 循 下 面 的 一 些 约 束 :

  • 不 能 用 成 员 选 择 符 (. 或 ->) 来 访 问 非 静 态 成 员 。

  • 不 能 说 明 为 虚 函 数 。

  • 不 能 同 有 相 同 参 数 类 型 的 非 静 态 成 员 函 数 同 名 称 。

注 意 : 选 择 了 一 个 静 态 成 员 函 数 的 成 员 选 择 符 (. 或 ->), 其 左 边 是 不 可 定 值 的 。 在有 副 作 用 的 函 数 同 此 函 数 一 起 使 用 时 , 这 一 点 可 能 很 重 要 。 例 如 : 表 达 式SideEffects().CountOF() 中 , 并 不 会 调 用 SideEffects 函 数 。

静 态 数 据 成 员

类 可 以 包 含 静 态 的 成 员 数 据 或 成 员 函 数 。 当 一 个 数 据 说 明 为 static, 则 对 于 该类 的 所 有 对 象 仅 维 持 该 成 员 的 一 个 拷 贝 ( 相 关 的 情 况 见 前 一 节 的 “ 静 态 成 员 函数 ” ) 。

静 态 数 据 成 员 并 不 是 给 定 类 类 型 对 象 的 一 部 分 ; 它 们 是 单 独 的 对 象 。 结 果 , 静 态数 据 成 员 的 说 明 不 能 看 作 是 定 义 。 数 据 成 员 的 说 明 是 在 类 范 围 之 中 的 , 但 其 定 义

是 在 文 件 范 围 中 实 现 的 。 这 些 静 态 成 员 具 有 外 部 连 接 。 下 面 的 例 子 显 示 了 这 一点 :

class BufferedOutput

{

public:

// 返 回 该 类 对 象 所 写 的 字 节 大 小

short BytesWritten() { return bytecount;}

// 复 位 计 数 器

static void ResetCount() { bytecount=0; }

// 静 态 成 员 的 说 明

static long bytec o unt;

};

// 在 文 件 范 围 中 定 义 bytecount long BufferedOutput::bytecount;

在 前 面 的 代 码 中 , 成 员 bytecount 是 在 类 BufferOutput 中 说 明 的 , 但 它 必 须 在 类说 明 之 外 定 义 。

不 必 引 用 某 类 类 型 的 对 象 即 可 引 用 静 态 数 据 成 员 。 引 用 BufferedOutput 对 象 所写 的 字 节 大 小 可 以 按 如 下 方 式 得 到 :

long nBytes=BufferedOutput::bytecount;

静 态 成 员 不 会 因 为 该 类 类 型 的 某 个 对 象 的 结 束 而 结 束 。 静 态 成 员 当 然 也 可 以 用成 员 选 择 符 (. 或 ->) 来 访 问 。 例 如 :

BufferedOutput Console;

long nBytes=Console.bytecount;

在 上 面 的 例 子 中 , 对 对 象 console 的 引 用 是 不 可 求 值 的 。 返 回 的 值 是 静 态 对 象bytecount 。

静 态 数 据 成 员 同 样 要 受 制 于 类 成 员 的 访 问 规 则 , 因 此 私 有 存 储 的 静 态 数 据 成 员 只允 许 类 成 员 函 数 和 友 元 访 问 。 这 些 规 则 在 第 10 章 “ 成 员 访 问 控 制 ” 中 描 述 。 例外 的 是 : 静 态 数 据 成 员 必 须 在 文 件 范 围 中 定 义 , 并 忽 视 它 们 的 存 储 约 束 。 如 果 数据 成 员 要 明 确 地 初 始 化 , 则 必 须 同 定 义 一 起 提 供 初 始 化 器 。

静 态 成 员 的 类 型 并 不 被 它 的 类 名 称 所 限 定 。 因 此 :BufferedOutput::bytecount 的 类 型 是 long 型。

联 合

联 合 这 种 类 类 型 在 一 个 时 间 点 只 能 包 含 一 种 数 据 元 素 ( 尽 管 数 据 元 素 可 以 是 数 组或 者 类 类 型 ) 。 联 合 的 成 员 代 表 的 是 联 合 所 能 包 含 的 数 据 种 类 。 一 个 联 合 类 型 的对 象 需 要 足 够 的 空 间 去 容 纳 成 员 表 中 最 大 的 成 员 。 考 虑 下 面 的 例 子 :

#include <stdlib.h> #include <string.h> #include <limits.h>

union NumericT ype // 说 明 一 个 可 容 纳 下 面 数 据 的 联 合

{

int iValue;// 整 型 值

long lValue;// 长 整 型 值

double dValue;// 浮 点 型 值

}

void main (int arg c ,char *argv[])

{

NumericType *Values=new NumericType[argc-1];

for(int i=1;i<argc;++i)

if(strchr(argv[i], ′ . ′ )!=0)

// 浮 点 型 用 dValue 成 员 进 行 赋 值

Values[i].dValue=atof(argv[i]);

else

// 如 果 大 于 最 大 的 整 型 数 , 则 存 到 lValue 成 员 中

if(atol(argv[i])>INT_MAX)

Values[i].lValue=atol(argv[i]);

else

// 否 则 , 存到 iValue 成 员 中

Values[i].iValue=atoi(argv[i]);

}

}

第 7 章 说 明 符 - 图5NumericType 联 合 在 存 储 器 中 的 位 置 示 意 图 如 图 8.1 。

图 8.1 NumericType 联 合 的 数 据 存 储

联 合 中 的 成 员 函 数

正 如 在 成 员 函 数 中 描 述 过 的 , 除 了 有 成 员 数 据 外 , 联 合 也 可 以 有 成 员 函 数 , 尽 管 联合 可 以 有 特 殊 成 员 函 数 如 构 造 函 数 和 析 构 函 数 , 但 联 合 不 能 有 虚 拟 函 数 ( 有 关 的更 多 的 信 息 参 见 第 11 章“ 特 殊 成 员 函 数 ” 中 的“ 构 造 函 数 ” 以 及“ 析 构 函 数 ”) 。

作 为 类 类 型 的 联 合

联 合 不 可 以 有 基 类 ; 因 此 , 它 们 不 能 从 其 它 的 联 合 、 结 构 和 类 中 继 承 属 性 。 联 合也 不 能 作 为 进 一 步 继 承 的 基 类 。

继 承 将 在 第 9 章 “ 派 生 类 ” 中 详 细 讨 论 。

联 合 的 成 员 数 据

除 了 以 下 所 述 之 外 , 联 合 可 以 在 成 员 表 中 包 含 大 多 数 的 数 据 类 型 :

  • 具 有 构 造 函 数 或 析 构 函 数 的 类 类 型

  • 具 有 自 定 义 的 赋 值 操 作 符 的 类 类 型

  • 静 态 数 据 成 员

无 名 联 合

无 名 联 合 是 在 说 明 时 不 带 类 名 或 说 明 符 表 的 联 合 。

语 法

union { 成 员 表 };

这 种 联 合 说 明 所 说 明 的 不 是 类 型 , 而 是 对 象 。 在 无 名 联 合 中 说 明 的 名 称 不 能 跟 在同 一 范 围 中 的 其 它 名 称 冲 突 。 在 无 名 联 合 中 说 明 的 名 称 可 以 直 接 使 用 , 就 像 并 不是 成 员 变 量 一 样 。 下 面 例 子 显 示 了 这 一 点 。

#include <iostream.h>

struct DataForm

{

enum DataType {CharData=1,IntData,StringData};

DataType t ype;

// 说 明 一 个 无 名 联 合

union

{

char chCharMem;

char *szStrmem;

int iIntMem;

};

void print();

};

void DataForm::print()

{

// 基 于 type 的 数 据 类 型 打 印 合 适 的 数 据 类 型

switch(type)

{

case CharData:

cout << chCharMem;

break; case IntData:

cout<<szStrMem;

break;

case StringData:

cout << iIntMem;

}

}

在 函 数 DataForm::print 中 , 三 个 成 员 (chCharMem 、 szStrMem 和 iIntMem) 的 访问 就 像 他 们 是 作 为 成 员 说 明 的 一 样 ( 没 有 联 合 说 明 ) 。 然 而 三 个 联 合 的 成 员 是 共享 同 一 存 储 器 的 。

联 合 的 成 员 数 据 除 了 上 述 所 列 的 一 些 限 制 之 外 , 还 要 受 下 列 约 束 的 限 制 :

  • 如 果 在 文 件 范 围 中 说 明 , 则 必 须 说 明 为 静 态 的 。

  • 它 们 仅 能 含 有 公 有 的 成 员 ; 私 有 的 和 保 护 的 成 员 在 无 名 联 合 中 会 产生 错 误 。

  • 它 们 不 能 有 成 员 函 数 。

注 意 : 仅 简 单 地 省 略 语 法 上 的 类 名 部 分 并 不 会 使 一 个 联 合 成 为 无 名 联 合 , 要 把 一个 联 合 限 制 为 无 名 联 合 , 则 说 明 一 定 不 要 说 明 对 象 。

位 域

类 和 结 构 可 以 拥 有 在 存 储 空 间 上 小 于 一 个 整 型 的 成 员 。 这 些 成 员 被 说 明 为 位 域 。位 域 成 员 说 明 符 的 语 法 规 格 如 下 :

语 法

说 明 符 opt : 常 量 表 达 式

说 明 符 是 程 序 中 用 来 存 储 成 员 的 名 称 。 它 必 须 是 一 个 整 型 ( 包 括 枚 举 型 ) 。 常 量表 达 式 说 明 了 成 员 在 结 构 中 所 占 的 位 数 。 无 名 位 域 也 即 没 有 标 识 符 的 位 域 成 员 , 可 以 用 作 填 充 。

注 意 : 一 个 无 名 的 零 宽 度 位 域 会 强 制 使 下 一 个 位 域 对 齐 到 下 一 个 类 型 边 界 。 这 里类 型 指 的 是 成 员 的 类 型 。

下 面 的 例 子 说 明 了 一 个 含 有 位 域 的 结 构 :

struct Date

{

unsigned nWeekDay :3; //0..7(3 位 )

unsigned nMonthDay :6; //0..31(6 位 )

unsigned nMonth :5; //0..12(5 位 )

unsigned nYear :8; //0..100(8 位 )

};

  • 个 Date 类 型 对 象 的 存 储 器 布 局 如 图 8.2 所 示 。

第 7 章 说 明 符 - 图6

图 8.2 Date 对 象 的 存 储 器 布 局

注 意 : nYear 是 8 位 长 的 而 且 超 出 了 说 明 的 unsigned int 类 型 的 字 边 界 。 因 此nYear 在 一 个 新 的 unsigned int 上 继 续 。 没 有 必 要 把 所 有 的 位 域 都 塞 到 一 个 所基 于 的 类 型 对 象 之 中 。 根 据 说 明 中 所 需 求 的 位 域 的 大 小 , 可 以 分 配 新 的 存 储 单

元 。

Microsoft 特 殊 处 →

作 为 位 域 说 明 的 数 据 的 排 列 顺 序 是 从 低 到 高 。

Microsoft 特 殊 处 结 束

如 果 在 结 构 说 明 中 包 含 一 个 无 名 零 长 度 位 域 , 如 下 例 所 示 : struct Date

{

unsigned nWeekDay :3; //0..7(3 位 )

unsigned nMonthDay :6; //0..31(6 位 )

unsigned :0; // 强 迫 对 齐 到 下 一 个 边 界

unsigned nMonth :5; //0..12(5 位 )

unsigned nYear :8; //0..100(8 位 )

}

其 存 储 器 分 布 如 图 8.3 所 示 。

第 7 章 说 明 符 - 图7

图 8.3 具 有 零 长 度 位 域 的 数 据 对 象 的 分 布

位 域 所 基 于 的 类 型 必 须 是 整 型 。 见 第 2 章 “ 基 本 概 念 ” 中 的 基 本 类 型 。

使 用 位 域 的 限 制

下 面 列 举 了 对 位 域 的 明 细 错 误 操 作 :

  • 取 一 个 位 域 的 地 址

  • 初 始 化

嵌 套 类 说 明

类 可 以 在 其 它 类 的 范 围 之 内 说 明 。 这 种 类 称 为 “ 嵌 套 类 ”。 嵌 套 类 被 视 为 在 外 围封 闭 类 的 范 围 之 中 , 并 只 能 在 此 作 用 之 中 有 效 地 使 用 。 要 引 用 一 个 嵌 套 类 而 从 它的 直 接 外 围 类 的 范 围 之 外 , 则 必 须 使 用 全 限 定 名 。

下 面 的 例 子 显 示 了 如 何 说 明 嵌 套 类 。

class BufferedIO

{

public:

enum IOError { None,Access,General };

// 说 明 嵌 套 类 BufferedInput

class BufferedInput

{

public:

int read();

int good() { return _inputerror == None; }

private:

IOError _inputer ror;

}

// 说 明 嵌 套 类 BufferedOutput

class BuufferedOutput

{

// 成 员 表

};

};

BufferedIO::BufferedIuput 和 BufferedIO::BufferedOutput 是 在 BufferedIO 之 中 说 明 的 。 这 些 类 名 称 在 类 BufferedIO 范 围 之 外 是 不 可 见 的 。 但 是 一 个BufferedIO 型 对 象 不 会 含 有 任 何 BufferedIuput 和 BufferedOutput 型 的 对 象 。嵌 套 类 可 以 直 接 使 用 来 自 于 外 围 类 的 名 称 、 静 态 成 员 名 称 、 类 型 名 称 以 及 枚 举名 称 。

为 了 使 用 其 它 类 成 员 , 必 须 使 用 指 针 、 引 用 或 对 象 名 称 。

在 上 面 的 BufferedIO 的 例 子 中 , 枚 举 IOError 可 以 由 嵌 套 类BufferedIO::BufferedInput 和 BufferedIo::BufferedOutput 的 成 员 函 数 直 接访 问 。 如 函 数 good 中 所 示 的 。

注 意 : 嵌 套 类 仅 仅 只 在 类 范 围 中 说 明 了 类 型 。 它 不 会 引 起 创 建 嵌 套 类 的 对 象 。 上面 的 例 子 只 是 说 明 了 两 个 嵌 套 类 但 并 未 说 明 任 何 这 些 类 的 对 象 。

访 问 权 限 和 嵌 套 类

在 别 的 类 中 的 嵌 套 类 并 没 有 给 嵌 套 类 的 成 员 函 数 以 特 殊 的 权 限 。 同 样 , 类 中 包 括的 成 员 函 数 对 于 嵌 套 类 的 成 员 也 没 有 什 么 特 殊 的 访 问 权 限 。

嵌 套 类 中 的 成 员 函 数

在 嵌 套 类 中 说 明 的 成 员 函 数 可 以 在 文 件 范 围 中 定 义 , 前 面 的 例 子 可 以 写 为 : class BufferedIO

{

public:

enum IOError { None,Access,General};

class BufferedInput

{

public:

int read(); // 说 明 但 不 定 义 成 员 函 数 read 和 good

int good();

private:

IOError _inputerror;

}

class BufferedOutput

{

// 成 员 表

};

}

// 在 文 件 范 围 中 定 义 成 员 函 数 read 和 good int BufferedIO::BufferedInput::read()

{

...

};

int BufferedIO::BufferedInput::good()

{

return _inputerror==None;

}

在 上 面 的 例 子 中 , 运 用 限 定 类 型 名 称 语 法 说 明 成 员 函 数 名 称 , 说 明 为 : BufferedIO::BufferedInput::read()

意 味 着 函 数 read 是 在 类 BufferedIO 的 范 围 中 的 BufferedInput 类 的 成 员 函 数 。因 为 这 一 说 明 使 用 了 限 定 的 类 型 名 语 法 , 因 而 如 下 形 式 的 构 造 是 可 能 的 :

typedef BufferedIO::BufferedInput BIO_INPUT

int BIO_INPUT::read()

上 面 的 说 明 是 同 前 一 个 说 明 等 价 的 , 但 它 用 一 个 typedef 名 称 代 替 了 类 名 称 。

友 元 函 数 和 嵌 套 类

在 嵌 套 类 中 说 明 的 友 元 函 数 被 视 为 在 嵌 套 类 的 范 围 中 , 而 不 在 类 的 范 围 中 。 因 此对 于 包 含 在 类 中 成 员 和 成 员 函 数 , 这 些 友 元 函 数 并 没 有 获 得 多 少 特 别 的 访 问 权限 。 如 果 你 要 在 定 义 于 文 件 范 围 中 的 友 元 函 数 中 使 用 一 个 在 嵌 套 类 中 说 明 的 名称 , 必 须 像 下 面 那 样 使 用 限 定 类 型 名 称 :

extern char *rgszMessage[];

class BufferedIO

{

public:

...

class BufferedInput

{

public:

friend int GetExtendedErrorStatus();

...

static char *message;

int iMsgNo;

};

};

char *BufferedIO::BufferedInput::message;

int GetExtendedErrorStaus()

{

...

strcpy(BufferedIO::BufferedInput::message,

rgszMessage[iMsgNo]);

return iMsgNo;

}

在 上 面 的 代 码 中 函 数 GetExtendedErrorStatus 是 作 为 友 元 函 数 说 明 的 , 在 这 个函 数 中 ( 它 定 义 于 文 件 范 围 ), 一 条 消 息 从 静 态 数 组 中 拷 贝 到 类 成 员 中 。 注 意GetExtendedErrorStatus 的 一 个 较 好 的 实 现 是 说 明 :

int GetExtendedErrorS tatus (char * message)

用 前 面 的 接 口 , 几 个 不 同 的 类 可 以 使 用 此 函 数 的 功 能 , 只 要 把 要 拷 贝 的 错 误 消 息的 地 址 传 给 函 数 即 可 。

类 范 围 中 的 类 型 名 称

在 类 范 围 中 定 义 的 类 型 名 称 应 视 为 局 限 于 它 们 的 类 。 它 们 不 能 在 类 的 范 围 之 外使 用 。

下 面 的 例 子 显 示 了 这 一 概 念 : class Tree

{

public:

typedef Tree *PTREE;

PTREE Left;

PTREE Right;

void *vData;

};

PTREE pTree; // 错 误 : 不 在 类 的 范 围 中