第 7 章 说 明 符
一 个 “ 说 明 符 ” 是 一 个 说 明 的 一 部 分 , 用 以 命 名 一 个 对 象 、 类 型 或 函 数 。 说 明 符是 出 现 在 说 明 中 的 以 一 个 或 多 个 被 逗 号 分 开 的 名 称 , 每 个 名 称 可 有 一 个 相 关 的 初始 化 器 。
语 法
说 明 符 表 :
初 始 说 明 符
说 明 符 表 , 初 始 说 明 符初 始 说 明 符 :
说 明 符 初 始 化 器 opt
本 章 包 括 以 下 主 题 :
-
说 明 符 概 述
-
类 型 名 称
-
抽 象 说 明 符
-
函 数 定 义
-
初 始 化 器
说 明 符 概 述
说 明 符 是 指 定 名 称 的 一 个 说 明 的 组 成 成 分 。 说 明 符 还 可 修 饰 基 本 的 类 型信 息 , 使 得 名 称 成 为 函 数 或 者 对 象 或 函 数 的 指 针 ( 在 第 6 章 “ 说 明 ” 中 讨论 的 指 示 符 传 送 诸 如 类 型 和 存 储 类 属 性 。 在 本 章 以 及 附 录 B “ M icrosoft 特 殊 修 饰 符 ” 中 讨 论 的 修 饰 符 用 以 修 饰 说 明 符 )。
图 7.1 给 出 了 两 个 名 称 szBuf 和 strcpy 的 一 个 完 整 说 明 , 并 提 出 了 说 明 的各 组 成 部 件 。
图 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 多 维 数 组 的 概 念 性 布 局
在 带 有 初 始 化 器 表 ( 如 初 始 化 器 中 所 描 述 的 ) 的 多 维 数 组 说 明 中 , 第 一 维 指 定 边 界的 常 量 表 达 式 可 以 省 略 。 例 如 :
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 函 数 定 义 的 各 个 部 分
说 明 符 语 法 的 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 引 用 类 型 初 始 化 的 决 策 图
第 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]);
}
}
NumericType 联 合 在 存 储 器 中 的 位 置 示 意 图 如 图 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 所 示 。
图 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 所 示 。
图 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; // 错 误 : 不 在 类 的 范 围 中