11 章 特 殊 成 员 函 数

C++ 定 义 了 几 种 只 能 作 为 类 成 员 函 数 说 明 的 函 数 , 它 们 称 为 “ 特 殊 成 员 ” 函 数 。这 些 函 数 影 响 着 给 定 类 对 象 创 建 、 删 除 、 拷 贝 以 及 转 换 成 其 它 类 型 对 象 的 方 法 。这 些 函 数 的 另 一 个 重 要 的 特 性 是 它 们 可 以 由 编 译 器 隐 含 调 用 。

这 些 特 殊 的 函 数 在 下 表 中 简 要 描 述 :

  • 构 造 函 数

  • 析 构 函 数

  • 临 时 对 象

  • 转 换

  • new 和 delete 运 算 符

  • 用 特 殊 成 员 函 数 进 行 初 始 化

  • 拷 贝 类 对 象

所 有 上 面 列 出 的 项 目 在 每 个 类 中 可 以 由 用 户 自 定 义 。

特 殊 成 员 函 数 同 其 它 成 员 函 数 一 样 遵 循 相 同 的 访 问 规 则 。 这 些 访 问 规 则 在 第 10 章 “ 成 员 访 问 控 制 ” 中 描 述 。 表 11.1 总 结 了 成 员 函 数 和 友 元 函 数 的 行 为 。

表 11.1 函 数 行 为 总 结

函数类型

否类

从中

基继

能 否 为

拟 函 数

能 否 有

返回值

成 员 函 数 还

是友元函数

提数

果 用 户

供 此

, 编 译

函器

能否生成

构造函数

成员函数

拷 贝 构 造函数

析构函数

成员函数

成员函数

转换

成员函数

赋值 (=)

成员函数

new delete

其 它 成 员

是是

否否

void* void

静 态 成 员数

静 态 成 员数

成员函数

函函

否否

函数

友元函数

友元函数

构 造 函 数

与 类 名 称 具 有 一 样 名 称 的 成 员 函 数 是 构 造 函 数 。 构 造 函 数 不 能 有 返 回 值 , 甚 至 不能 有 return 语 句 。 说 明 一 个 有 返 回 值 的 构 造 函 数 是 错 误 的 , 取 构 造 函 数 的 地 址也 是 错 误 的 。

如 果 一 个 类 有 构 造 函 数 , 在 程 序 中 每 个 该 类 类 型 的 对 象 在 使 用 之 前 由 此 构 造 函 数进 行 初 始 化 ( 有 关 初 始 化 的 更 多 信 息 参 见 本 章 后 面 的 “ 用 特 殊 成 员 函 数 进 行 初 始化 ” ) 。

构 造 函 数 是 在 对 象 的 创 建 点 上 被 调 用 的 。 创 建 对 象 可 以 是 :

  • 全 局 对 象 ( 文 件 范 围 或 外 部 链 接 的 ) 。

  • 在 一 个 函 数 或 者 小 的 封 闭 块 中 的 局 部 变 量 。

  • 用 new 运 算 符 创 建 的 动 态 对 象 。 new 操 作 在 程 序 的 堆 或 自 由 存 储 区中 分 配 一 个 对 象 。

  • 因 显 式 调 用 构 造 函 数 而 创 建 的 临 时 对 象 ( 详 见 本 章 后 面 的 “ 临 时 对象 ” ) 。

  • 因 编 译 器 隐 含 调 用 构 造 函 数 而 创 建 的 临 时 对 象 ( 详 见 本 章 后 面 的 “ 临时 对 象 ” ) 。

  • 其 它 类 的 数 据 成 员 。 在 创 建 类 类 型 的 对 象 时 , 若 此 类 类 型 由 其 它 类类 型 变 量 组 成 , 将 会 引 起 该 类 中 每 个 对 象 的 创 建 。

  • 一 个 类 的 基 类 子 对 象 。 创 建 派 生 类 类 型 的 对 象 时 会 引 起 基 类 构 件 的创 建 。

构 造 函 数 的 作 用

  • 个 构 造 函 数 执 行 各 种 任 务 , 但 对 于 程 序 员 来 说 , 这 些 任 务 是 不 可 见 的 , 你 甚 至可 以 不 必 为 构 造 函 数 写 任 何 代 码 。 这 些 任 务 都 同 建 立 一 个 完 全 的 、 正 确 的 类 类型 对 象 实 例 有 关 。

在 MS C++ 中 ( 同 样 也 在 很 多 其 它 C++ 中 ) 一 个 构 造 函 数 :

  • 初 始 化 对 象 的 虚 拟 基 指 针 (vbptr) 。 如 果 该 类 是 由 虚 拟 基 类 派 生 出的 , 则 这 一 步 要 执 行 。

  • 按 说 明 的 顺 序 调 用 基 类 和 成 员 的 构 造 函 数 。

  • 初 始 化 对 象 的 虚 拟 函 数 指 针 (vfptr) 。 如 果 该 类 有 或 者 继 承 了 虚 拟函 数 , 则 这 一 步 要 执 行 , 虚 拟 函 数 指 针 指 向 类 的 虚 拟 函 数 表 (v- table), 并 且 使 虚 拟 函 数 的 调 用 同 代 码 正 确 绑 定 ( binding )。

  • 在 构 造 函 数 体 中 执 行 可 选 的 代 码 。

当 构 造 函 数 结 束 以 后 , 所 分 配 的 存 储 器 就 是 一 个 给 定 类 类 型 的 对 象 。 因 为 构 造 函数 执 行 这 些 步 骤 , 故 虚 拟 函 数 的 “ 迟 后 绑 定 ” 形 态 可 以 在 虚 拟 函 数 的 调 用 点 得 以解 决 , 构 造 函 数 也 要 构 造 基 类 以 及 构 造 组 合 对 象 ( 作 为 数 据 成 员 的 对 象 ), 迟 后 绑定 是 C++ 实 现 对 象 的 多 态 行 为 的 机 制 。

说 明 构 造 函 数 的 规 则

构 造 函 数 具 有 同 类 名 相 同 的 名 称 。 只 要 遵 守 重 载 函 数 的 规 则 ( 有 关 详 情 参 见 第 12 章 “ 重 载 ” ), 可 以 说 明 多 个 构 造 函 数 。

语 法

类 名 称 ( 参 量 说 明 表 op t) cv- 修 饰 符 表 opt

C++ 定 义 了 两 种 类 型 的 构 造 函 数 , 缺 省 的 和 拷 贝 的 构 造 函 数 。 如 表 11.1 所 述 。表 11.1 缺 省 的 和 拷 贝 构 造 函 数

构造函数的种类 参量 目 的

缺省构造函数 可以无参量调用 构 造 一 个 类 类 型 的 缺 省 对

拷贝构造函数 可 以 接 受 对 同 种 类 类 型 的

引用作为唯一参量

拷贝类类型的对象

缺 省 构 造 函 数 不 要 参 量 即 可 调 用 , 但 你 可 以 说 明 一 个 带 有 参 量 表 的 缺 省 构 造 函 数只 要 让 所 有 的 参 量 有 缺 省 值 即 可 。 同 样 , 拷 贝 构 造 函 数 必 须 接 受 同 一 类 类 型 的 引用 作 为 唯 一 参 量 。 但 可 以 提 供 更 多 的 参 量 , 只 要 后 续 的 参 量 具 有 缺 省 值 即 可 。

如 果 你 不 提 供 任 何 构 造 函 数 , 编 译 器 会 试 图 生 成 一 个 缺 省 的 构 造 函 数 。 同 样 , 如果 你 没 有 提 供 拷 贝 构 造 函 数 , 编 译 器 也 会 试 图 产 生 一 个 。 编 译 器 产 生 的 构 造 函 数视 为 公 有 的 成 员 函 数 。 如 果 你 说 明 一 个 拷 贝 构 造 函 数 , 而 其 第 一 个 参 量 是 一 个对 象 而 不 是 一 个 引 用 , 则 会 产 生 错 误 。

编 译 器 生 成 的 缺 省 构 造 函 数 建 立 对 象 ( 初 始 化 vftables 和 vbtables, 如 前 面 所述 ), 并 调 用 基 类 及 成 员 的 缺 省 构 造 函 数 , 但 不 会 采 取 其 它 的 行 动 。 基 类 和 成 员 的构 造 函 数 只 要 存 在 , 是 可 访 问 的 , 并 且 是 无 二 义 性 的 就 会 被 调 用 。

编 译 器 生 成 的 拷 贝 构 造 函 数 建 立 一 个 对 象 , 并 且 采 用 一 个 成 员 方 式 的 拷 贝 来 复制 要 被 拷 贝 的 对 象 的 内 容 。 如 果 基 类 或 成 员 的 构 造 函 数 存 在 , 则 它 们 将 被 调 用 , 否 则 , 就 采 取 位 方 式 的 拷 贝 。

如 果 所 有 的 基 类 和 该 类 类 型 的 成 员 类 具 有 接 受 一 个 常 量 参 量 的 构 造 函 数 , 则 编 译器 生 成 的 拷 贝 构 造 函 数 接 受 一 个 唯 一 的 参 量 的 类 型 是 const type& ; 否 则 , 编 译器 生 成 的 拷 贝 构 造 函 数 接 受 的 唯 一 参 量 的 类 型 是 type & 。

你 可 以 用 一 个 构 造 函 数 去 初 始 化 一 个 const 和 volatile 对 象 , 但 构 造 函 数 本 身不 能 说 明 为 const 和 volatile 的 。 对 于 构 造 函 数 唯 一 合 法 的 存 储 类 型 inline, 对 于 构 造 函 数 使 用 任 何 其 它 的 存 储 类 修 饰 符 , 包 括 __ declspec 关 键 字 都 会 引 起 错误 。 构 造 函 数 和 析 构 函 数 除 了 __ stdcall 外 不 能 说 明 为 其 它 的 调 用 约 定 。

派 生 类 是 不 能 继 承 基 类 中 的 构 造 函 数 的 。 当 一 个 派 生 类 的 对 象 在 创 建 的 时 候 , 它是 从 基 类 构 件 开 始 构 造 的 , 然 而 才 进 入 派 生 类 构 件 。 编 译 器 使 用 每 个 基 类 的 构 造函 数 作 为 完 整 对 象 初 始 化 的 一 部 分 ( 除 了 虚 拟 派 生 有 所 不 同 , 参 见 本 章 后 面 的“ 初始 化 基 类 ” ) 。

显 式 地 调 用 构 造 函 数

在 程 序 中 , 为 创 建 给 定 类 型 的 对 象 , 可 以 显 式 地 调 用 构 造 函 数 。 例 如 : 创 建 两 个Point 型 对 象 的 描 述 一 条 线 段 的 端 点 , 可 以 写 如 下 代 码 : DrawLine(Point(13,22),Point(87,91);

创 建 了 两 个 Point 对 象 , 传 递 给 DrawLine 函 数 , 并 在 该 表 达 式 ( 函 数 调 用 ) 结 束 后拆 除 。

另 外 一 个 显 式 地 调 用 构 造 函 数 的 情 况 是 在 一 个 初 始 化 中 : Point pt=Point(7,11);

创 建 了 一 个 Point 类 型 的 对 象 , 并 用 接 受 两 个 整 形 参 量 的 构 造 函 数 进 行 初 始 化 。如 前 面 的 两 个 例 子 中 , 通 过 显 式 地 调 用 构 造 函 数 创 建 的 对 象 是 无 名 的 对 象 , 并 在

它 们 所 创 建 的 表 达 式 中 是 有 生 存 期 的 。 在 本 章 后 面 的 “ 临 时 对 象 ” 中 将 对 这 一点 进 行 深 入 的 讨 论 。

在 构 造 函 数 内 调 用 成 员 函 数 和 虚 拟 函 数

在 构 造 函 数 里 面 调 用 任 何 成 员 函 数 通 常 是 很 安 全 的 , 因 为 在 执 行 第 一 行 用 户 代 码之 前 , 对 象 已 经 完 全 建 立 起 来 了 ( 已 经 初 始 化 了 虚 表 等 等 ) 。 但 是 当 成 员 函 数 调 用了 其 抽 象 基 类 的 虚 拟 成 员 函 数 时 , 在 构 造 函 数 和 析 构 函 数 调 用 此 成 员 函 数 存 在 着潜 在 的 不 安 全 性 。

构 造 函 数 可 以 调 用 虚 拟 函 数 。 在 调 用 虚 拟 函 数 时 , 被 调 用 的 是 在 构 造 函 数 自 身 的类 中 定 义 的 函 数 ( 或 者 从 其 基 类 中 继 承 的 函 数 ) 。 下 面 的 例 子 显 示 了 一 个 虚 拟 函数 在 一 个 构 造 函 数 中 被 调 用 时 发 生 的 情 况 。

#include <iostream.h> class Base

{

public:

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

virtual void f(); // 虚 拟 成 员 函 数

};

Base::Base()

{

cout<<"Constructing Base sub-object\n ";

void Base::f()

{

cout<<"called Base::f()\n";

}

class Derived:public Base

{

public:

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

void f(); // 该 类 虚 拟 函 数 的 实 现

};

Derived::Derived()

{

cout<<"constructing Derived object\n";

}

void Derived::f()

{

void main()

{

Derived d;

}

在 上 面 的 程 序 运 行 的 时 候 ,Derived d 的 说 明 会 引 发 下 列 一 系 列 事 件 : 1. 类 Derived 的 构 造 函 数 (Derived::Derived) 被 调 用 。

  1. 在 进 入 到 Derived 类 的 构 造 体 之 前 , 基 类 Base 的 构 造 函 数 被 调 用 。

  2. Base::Base 调 用 函 数 f, 它 是 一 个 虚 拟 函 数 。 通 常 被 调 用

    的 函 数 会 是Derived::f, 因 为 对 象 d 是 Derived 类 型 的 对 象 。 但 因 为 Base::Base 是 一 个 构造 函 数 , 此 时 的 对 象 是 一 个 Derived 类 型 的 对 象 , 故 Base::f 将 会 被 调 用 。

构 造 函 数 与 数 组

数 组 的 构 造 只 能 使 用 缺 省 的 构 造 函 数 。 缺 省 构 造 函 数 要 么 不 接 受 任 何 参 量 , 要 么对 于 它 的 所 有 参 量 都 有 缺 省 的 值 。 数 组 通 常 是 按 升 序 来 构 造 的 , 该 数 组 的 每 一 个成 员 的 初 始 化 都 是 使 用 同 一 构 造 函 数 。

构 造 的 次 序

对 于 派 生 类 或 其 成 员 数 据 是 类 类 型 的 类 , 构 造 发 生 的 顺 序 有 助 于 你 理 解 , 在 任 一

构 造 与 继 承

  • 个 派 生 类 的 对 象 是 从 基 类 到 派 生 类 通 过 按 次 序 为 每 个 类 调 用 构 造 函 数 来 构 造的 。

每 个 类 的 构 造 函 数 能 仅 依 赖 于 被 完 全 构 造 好 的 它 的 基 类 。

有 关 初 始 化 的 完 整 描 述 , 包 括 初 始 化 的 顺 序 , 见 本 章 后 面 的 “ 初 始 化 基 类 及 成员 ” 。

构 造 与 组 合 类 型

含 有 类 类 型 数 据 成 员 的 类 称 为 组 合 类 。 当 创 建 一 个 组 合 类 类 型 的 对 象 时 , 含 有 类的 构 造 函 数 在 该 类 的 构 造 函 数 之 前 调 用 。

有 关 这 种 情 况 的 初 始 化 , 见 本 章 后 面 的 “ 初 始 化 基 类 及 成 员 ”。

析 构 函 数

析 构 函 数 是 “ 反 向 ” 的 构 造 函 数 。 它 们 在 对 象 被 销 毁 ( 回 收 ) 时 调 用 。 设 计 一 个函 数 为 类 的 析 构 函 数 只 要 在 类 名 之 前 加 上 (~) 号 。 例 如 , 类 string 的 析 构 函 数 是

~string() 。

析 构 函 数 通 常 是 在 一 个 对 象 不 再 需 要 时 , 完成 “ 清 除 ”。 考 虑 一 下 下 面 类 string 的 说 明 :

#include <string.h>

class String

{

public:

String(char *ch); // 说 明 构 造 函 数

~String(); // 以 及 析 构 函 数private:

char *_text;

};

// 定 义 构 造 函 数String::String (char *ch)

{

// 动 态 分 配 正 确 数 量 的 存 储 器

_text=new char[strlen(ch)+1];

// 如 果 分 配 成 功 , 则 拷 贝 初 始 化 字 符 串

if(_text)

strcpy(_text,ch);

}

// 定 义 析 构 函 数String::~String()

{

// 回 收 先 前 为 此 字 符 串 保 留 的 存 储 器

delete[] _text;

}

在 前 面 的 代 码 上 。 析 构 函 数 String::~String 使 用 delete 运 算 符 回 收

( deallocate ) 动 态 分 配 给 text 的 存 储 空 间 。

说 明 析 构 函 数

析 构 函 数 与 类 名 称 有 相 同 的 名 称 , 并 且 前 缀 有 ~ 号 。

语 法

~ 类 名 称 () 或

类 名 称 :: ~ 类 名 称 ()

语 法 的 第 一 种 形 式 用 于 析 构 函 数 说 明 或 定 义 于 类 说 明 之 中 ; 第 二 种 形 式 用 于 析 构函 数 定 义 于 类 说 明 之 外 。

有 几 条 规 则 约 束 着 析 构 函 数 的 说 明 。 析 构 函 数 :

  • 不 能 接 受 参 量

  • 不 能 说 明 有 任 何 返 回 类 型 ( 包 括 void)

  • 不 能 用 return 语 句 返 回 值

  • 不 能 说 明 为 const,volatile 或 static, 但 析 构 函 数 可 以 因 说 明 为const,volatile 或 static 的 对 象 的 析 构 而 被 调 用 。

  • 可 以 说 明 为 虚 拟 的 。 使 用 虚 析 构 函 数 , 你 可 以 折 除 对 象 而 不 必 知 道该 对 象 的 类 型 。 由 于 使 用 虚 拟 函 数 机 制 , 将 调 用 该 对 象 的 正 确 的 析构 函 数 。 注 意 , 在 一 个 抽 象 类 中 , 析 构 函 数 可 以 说 明 为 纯 虚 拟 函 数 。

使 用 析 构 函 数

当 下 列 事 件 发 生 时 将 调 用 析 构 函 数 :

  • 一 个 用 new 运 算 符 分 配 的 对 象 显 式 地 使 用 delete 运 算 符 回 收 。 在用 delete 运 算 符 对 一 个 对 象 进 行 回 收 时 , 释 放 的 存 储 器 是 “ 最 外 派生 对 象 ” 的 (most derived object), 或 者 是 一 个 完 整 对 象 的 而 不 是代 表 基 类 的 子 对 象 。 对 于 “ 最 外 派 生 对 象 ” 的 回 收 只 是 在 同 虚 拟 析构 函 数 一 起 工 作 时 才 有 保 证 的 。 回 收 可 能 在 多 重 继 承 的 情 形 下 失 败 , 因 为 在 此 情 形 下 , 类 类 型 信 息 并 不 同 实 际 对 象 所 信 赖 的 类 型 相 一致 。

  • 一 个 在 块 范 围 中 的 局 部 ( 自 动 ) 对 象 超 出 了 其 范 围 。

  • 临 时 对 象 生 存 期 的 结 束 。

  • 全 局 或 静 态 成 员 还 存 在 , 但 程 序 已 经 结 束 。

  • 用 析 构 函 数 的 全 限 定 名 来 显 式 地 调 用 析 构 函 数 ( 详 情 见 本 章 后 面 的“ 显 式 的 析 构 函 数 的 调 用 ” ) 。

前 面 列 表 中 所 描 述 的 情 况 保 证 了 所 有 的 对 象 可 以 用 用 户 自 定 义 的 方 法 折 除 。

如 果 一 个 基 类 或 者 数 据 成 员 有 一 个 可 访 问 的 析 构 函 数 , 同 时 如 果 一 个 派 生 类 没 有说 明 析 构 函 数 , 编 译 器 会 生 成 一 个 。 编 译 器 产 生 的 析 构 函 数 调 用 基 类 的 析 构 函 数和 派 生 类 成 员 的 析 构 函 数 。 缺 省 的 析 构 函 数 是 公 有 的 ( 有 关 访 问 属 性 的 详 情 见 第10 章 “ 成 员 访 问 控 制 ” 中 的 “ 基 类 访 问 说 明 ” ) 。

析 构 函 数 可 以 自 由 地 调 用 类 成 员 函 数 以 及 访 问 类 成 员 数 据 。 当 在 析 构 函 数 中 调用 虚 函 数 时 , 该 函 数 是 在 当 前 正 被 折 除 的 类 的 函 数 ( 有 关 详 情 见 下 一 节 , “ 析 构 的

次 序 ” ) 。

使 用 析 构 函 数 有 两 条 原 则 , 第 一 是 不 能 取 析 构 函 数 的 地 址 。 第 二 是 派 生 类 不 能 继承 它 的 基 类 的 析 构 函 数 。 相 反 , 如 前 面 所 解 释 的 , 它 们 总 是 覆 盖 了 基 类 的 析 构 函数 。

析 构 的 次 序

当 某 个 对 象 超 出 了 其 范 围 或 被 删 除 时 , 在 其 完 整 的 析 构 中 会 发 生 如 下 一 些 系 列 的事 件 :

  1. 调 用 该 类 的 析 构 函 数 , 执 行 析 构 函 数 体 的 代 码 。

  2. 非 静 态 成 员 按 它 出 现 在 类 说 明 中 的 反 序 调 用 其 析 构 函

    数 。 在 构 造 函 数 中 使 用的 可 选 成 员 初 始 化 表 的 运 用 不 会 影 响 构 造 或 析 构 的 次 序 ( 有 关 初 始 化 成 员 的 详 情见 本 章 后 面 的 “ 初 始 化 基 类 和 成 员 ” ) 。

  3. 非 虚 拟 基 类 的 析 构 函 数 按 其 说 明 的 反 序 调 用 。

  4. 虚 拟 基 类 的 析 构 函 数 按 其 说 明 的 反 序 调 用 。

非 虚 拟 基 类 的 析 构 函 数

非 虚 拟 基 类 的 析 构 函 数 是 按 基 类 名 说 明 的 反 序 调 用 的 。 考 虑 下 面 的 类 说 明 : class MultInherit: public Base1,public Base2

...

在 上 面 的 例 子 中 ,Base2 的 析 构 函 数 在 Base1 的 析 构 函 数 之 前 调 用 。

虚 拟 基 类 的 析 构 函 数

虚 拟 基 类 的 析 构 函 数 是 按 它 们 出 现 在 有 向 无 环 图 中 的 反 序 被 调 用 的 ( 按 深 度 优 先从 左 到 右 , 后 序 遍 历 ) 。 如 图 11.1 所 示 描 绘 了 一 幅 继 承 图 。

第 11 章 特 殊 成 员 函 数 - 图1

图 11.1 显 示 虚 拟 基 类 的 继 承 图下 面 列 出 了 图 11.1 中 各 类 的 类 头 :

class A class B

class C:virtual public A,virtual public B class D:virtual public A,virtual public B class E:public C,public D,virtual public B

对 于 类 型 E 的 对 象 要 确 定 虚 拟 基 类 析 构 函 数 的 顺 序 。 编 译 器 用 如 下 算 法 编 一 个列 表 :

  1. 从 图 中 的 最 深 点 开 始 ( 在 此 例 中 是 E 点 ) 遍 历 左 子 图 。

  2. 而 左 一 直 遍 历 到 所 有 的 结 点 都 被 访 问 , 记 下 当 前 结 点 的 名 称 。

  3. 再 次 访 问 前 一 个 结 点 ( 向 下 并 向 右 ) 去 证 实 正 在 标 记

    的 结 点 是 否 是 一 个 虚 拟 基类 。

  4. 如 果 被 标 记 的 结 点 是 一 个 虚 拟 基 类 , 则 搜 索 该 列 表 看

    是 否 该 结 点 已 经 在 列 表之 中 了 。 如 果 被 标 记 的 结 点 不 是 一 个 虚 拟 基 类 , 则 忽 略 它 。

  5. 如 果 该 标 记 的 结 点 还 不 在 列 表 中 , 则 把 它 加 入 到 列 表 的 尾 部 。

  6. 返 回 到 图 的 上 一 层 , 并 沿 着 右 边 的 下 一 条 路 径 遍 历 图 。

  7. 转 向 2 继 续 。

  8. 当 本 结 点 所 有 可 能 遍 历 的 路 径 用 完 后 , 记 下 该 结 点 。

  9. 转 向 3 继 续 。

  10. 继 续 该 过 程 直 到 底 部 的 结 点 再 次 成 为 当 前 结 点 。因 此

    对 于 类 E, 析 构 的 顺 序 是 :

  1. 非 虚 拟 基 类 E 。

  2. 非 虚 拟 基 类 D 。

  3. 非 虚 拟 基 类 C 。

  4. 虚 拟 基 类 B 。

  5. 虚 拟 基 类 A 。

这 一 过 程 产 生 一 个 单 一 条 目 的 有 序 列 表 , 类 名 不 会 出 现 两 次 。 一 旦 该 列 表 建 成 以后 , 按 反 序 遍 历 该 列 表 , 从 最 后 到 最 前 将 调 用 每 个 类 的 析 构 函 数 。

当 一 个 类 的 构 造 函 数 和 析 构 函 数 依 赖 于 其 它 正 在 被 创 建 的 构 件 或 要 长 期 被 保 留的 构 件 时 , 例 如 ,( 在 图 11.1 中 ) 当 A 的 析 构 函 数 ( 若 其 代 码 的 执 行 ) 依 赖 于 B 的

仍 然 存 在 时 , 或 反 过 来 构 造 和 析 构 的 次 序 是 非 常 重 要 的 。

在 继 承 图 中 , 类 之 间 的 这 种 交 互 依 赖 具 有 固 有 的 危 险 性 , 因 为 其 后 派 生 的 类 会 改变 最 后 的 路 径 , 与 此 相 连 也 改 变 了 构 造 和 析 构 的 次 序 。

显 式 的 析 构 函 数 的 调 用

显 式 地 调 用 析 构 函 数 通 常 是 不 必 要 的 , 但 它 们 在 执 行 放 在 绝 对 地 址 中 的 对 象 是 很有 用 的 。 这 些 对 象 通 常 是 用 带 有 一 个 位 置 参 量 的 用 户 自 定 义 的 new 运 算 符 分 配的 。 delete 操 作 不 能 回 收 这 一 存 储 器 , 因 为 它 不 是 从 自 由 存 储 区 中 分 配 的 ( 有 关详 情 见 本 章 后 面 的 “ new 和 delete 运 算 符 ” ) 。 然 而 一 个 对 析 构 函 数 的 调 用 能够 完 成 合 适 的 清 除 工 作 。 要 显 式 调 用 类 String 的 对 象 s 析 构 函 数 , 可 采 用 下 列语 句 之 一 :

s.S tring::~String();// 非 虚 拟 的 调 用

ps->String::~String(); // 非 虚 拟 的 调 用s.~String();// 虚 拟 调 用

ps->~string(); // 虚 拟 调 用

在 上 面 的 代 码 中 显 示 的 显 式 调 用 析 构 函 数 的 符 号 可 以 直 接 使 用 , 而 无 视 是 否 该类 型 定 义 了 一 个 析 构 函 数 。 这 使 你 能 用 这 种 显 式 的 调 用 , 而 又 不 必 了 解 该 类 型是 否 定 义 了 一 个 析 构 函 数 。 如 果 一 个 析 构 函 数 未 定 义 , 则 对 该 析 构 函 数 的 显 式 调用 不 会 产 生 效 果 。

临 时 对 象

在 某 些 情 况 下 , 对 于 编 译 器 来 说 必 须 创 建 临 时 对 象 。 因 以 下 原 因 要 创 建 这 些 临 时变 量 :

  • 要 用 一 个 不 同 于 正 被 初 始 化 的 引 用 类 型 的 另 一 类 型 去 初 始 化 一 个 常量 引 用 。

  • 要 保 存 一 个 函 数 返 回 的 用 户 自 定 义 类 型 的 返 回 值 。 如 果 用 户 程 序 没有 把 返 回 值 拷 贝 到 另 一 对 象 中 时 , 这 一 临 时 对 象 才 会 被 创 建 :

UDT Func1(); // 说 明 一 个 返 回 用 户 自 定 义 类 型 的 函 数

...

Func1(); // 调 用 Func1 函 数 , 但 忽 略 其 返 回 值

// 创 建 一 个 临 时 对 象 以 保 存 返 回 值

因 为 返 回 值 并 未 拷 贝 到 其 它 对 象 , 故 创 建 一 个 临 时 对 象 。 一 个 要 创 建 临 时 对 象 的更 普 遍 的 情 形 是 要 调 用 重 载 运 算 符 函 数 的 表 达 式 求 值 中 。 这 些 重 载 的 运 算 符 返回 用 户 自 定 义 的 类 型 的 值 ( 通 常 不 拷 贝 到 其 它 对 象 之 中 )。 考 虑 表 达 式 :

ComplexResult=Complex1+Complex2+Complex3

求 出 表 达 式 Complex1+Complex2 的 值 , 结 果 存 放 在 临 时 对 象 中 。 接 着 求 表 达 式temporary+complex3 的 值 , 并 把 结 果 存 放 到 complexresult 中 ( 假 定 赋 值 运 算 符没 有 被 重 载 ) 。

  • 存 一 个 造 型 转 换 为 用 户 自 定 义 类 型 的 转 换 结 果 。 当 一 个 给 定 类 型 的对 象 显 式 地 转 换 为 用 户 自 定 义 类 型 时 , 新 的 对 象 是 作 为 临 时 对 象 来构 造 的 。

临 时 对 象 有 一 个 生 存 期 , 在 其 创 建 点 和 拆 除 点 之 间 有 定 义 。 任 何 一 个 创 建 了 多 个临 时 对 象 的 表 达 式 最 后 是 按 创 建 它 们 的 相 反 的 顺 序 拆 除 的 。 析 构 的 发 生 点 如 表

  1. 所 示 。

表 11.2 临 时 对 象 的 析 构 点

创建临时对象的原因 析构点

表达式求值的结果 所 有 因 为 表 达 式 求 值 的 结 果 而 创 建 的 临 时 对

象 在 表 达 式 求 值 结 束 时 ( 也 即 分 号 处 ) 或 者 在控制表达式 (for 、 if 、 while 、 do 和 switch 语句 ) 结束时被拆除。

内 置 的 ( 非 重 载 的 ) 符 (|| 和

&&)

在 右 操 作 数 结 束 之 后 。 在 这 一 析 构 点 , 所有

因 右 逻 辑 运 算 操 作 数 求 值 而 创 建 的 临 时 对 象被拆除。

初始化常量引用 如果一个初始化符并不是同要被初始化的引用有相同的 l 值类型 , 则创建基于此对象类型的临时对象并用此初始化表达式初始化。这一临时对象在同它绑定在一起的引用对象被拆除以后马上拆除。

转 换

给 定 类 类 型 的 对 象 可 以 转 换 为 其 它 类 型 的 对 象 。 这 可 以 按 以 下 来 完 成 : 从 源 类 类

型 构 造 一 个 目 标 类 类 型 的 对 象 , 并 把 结 果 拷 贝 到 目 标 对 象 。 这 一 过 程 称 为 构 造函 数 式 转 换 。 对 象 也 可 以 由 用 户 提 供 的 转 换 函 数 转 换 。

当 标 准 转 换 ( 在 第 3 章 “ 标 准 转 换 ” 中 描 述 ) 不 能 完 成 从 一 个 给 定 类 型 转 换 到 一个 类 类 型 时 , 编 译 器 会 选 择 用 户 自 定 义 的 转 换 以 帮 助 完 成 这 一 工 作 。 除 了 显 式 的类 型 转 换 , 转 换 还 发 生 在 :

  • 一 个 初 始 化 表 达 式 同 被 初 始 化 的 对 象 有 不 同 的 类 型 。

  • 在 函 数 调 用 中 使 用 的 参 量 类 型 同 函 数 说 明 中 所 说 明 的 参 量 不 匹配 。

  • 从 函 数 中 返 回 的 对 象 的 类 型 同 函 数 说 明 中 说 明 的 返 回 类 型 不 匹 配 。

  • 两 个 表 达 式 操 作 数 必 须 有 同 样 的 类 型 。

  • 一 个 控 制 复 述 或 选 择 语 句 的 表 达 式 需 要 从 提 供 的 表 达 式 中 得 到 不 同的 类 型 。

用 户 自 定 义 的 转 换 仅 用 于 它 没 有 二 义 性 时 , 否 则 会 产 生 一 个 错 误 消 息 。 在 使 用 点上 将 检 查 二 义 性 , 因 而 , 如 果 可 以 引 起 二 义 性 的 特 征 未 被 使 用 , 只 能 标 明 一 个 类有 潜 在 的 二 义 性 , 并 不 会 产 生 任 何 错 误 。 尽 管 有 很 多 情 形 会 引 起 二 义 性 , 下 面 两条 是 最 会 引 起 二 义 性 的 原 因 。

  • 一 个 使 用 多 重 继 承 派 生 的 类 , 没 有 说 清 楚 从 哪 一 个 基 类 选 择 此 转 换( 见 第 9 章 “ 派 生 类 ” 中 的 “ 二 义 性 ” ) 。

  • 一 个 显 式 的 类 型 转 换 运 算 符 和 为 此 同 一 目 的 构 造 函 数 同 时 存 在 ( 见本 章 后 面 “ 转 换 函 数 ” ) 。

  • 构 造 函 数 方 式 的 转 换 和 转 换 函 数 方 式 的 转 换 两 者 都 要 遵 循 第 10 章“ 成 员 访 问 控 制 ” 中 所 描 述 的 访 问 控 制 规 则 。 访 问 控 制 仅 在 转 换 发

现 无 二 义 性 以 后 才 进 行 检 测 。

转 换 构 造 函 数

可 以 用 单 一 参 量 调 用 的 构 造 函 数 是 用 于 把 参 量 类 型 转 变 成 类 类 型 的 转 换 。 这 种构 造 函 数 称 为 转 换 构 造 函 数 。 考 虑 下 面 的 例 子 :

class Point

{

public;

Point();

Point(int);

...

};

有 时 需 要 一 种 转 换 , 但 在 类 中 又 不 存 在 转 换 构 造 函 数 。 这 种 转 换 不 能 由 构 造 函 数完 成 , 编 译 器 不 会 寻 找 可 以 完 成 该 转 换 的 中 间 类 型 。 比 如 : 假 设 存 在 着 一 个 从Point 类 型 到 Rect 类 型 的 转 换 以 及 一 个 从 int 到 Point 类 型 的 转 换 , 但 编 译 器不 会 通 过 构 造 一 个 中 间 的 Point 类 型 对 象 来 提 供 一 个 从 int 到 Rect 类 型 的 转换 。

转 换 和 常 量

尽 管 内 置 类 型 的 常 量 如 (int,long 和 double) 可 以 出 现 在 表 达 式 中 , 但 类 类 型 的常 量 是 不 允 许 的 ( 部 分 是 因 为 , 类 通 常 用 来 表 示 复 杂 的 对 象 , 用 符 号 不 便 于 表示 ) 。 但 是 如 果 提 供 了 对 内 置 类 型 进 行 转 换 的 转 换 构 造 函 数 , 这 些 内 置 类 型 的 常

量 可 以 用 于 表 达 式 , 并 且 转 换 会 引 出 正 确 的 行 为 。 如 下 例 子 , 一 个 Money 类 具 有从 long 和 double 类 型 的 转 换 :

class Money

{

public;

Money(long)

Money(double)

...

Money operator+(const Money&); // 重 载 加 法 运 算 符

};

因 此 , 像 下 面 的 表 达 式 可 以 说 明 常 量 值 :

Money AccountBalance=37.89;

Money NewBalance=AccountBalance+14L;

第 二 个 例 子 引 起 了 重 载 加 法 运 算 符 的 调 用 ( 在 下 一 章 中 讨 论 ) 。 两 个 例 子 都 会 令编 译 器 在 表 达 式 中 使 用 常 量 以 前 把 它 们 转 换 成 Money 类 型 。

转 换 构 造 函 数 的 缺 点

因 为 编 译 器 会 隐 含 地 选 择 一 个 转 换 构 造 函 数 , 故 你 将 放 弃 对 具 体 调 用 函 数 的 控制 。 如 果 保 留 全 面 的 控 制 是 很 重 要 的 , 就 不 要 说 明 任 何 接 受 单 一 参 量 的 构 造 函数 。 相 反 , 定 义 一 个 “ 帮 助 ” 函 数 以 完 成 这 些 转 换 , 看 如 下 代 码 :

#include <stdio.h> #include <stdlib.h>

// 说 明 Money 类class Money

{

public;

Money();

// 定 义 只 能 显 式 调 用 的 转 换 函 数

static Money Convert(char * ch) {return Money(ch);};

static Money Convert(double d) {return Money(d);};

void Print (){printf("\n%f",_amount);} private:

Money(char * ch) {_amount=atof(ch);}

Money(double d) {_amount=d;}

double _amount;

};

void main()

{

// 完 成 一 个 从 char* 到 Money 的 转 换

Money Acct=Money::Convert("57.29");

Acct.Print();

// 完 成 一 个 从 double 到 Money 的 转 换

Acct=Money::Convert(33.29);

Acct.Print();

}

在 上 面 的 代 码 中 , 转 换 构 造 函 数 是 私 有 的 , 并 且 不 能 在 类 型 转 换 中 使 用 。 但 它 们可 以 显 式 地 通 过 调 用 Convert 函 数 来 激 活 。 因 为 Convert 函 数 是 静 态 的 , 它 们 的访 问 不 需 要 特 别 的 对 象 。

转 换 函 数

前 一 节 中 介 绍 , 用 构 造 函 数 进 行 转 换 中 一 种 类 型 的 对 象 可 以 隐 含 地 转 换 成 特 殊的 类 类 型 。 这 一 节 介 绍 一 种 方 法 , 通 过 它 可 以 提 供 一 个 显 式 的 转 换 方 法 把 给 定 的类 类 型 转 换 成 其 它 类 型 。 从 某 种 类 类 型 的 转 换 经 常 是 使 用 转 换 函 数 完 成 的 。 转换 函 数 使 用 下 面 的 语 法 :

语 法

转 换 函 数 名 称 :

operator 转 换 类 型 名 称 () 转 换 类 型 名 称 :

类 型 指 示 符 表 指 针 运 算 符 opt

下 面 的 例 子 说 明 了 一 个 转 换 函 数 把 Money 类 型 转 换 成 double 类 型 : class Money

{

public:

Money();

operator double() { return _amount;}

private:

double _amount;

};

  • 旦 给 出 了 前 面 的 类 说 明 , 则 可 以 编 写 下 面 的 代 码 :

Money Account;

...

double CashOnHand=Account;

把 CashOnHand 用 Account 进 行 初 始 化 会 引 起 从 类 型 Account 到 double 的 转 换 。转 换 函 数 通 常 被 称 为 “ 造 型 运 算 符 ”。 因 为 它 们 ( 如 同 构 造 函 数 的 叫 法 ) 是 在 造型 类 型 转 换 时 调 用 的 函 数 。 下 面 的 例 子 使 用 了 造 型 或 显 式 的 类 型 转 换 打 印 一 个Money 型 对 象 的 当 前 值 :

cout <<(double)Account<<endl;

转 换 函 数 可 以 在 派 生 类 中 被 继 承 。 转 换 运 算 符 仅 仅 隐 藏 基 类 中 转 换 完 全 相 同 类型 的 转 换 运 算 符 。 因 此 一 个 用 户 自 定 的 operator int 函 数 不 会 隐 藏 一 定 义 于 基类 中 的 operator short 函 数 。

在 进 行 隐 含 转 换 时 仅 有 一 个 用 户 自 定 义 的 类 型 转 换 函 数 适 用 。 如 果 没 有 显 式 的定 义 转 换 函 数 , 编 译 器 不 会 寻 找 一 个 中 间 类 型 以 通 过 它 进 行 对 象 的 转 换 。

如 果 需 要 的 转 换 引 起 了 二 义 性 , 则 会 产 生 一 个 错 误 。 在 多 个 用 户 自 定 义 的 转 换 可以 适 用 , 或 在 用 户 自 定 义 的 转 换 和 内 置 的 转 换 同 时 存 在 时 产 生 二 义 性 。

下 面 的 例 子 示 例 了 一 个 存 在 潜 在 二 义 性 的 类 说 明 : #include <string.h>

class String

{

// 定 义 从 char * 类 型 转 换 的 构 造 函 数

String(char *) {strcpy(_text,s);}

// 定 义 char * 的 转 换

operator char* ( ) {return_text,}

int operator==(const String& s)

{return !strcmp(_text,s._text);} private:

char _text[80];

};

int main()

{

String s("abcd");

char *ch="efg";

// 使 编 译 器 选 择 一 个 转 换

return s==ch;

}

在 表 达 式 s==ch 上 , 编 译 器 有 两 种 选 择 , 并 且 没 有 办 法 决 定 哪 一 种 更 好 。 它 可 以使 用 构 造 函 数 把 ch 转 换 为 一 个 String 类 型 的 对 象 , 然 后 采 用 用 户 自 定 义 的operator== 进 行 比 较 。

然 而 它 也 可 以 把 s 转 换 成 一 个 char* 型 的 指 针 ( 使 用 转 换 函 数 ) , 然 后 采 用 指 针之 间 的 比 较 。

因 为 两 种 方 法 之 中 没 有 哪 一 个 更 好 一 些 , 编 译 器 也 不 能 决 定 比 较 表 达 式 的 意 义 ,

故 而 产 生 一 个 错 误 。

说 明 转 换 函 数 的 规 则

下 面 四 条 规 则 适 用 于 说 明 转 换 构 造 函 数 ( 参 见 转 换 函 数 语 法 ):

  • 类 , 枚 举 和 typedef 名 不 能 在 类 型 指 示 符 表 中 说 明 , 因 而 下 面 的 代 码会 产 生 错 误 :

operator struct String {char string_storage;}(); 相 反 , 结 构 String 的 说 明 应 先 于 转 换 函 数 的 说 明 。

  • 转 换 函 数 不 能 带 参 量 , 说 明 参 量 会 引 起 错 误 。

  • 转 换 函 数 已 说 明 了 转 换 类 型 名 称 的 返 回 类 型 ; 故 为 转 换 函 数 说 明 任何 返 回 类 会 产 生 错 误 。

  • 转 换 函 数 可 以 说 明 为 虚 拟 的 。

new 和 delete 运 算 符

C++ 支 持 使 用 new 和 delete 运 算 符 动 态 分 配 和 回 收 对 象 , 这 些 操 作 从 一 个 称 为“ 自由 区 ” 的 池 中 为 对 象 分 配 存 储 器 。 new 运 算 符 调 用 operator new 函 数 ,delete 运 算 符 调 用 operator delete 函 数 。

operator new 函 数

当 编 译 器 在 程 序 中 碰 到 如 下 的 一 个 语 句 时 , 它 就 翻 译 为 一 个 对 operator new 的

调 用 。

char *pch=new char[BUFFER_SIZE]

对 于 分 配 0 字 节 存 储 区 的 要 求 ,operator new 返 回 一 个 不 同 对 象 的 指 针 ( 也 就 是说 , 反 复 调 用 operator new 会 返 回 不 同 的 指 针 ) 。 如 果 没 有 足 够 的 存 储 器 满 足 分配 的 要 求 , 缺 省 时 , 通 常 operator new 返 回 NULL 。 当 然 你 可 以 通 过 写 一 个 定 制的 例 外 处 理 例 程 来 改 变 这 种 缺 省 的 行 为 。 然 后 调 用 _set_new_handler 运 行 时 间库 函 数 并 把 你 的 例 外 处 理 例 程 函 数 的 名 称 作 为 其 参 量 。 有 关 恢 复 机 制 的 细 节 参见 下 一 节 “ 处 理 无 足 够 存 储 器 的 条 件 ”。

operator new 的 两 种 范 围 在 表 11.3 中 描 述 。表 11.3 operator new 函 数 的 范 围运算符 范围

::operator new 全局的

class_name ::operator new 类 的

operator new 函 数 的 第 一 个 参 量 必 须 是 类 型 size_t( 在 STDDEF.H 中 定 义 ) , 其返 回 值 总 是 void* 。

在 用 new 运 算 符 分 配 内 部 数 据 类 型 对 象 时 , 或 者 分 配 不 包 含 用 户 自 定 义 型operator new 函 数 的 类 类 型 对 象 时 , 以 及 分 配 任 意 类 型 的 数 组 时 , 调 用 的 是 全 局operator new 函 数 。 当 operator new 用 在 定 义 operator new 函 数 的 类 类 型 对象 对 , 调 用 的 是 类 的 operator new 函 数 。

为 类 定 义 的 operator new 函 数 是 一 个 静 态 成 员 函 数 ( 因 此 它 不 可 能 是 虚 拟 的 ) 。它 对 于 该 类 的 对 象 来 说 隐 藏 了 全 局 的 operator new 函 数 。 研 究 下 面 的 情 况 , 其

中 new 用 于 分 配 存 储 器 , 并 把 存 储 器 设 为 给 定 的 值 :

#include <malloc.h> #include <memory.h> class Blanks

{

public:

Blanks() { }

void* operator new(size_t stAllocateBlock,char chInit);

};

void * Blanks::operator new(size_t stAllocateBlock,char chInit);

{

void * pvTemp=malloc(stAllocateBlock);

if (pvTemp!=0)

memset (pvTemp,chInit,stAllocateBlock),

return pvTemp;

};

对 于 不 同 的 Blanks 类 型 的 对 象 来 说 , 全局 operator new 函 数 被 隐 藏 了 。 因 此 下面 的 代 码 分 配 一 个 Blanks 型 的 对 象 并 初 始 化 为 0xa5:

int main()

{

Blanks *a5=new (0Xa5) Blanks;

return a5!=0;

};

在 括 号 中 提 供 给 new 的 参 量 是 传 递 给 Blank::operator new 作 为 chInit 参 量 的 。然 而 全 局 operator new 函 数 是 隐 藏 了 的 , 故 按 如 下 调 用 全 局 operator new 的 代码 会 引 起 错 误 。

Blanks *SomeBlanks=new Blanks:

在 以 前 的 编 译 器 版 本 中 , 非 类 类 型 和 所 有 的 数 组 ( 无 论 它 们 是 否 是 类 类 型 数 组 ) 使用 new 运 算 符 分 配 存 储 器 总 是 使 用 全 局 operator new 函 数 。

从 Visual C++5.0 开始 , 编 译 器 开 始 在 类 说 明 中 支 持 成 员 数 组 new 和 delete 运算 符 。

例 如 :

class X { public:

void * operator new[](size_t);

void Operator delete[](void*),

};

void f(){

X *pX=new X[5],

delete []pX;

}

处 理 无 足 够 存 储 器 条 件

对 于 失 败 的 存 储 器 分 配 可 以 采 用 如 下 的 代 码 :

int *pi=new int[BIG_NUMBER];

if (pi==0)

{

cerr<<"Insufficient memory"<<endl;

return -1;

}

当 然 有 其 它 的 办 法 处 理 失 败 的 存 储 器 分 配 请 求 , 即 写 一 个 定 制 的 例 程 去 处 理 这 种失 败 。 然 后 通 过 调 用 _set_new_handler 运 行 函 数 注 册 你 的 处 理 例 程 。 这 种 方 法在 下 一 节 介 绍 。

使 用 _set_new_handler

在 某 些 情 况 下 , 可 以 在 分 配 存 储 器 中 采 取 一 些 矫 正 的 办 法 使 分 配 要 求 可 得 以 满足 。 为 在 全 局 operator new 函 数 失 败 时 获 得 控 制 , 使 用 _set_new_handler 函 数( 在 NEW.H 中 定 义 ) 如 下 :

#include <stdio.h> #include <new.h>

// 定 义 一 个 在 new 分 配 存 储 器 失 败 时 调 用 的 函 数int MyNewHandler(size_t size)

{

clog<<"Allocation failed.Coalescing heap."<<endl;

// 调 用 一 个 工 具 函 数 以 恢 复 一 些 堆 的 空 间

return CoalesceHeap();

}

void main()

{

// 把 new 失 败 处 理 函 数 设 为 MyNewHandler

_set_new_handler(MyNewHandler);

int *pi=new int[BIG_NUMBER];

}

在 上 面 的 例 子 中 ,main 函 数 的 第 一 个 语 句 是 把 new 的 处 理 函 数 设 为MyNewHandler 。 第 二 条 语 句 是 使 用 new 运 算 符 分 配 一 大 块 存 储 器 。 当 分 配 失 败的 时 候 , 控 制 转 向 MyNewHandler 。

传 递 给 MyNewHandler 的 参 量 是 要 求 分 配 的 存 储 器 的 字 节 大 小 。 从 MyNewHandler 返 回 的 是 一 个 标 识 以 表 明 是 否 要 再 进 行 一 次 分 配 操 作 。 非 0 值 表 示 应 再 进 行 一次 , 而 0 值 则 表 示 分 配 失 败 了 。

MyNewHandler 打 印 一 条 警 号 消 息 并 采 取 矫 正 的 步 骤 。 如 果 MyNewHandler 返 回一 个 非 0 值 ,new 运 算 符 再 试 图 分 配 一 次 ; 当 MyNewHandler 返回 0 值 时 ,new 操 作分 配 企 图 , 把 一 个 0 值 返 回 给 程 序 。

_set_new_handler 返 回 的 是 一 个 老 的 new 处 理 函 数 的 地 址 。 因 此 如 果 一 个 新 的new 处 理 函 数 只 在 短 时 间 内 使 用 , 则 老 的 处 理 函 数 可 以 按 如 下 代 码 还 原 : #include <new.h>

...

_PNH old_handler=_set_new_handler(MyNewHandler);

// 要 求 使 用 MyNewHandler 的 代 码

...

// 重 新 安 装 以 前 的 处 理 函 数

_set_new_handler(old_handler);

用 0 参 量 调 用 _set_new_handler 会 引 起 移 去 new 处 理 函 数 , 也 就 是 没 有 缺 省 的处 理 函 数 了 。

你 可 以 用 任 何 名 称 说 明 new 处 理 函 数 , 但 它 必 须 是 一 个 返 回 int 型 的 函 数 ( 非 0 表 示 new 处 理 函 数 继 续 ,0 表 示 失 败 ) 。

如 果 提 供 了 一 个 用 户 自 定 义 的 operator new 函 数 , 该 new 处 理 函 数 不 会 在 失 败时 自 动 调 用 。

_set_new_handler 的 函 数 原 型 以 及 _PNH 在 NEW.H 中 定 义 :

_PNH_set_new_handler(_PNH);

类 型 _PNH 是 一 个 指 向 函 数 的 指 针 , 该 函 数 的 类 型 size_t 作 为 唯 一 的 参 量 并 返 回int 型 。

operator delete 函 数

用 new 操 作 动 态 分 配 的 存 储 器 可 以 用 delete 操 作 释 放 。 delete 运 算 符 调 用operator delete 函 数 把 存 储 器 释 放 回 可 用 的 存 储 区 池 中 。 使 用 delete 操 作 也会 引 起 类 的 析 构 函 数 的 调 用 ( 如 果 有 的 话 ) 。

operator delete 函 数 也 有 全 局 和 类 范 围 的 两 种 。 对 于 给 定 的 类 只 能 为 其 定 义

  • 个 operator delete 函 数 ; 一 旦 定 义 以 后 , 它 会 隐 藏 全 局 operator delete 函数 。 全 局 的 operator delete 函 数 总 是 可 以 为 任 意 类 型 的 数 组 所 调 用 。

全 局 operator delete 函 数 在 说 明 中 带 一 个 void 类 型 的 单 一 参 量 , 它 含 有 要 释放 对 象 的 指 针 。 它 的 返 回 类 型 是 void(operator delete 函 数 不 能 有 返 回 值 ) 。类 成 员 operator delete 函 数 有 两 种 形 式 :

void operator delete(void *);

void operator delete(void *,size_t);

对 于 给 定 的 类 只 能 提 供 上 述 两 种 不 同 形 式 中 的 一 种 , 第 一 种 形 式 就 像 全 局operator delete 一 样 工 作 ; 第 二 种 形 式 有 两 个 参 量 。 第 一 个 参 量 是 要 释 放 存 储器 块 的 指 针 , 第 二 个 参 量 是 要 释 放 的 存 储 器 字 节 大 小 。 当 一 个 基 类 中 的 operator delete 函 数 用 于 释 放 派 生 类 对 象 时 , 第 二 种 形 式 特 别 有 用 。

operator delete 函 数 是 静 态 的 , 因 而 它 不 能 是 虚 拟 的 ,operator delete 函 数 必须 遵 循 第 10 章 “ 成 员 访 问 控 制 ” 中 描 述 的 访 问 控 制 。

下 面 的 例 子 显 示 了 用 户 设 计 的 operator new 和 operator delete 函 数 用 于 记 录分 配 和 释 放 存 储 器 :

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

int fLogMemory=0;// 是 否 记 录 (0= 否 ; 非 0= 是 ) int cBlocksAllocated=0; // 分 配 的 块 数

// 用 户 自 定 义 的 new 操作

void *operator new(size_t stAllocateBLock)

{

static fInOpNew=0,// 保 护 标 志

if(fLogMemory && !fInOpNew)

{

fInOpNew=1;

clog<<"Memory Block"<<++cBlocksAllocated

<<"allocated for"<<stAllocateBlcok

<<"bytes\n";

fInOpNew=0;

}

return malloc(stAllocateBlock);

}

// 用 户 定 义 的 operator_delete

void operator delete(void *pvMem)

{

static fInOpDelete=0; // 保 护 标 志

if (fLogMemory &&!fInOpDelete)

{

fInOpDelete=1;

clog<<"Memory block"<<--cBlocksAllocated

<<"deallocated\n";

fInOpDelete=0;

}

free (pvMem);

}

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

{

fLogMemory=1; // 打开 log 标识

if (argc>1)

for (int i=0; i<atoi(argv[1]);++i )

{

char *pMem=new char[10];

delete [] pMem;

};

return cBlocksAllocated;

}

上 面 的 代 码 可 以 用 来 检 查 “ 存 储 器 漏 损 ” , 即 从 自 动 堆 中 分 配 以 后 就 没 有 释 放 的存 储 器 。 为 完 成 这 一 检 查 , 全局 new 和 delete 操 作 被 重 新 定 义 以 统 计 分 配 和 释放 的 存 储 器 。

从 Visual C++5.0 开始 , 编 译 器 在 类 说 明 中 支 持 成 员 数 组 new 和 delete 操 作 , 例如 :

class X

{

public:

void * operator new[] (size_t);

void operatot delete[](void*);

};

void f() {

X* pX=new X[5];

delete[] pX;

}

用 特 殊 成 员 函 数 初 始 化

这 一 节 描 述 使 用 特 殊 成 员 函 数 的 初 始 化 , 它 是 下 面 一 些 初 始 化 论 题 的 扩 展 。

  • 第 7 章 “ 说 明 ” 中 的 “ 初 始 化 集 合 ” , 描 述 了 如 何 初 始 化 非 类 类 型数 组 以 及 简 单 类 类 型 对 象 。 这 些 简 单 的 类 类 型 不 可 有 私 有 的 及 保 护的 成 员 , 它 们 也 不 能 有 基 类 。

  • 构 造 函 数 。 它 解 释 了 如 何 使 用 特 殊 的 构 造 函 数 初 始 化

    类 类 型 对 象 。缺 省 的 初 始 化 方 法 是 执 行 一 个 位 方 式 的 拷 贝 , 把 初 始 化 器 拷 贝 给 要 被 初 始 化 的 对象 。 这 种 技 术 仅 适 用 于 :

  • 内 部 类 型 的 对 象 。 例 如 : int i=100;

  • 指 针 。 例 如 :

int i

int *pi=&i;

  • 引 用 。 例 如 :

String sFileName("FILE.DAT"); String &rs=sFileName;

  • 类 型 对 象 , 该 类 不 可 有 保 护 的 或 私 有 的 成 员 , 不 能 有 虚 拟 函 数 也 不 能有 基 类 , 例 如 :

struct Point

{

int x,y;

}

Point pt={10,20}; //static storage calss only

通 过 说 明 构 造 函 数 ( 有 关 说 明 这 种 函 数 的 详 情 见 本 章 开 始 的 “ 构 造 函 数 ” ), 类 可以 说 明 更 多 精 确 的 初 始 化 。 如 果 一 个 对 象 的 类 型 是 有 构 造 函 数 的 类 类 型 , 该 对 象一 定 会 被 初 始 化 , 否 则 一 定 存 在 着 缺 省 的 构 造 函 数 。 没 有 特 别 初 始 化 的 对 象 会 激活 类 的 缺 省 构 造 函 数 。

显 式 的 初 始 化

C++ 支 持 两 种 形 式 的 显 式 初 始 化 。

  • 支 持 在 括 号 中 提 供 初 始 表 : String sFileName("FILE.DAT);

在 括 号 中 的 初 始 化 器 表 的 项 是 作 为 类 构 造 函 数 的 参 量 。 这 种 形 式 的 初 始 化 使 得对 一 个 对 象 使 用 多 个 值 进 行 初 始 化 成 为 可 能 , 并 且 也 可 以 同 new 运 算 符 联 合 使用 。 如 :

Rect *pRect=new Rect(10,15,24,97);

  • 支 持 使 用 等 号 初 始 化 语 法 提 供 单 一 初 始 化 器 , 例 如 :

    String sFileName="FILE.DAT";

尽 管 前 面 的 例 子 同 第 一 种 形 式 中 的 String 例 子 以 同 样 的 方 式 工 作 , 但 该 语 法 并不 适 用 于 同 自 由 堆 中 分 配 的 对 象 一 起 使 用 。

在 等 号 右 边 的 单 一 表 达 式 是 作 为 类 的 拷 贝 构 造 函 数 的 参 量 , 因 此 它 必 须 是 能 够转 换 成 类 类 型 的 某 种 类 型 。

注 意 因 为 在 初 始 化 的 上 下 文 中 等 号 (=) 同 赋 值 号 是 不 同 的 , 故 重 载 operator= 对于 初 始 化 没 有 影 响 。

等 号 初 始 化 语 法 同 函 数 式 语 法 是 不 同 的 , 尽 管 在 大 多 数 情 况 下 产 生 的 代 码 是 相 同的 。 它 们 之 间 的 不 同 在 于 : 当 使 用 等 号 语 法 , 编 译 器 的 行 为 就 像 发 生 了 如 下 的 顺序 事 件 :

  • 创 建 一 个 同 要 被 初 始 化 的 对 象 同 一 类 型 的 临 时 对 象 。

  • 把 临 时 对 象 拷 贝 到 要 被 初 始 化 的 对 象 之 中 。

在 编 译 器 执 行 这 些 步 骤 之 前 , 构 造 函 数 必 须 是 可 访 问 的 。 尽 管 编 译 器 在 大 多 数 情况 下 可 以 忽 略 临 时 对 象 的 创 建 和 拷 贝 步 骤 , 然 而 一 个 不 可 访 问 的 拷 贝 构 造 函 数会 引 起 等 号 初 始 化 的 失 败 。 考 虑 下 面 的 例 子 :

class anInt

{

anInt (const anInt&); // 私 有 拷 贝 构 造 函 数public:

anInt(int); // 公 有 的 int 构 造 函 数

};

...

anInt myInt=7; // 破 坏 了 访 问 控 制 , 试 图 引 用 私 有 拷 贝 构 造 函 数anInt myInt(7); // 正 确 ; 设 有 调 用 拷 贝 构 造 函 数

在 函 数 调 用 中 , 传 值 形 式 的 类 型 参 量 以 及 传 值 形 式 的 返 回 对 象 用 下 面 的 代 码 概 念上 地 进 行 初 始 化 。

type-name name=value 例 如 :

String s="C++";

因 此 , 参 量 类 型 必 须 是 可 以 转 换 为 作 为 参 量 进 行 传 递 的 类 类 型 。 该 类 的 拷 贝 构 造函 数 , 以 及 自 定 义 转 换 运 算 符 或 接 受 实 际 参 量 的 构 造 函 数 必 须 是 公 有 的 。

在 使 用 new 运 算 符 的 表 达 式 中 , 从 自 由 堆 中 分 配 的 对 象 概 念 性 地 用 以 下 形 式 进 行初 始 化 :

type-name name(initializer 1 ,initializer 2 ,...iniatializer n ) 例 如 :

String *ps=new String("C++");

对 于 基 类 构 件 和 类 成 员 对 象 的 初 始 化 也 是 概 念 性 地 用 这 种 方 法 初 始 化 的 ( 详 见 本章 后 面 的 “ 初 始 化 基 类 及 成 员 ” ) 。

初 始 化 数 组

如 果 一 个 类 有 一 个 构 造 函 数 , 此 类 的 数 组 由 一 构 造 函 数 进 行 初 始 化 。 若 初 始 化 器表 中 的 项 数 小 于 数 组 元 素 的 个 数 , 则 缺 省 构 造 函 数 将 用 于 剩 下 的 数 组 元 素 。 若 此

类 并 未 定 义 缺 省 构 造 函 数 , 则 初 始 化 器 表 必 须 是 完 全 的 , 也 就 是 数 组 中 的 每 一 个元 素 必 须 有 一 个 初 始 化 器 。

考 虑 定 义 了 两 个 构 造 函 数 的 Point 类 :

class Point

{

public:

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

Point(int,int); // 从 两 个 int 型 的 构 造 函 数

...

}

一 个 Point 对 象 数 组 可 以 按 如 下 说 明 :

Point aPoint[3]={

Point(3,3) // 使 用 int,int 构 造 函 数

}

aPoint 数 组 的 第 一 个 元 素 使 用 构 造 函 数 Point(int,int) 来 构 造 , 剩 余 的 两 个 元素 使 用 缺 省 构 造 函 数 来 构 造 。

静 态 成 员 数 组 ( 无 论 是 否 为 const) 都 可 以 在 它 们 的 定 义 中 ( 在 类 说 明 之 外 ) 进 行初 始 化 。 例 如 :

class WindowColors

{

public:

static const char *rgszWindowPartList[7];

...

};

const char * WindowColors::rgszWindowPartList[7]={

"Active Title Bar","Inactive Title Bar", "Title Bar Text", "Menu Bar",

"Menu Bar Text", "Window Background", "Frame" };

初 始 化 静 态 对 象

全 局 静 态 对 象 是 按 它 们 出 现 在 源 代 码 中 的 次 序 进 行 初 始 化 , 它 们 按 此 相 反 的 次 序拆 除 。 然 而 , 交 叉 转 换 单 元 中 的 初 始 化 的 次 序 依 赖 于 连 接 器 如 何 组 织 目 标 文 件 , 析 构 的 次 序 仍 然 按 对 象 创 建 次 序 的 相 反 次 序 发 生 。

局 部 静 态 对 象 的 初 始 化 发 生 在 当 它 们 第 一 次 出 现 在 程 序 流 中 时 , 而 且 它 们 在 程 序结 束 时 也 是 按 此 相 反 的 次 序 被 拆 除 的 。 局 部 静 态 对 象 的 析 构 仅 发 生 在 程 序 流 中发 现 此 对 象 , 且 此 对 象 被 初 始 化 时 。

初 始 化 基 类 及 成 员

  • 个 派 生 类 的 对 象 是 由 代 表 每 个 基 类 的 构 件 以 及 对 于 此 特 殊 类 唯 一 的 一 个 构 件组 成 。 含 有 成 员 对 象 的 类 对 象 也 可 包 含 其 它 类 的 实 例 。 这 一 节 描 述 当 这 种 类 型的 类 对 象 在 创 建 时 , 这 些 构 件 对 象 是 如 何 初 始 化 的 。

为 了 完 成 初 始 化 , 得 运 用 构 造 函 数 初 始 化 或 ctor 初 始 化 语 法 。

语 法

ctor- 初 始 化 器

成 员 初 始 化 器 表成 员 初 始 化 器 表 :

成 员 初 始 化 器

成 员 初 始 化 器 , 成 员 初 始 化 器 表成 员 初 始 化 器 :

完 整 的 类 名 称 ( 表 达 式 表 opt )

标 识 符 ( 表 达 式 表 opt )

用 于 构 造 函 数 中 的 这 一 语 法 , 在 下 一 节 “ 初 始 化 成 员 对 象 ” 和 “ 初 始 化 基 类 ” 中更 详 尽 介 绍 。

初 始 化 成 员 对 象

类 可 以 含 有 类 类 型 成 员 对 象 , 但 要 保 证 满 足 对 这 些 成 员 对 象 的 初 始 化 , 并 要 满 足如 下 之 一 的 条 件 。

  • 所 含 对 象 的 类 不 要 求 有 构 造 函 数 。

  • 所 含 对 象 的 类 有 一 可 访 问 的 缺 省 构 造 函 数 。

  • 包 容 类 的 构 造 函 数 显 式 初 始 化 所 含 的 对 象 。下 面 的 例 子 给 出 了 如 何 完 成 这 样 的 初 始 化 :

// 说 明 一 个 Point 类class Point

{

public:

Point (int x,int y) {_x=x;_y=y;}

private:

int _x,_y;

}

// 说 明 一 个 包 含 有 Point 类 型 对 象 的 rectangle 类class Rect

{

public:

Rect (int x1,int y1,int x2,int y2); private:

Point _topleft,_bottomright;

}

// 定 义 类 Rect 的 构 造 函 数 , 这 一 构 造 函 数 显 式 地 初 始 化 类 Point 对象Rect :: Rect(int x1,int y1,int x2, int y2):

_topleft(x1,y1),_bottomright(x2,y2)

{

}

在 前 面 例 子 中 显 示 的 Rect 类 中 含 有 两 个 Point 类 成 员 对 象 。 它 的 构 造 函 数 显 式地 初 始 化 了 对 象 _topleft 和 _bottomright 。 注 意 跟 在 构 造 函 数 的 后 括 号 后 面 的是 冒 号 。 跟 在 冒 号 后 面 的 是 成 员 名 和 参 量 , 该 参 量 是 用 来 初 始 化 类 对 象 的 。

警 告 : 在 构 造 函 数 中 说 明 的 成 员 初 始 化 顺 序 不 会 影 响 这 些 成 员 被 构 造 的 次 序 。 成员 是 按 它 们 在 类 说 明 中 的 说 明 顺 序 被 构 造 的 。

引 用 及 常 量 成 员 对 象 必 须 用 成 员 初 始 化 语 法 ( 在 “ 初 始 化 基 类 及 成 员 ” 中 描 述 )

来 初 始 化 。 没 有 其 它 的 办 法 来 初 始 化 这 些 对 象 。

初 始 化 基 类

直 接 基 类 的 初 始 化 同 成 员 对 象 的 初 始 化 以 同 样 的 方 式 进 行 。 考 虑 下 面 的 代 码 :

// 说 明 类 窗 口class Window

{

public:

Windodw (Rect rSize);

...

}

// 说 明 类 DialogBox 直 接 派 生 于 类 Window class DialogBox:public Window

{

public:

DialogBox(Rect rSize);

...

}

// 定 义 DialogBox 构 造 函 数 。 这 一 构 造 函 数 显 式 地 初 始 化 Window 子 对 象DialogBox:: DialogBox(Rect rSize):Window(rSize)

{

}

注 意 在 DialogBox 的 构 造 函 数 中 ,Windows 基 类 是 使 用 参 量 rSize 进 行 初 始 化 的 。这 一 初 始 化 由 要 初 始 化 的 基 类 名 称 组 成 , 基 类 名 称 后 跟 随 的 是 由 圆 括 号 括 起 来 的传 递 给 基 类 构 造 函 数 的 参 量 表 。

在 基 类 的 初 始 化 中 , 不 代 表 基 类 构 件 的 子 对 象 的 对 象 称 为 一 个 “ 完 整 对 象 ”。 该“ 完 整 对 象 ” 的 类 视 为 该 对 象 的 “ 最 外 派 生 类 ” (most derived) 。

代 表 虚 拟 基 类 的 子 对 象 是 由 “ 最 外 派 生 类 ” 的 构 造 函 数 进 行 初 始 化 的 。 这 意 味着 只 要 说 明 了 虚 拟 基 类 的 派 生 类 , 则 最 外 派 生 类 必 须 显 式 初 始 化 虚 拟 基 类 , 否 则虚 拟 基 类 必 须 有 一 个 缺 省 的 构 造 函 数 。 在 最 外 派 生 类 以 外 的 其 它 类 的 构 造 函 数中 出 现 的 虚 拟 基 类 的 初 始 化 将 被 忽 略 。

尽 管 对 基 类 的 初 始 化 通 常 限 于 直 接 基 类 的 初 始 化 , 但 一 个 类 的 构 造 函 数 可 以 初 始化 一 个 非 直 接 的 虚 拟 基 类 。

基 类 及 成 员 的 初 始 化 次 序

基 类 和 成 员 对 象 按 如 下 次 序 进 行 初 始 化 :

  1. 虚 拟 基 类 的 初 始 化 按 它 们 出 现 在 有 向 无 环 图 中 的 次 序 。 有 关 使 用 有 向 无 环 图去 构 造 一 个 唯 一 子 对 象 列 表 的 详 情 见 第 9 章“ 派 生 类 ” 中 的“ 虚 拟 基 类 ” ( 注 意 , 这 些 子 对 象 的 析 构 是 按 反 序 遍 历 该 列 表 ) 。 有 关 如 何 遍 历 有 向 无 环 图 的 情 况 见 本章 前 面 的 “ 析 构 的 次 序 ”。

  2. 非 虚 拟 基 类 按 它 们 在 类 说 明 中 出 现 的 次 序 进 行 初 始 化

  3. 成 员 对 象 的 初 始 化 是 按 它 们 在 类 中 说 明 的 次 序 进 行 初

    始 化 。

在 构 造 函 数 的 成 员 初 始 化 器 表 中 说 明 的 成 员 初 始 化 器 和 基 类 初 始 化 器 的 次 序 不会 影 响 到 基 类 及 成 员 对 象 进 行 初 始 化 的 次 序 。

初 始 化 的 范 围

对 基 类 和 成 员 对 象 的 初 始 化 定 值 在 与 其 说 明 在 一 起 的 构 造 函 数 的 范 围 内 , 因 此 , 它 们 能 够 隐 含 地 引 用 类 成 员 。

拷 贝 类 对 象

两 种 操 作 会 引 起 对 象 的 拷 贝 :

  • 赋 值 。 在 一 个 对 象 的 值 被 赋 给 另 一 个 对 象 时 , 第 一 个 对 象 是 被 拷 贝给 第 二 个 对 象 的 。 因 此 :

Point a,b;

...

a=b;

会 引 导 起 b 的 值 拷 贝 给 a 。

  • 初 始 化 。 初 始 化 发 生 在 说 明 一 个 新 对 象 时 , 发 生 在 给 函 数 传 递 参 量的 值 时 , 也 生 在 以 传 值 形 式 从 函 数 返 回 参 量 时 。

程 序 员 可 以 为 类 类 型 对 象 定 义 拷 贝 的 意 义 。 考 虑 下 面 的 代 码 : TextFile a, b;

a.Open("FILE1.DAT");

b.Open("FILE2.DAT");

b=a;

上 面 的 代 码 可 以 意 味 着 : “ 把 FILE1.DAT 的 内 容 拷 贝 到 FILE2 中 ” ; 或 者 它 也 可

意 味 着 : “ 忽 略 FILE2.DAT , 并 把 b 作 为 FILE1.DAT 的 第 二 个 句 柄 ”。 程 序 员 有责 任 为 每 个 类 赋 以 合 适 的 拷 贝 语 义 。

拷 贝 由 下 列 两 种 方 法 完 成 :

  • 赋 值 ( 使 用 赋 值 运 算 符 ,operator=)

  • 初 始 化 ( 使 用 拷 贝 构 造 函 数 )( 有 关 拷 贝 构 函 数 的 详 情 见 本 章 开 头 的“ 说 明 构 造 函 数 的 规 则 ” ) 。

任 一 给 定 的 类 可 以 实 现 一 种 或 以 上 两 种 拷 贝 方 式 。 如 果 两 种 方 法 都 未 实 现 , 赋 值 将 作 为 一 个 成 员 方 式 (memberwise) 赋 值 , 初 始 化 也 作 为 成 员 方 式 初 始 化 。 在本 章 后 面 会 讨 论 有 关 “ 成 员 方 式 的 赋 值 和 初 始 化 ” 的 细 节 。

拷 贝 构 造 函 数 带 有 一 个 类 型 class - name& 的 参 量 , 其 中 class - name 是 定 义 拷 贝 构造 函 数 的 类 的 名 称 。 例 如 :

class Window

{

public:

Window (const Window&); // 定 义 拷 贝 构 造 函 数

...

};

注 意 : 只 要 可 能 , 拷 贝 构 造 函 数 的 参 量 都 应 该 是 const class-name& 。 这 可 以 防止 拷 贝 构 造 函 数 无 意 中 修 改 了 要 拷 贝 的 对 象 。 这 也 使 得 拷 贝 const 对 象 成 为 可能 。

编 译 器 生 成 的 拷 贝

编 译 器 生 成 的 拷 贝 构 造 函 数 , 就 像 用 户 自 定 义 的 拷 贝 构 造 函 数 , 带 有 一 个 “ 对 类类 型 引 用 ” 的 参 量 类 型 。 只 有 一 个 例 外 是 : 当 所 有 的 基 类 及 成 员 类 都 说 明 有 拷 贝构 造 函 数 , 并 且 该 函 数 带 有 一 个 具 有 const class -name& 型 的 参 量 时 。 在 这 种 情况 下 , 编 译 器 生 成 的 拷 贝 构 造 函 数 的 参 量 是 const 型 。

当 拷 贝 构 造 函 数 的 参 量 不 是 const 型 时 , 通 过 拷 贝 一 个 const 对 象 进 行 初 始 化 会产 生 一 个 错 误 。 但 反 过 来 就 不 同 : 如 果 参 量 类 型 是 const 型 的 , 通 过 拷 贝 一 个 非const 型 对 象 进 行 初 始 化 不 会 产 生 错 误 。

关 于 const, 编 译 器 产 生 的 赋 值 运 算 符 也 遵 循 同 样 的 方 式 。 它 们 带 有 一 个 唯 一 的class - name& 类 型 的 参 量 , 除 非 赋 值 运 算 符 在 所 有 的 基 类 及 成 员 类 中 采 用 的 参 量类 型 是 const class- name& 。 在 这 种 情 况 下 , 编 译 器 为 该 类 产 生 的 赋 值 运 算 符 采用 count 型 参 量 。

注 意 : 当 虚 拟 基 类 由 拷 贝 构 造 函 数 ( 无 论 是 编 译 器 产 生 的 , 还 是 自 定 义 的 ) 进 行 初始 化 时 , 它 们 仅 在 被 构 造 时 初 始 化 一 次 。

这 些 含 义 同 拷 贝 构 造 函 数 很 相 似 。 当 参 量 类 型 不 是 const 型 时 , 从 一 个 const 型对 象 赋 值 会 产 生 一 个 错 误 。 反 之 则 不 同 : 如 果 一 个 const 型 值 赋 给 一 个 非 const 型 值 , 赋 值 可 以 进 行 。

有 关 重 载 赋 值 运 算 符 的 更 多 的 情 况 见 第 12 章 “ 重 载 ” 中 的 “ 赋 值 ”。

成 员 方 式 的 赋 值 与 初 始 化

缺 省 的 赋 值 和 初 始 化 的 方 法 分 别 是“ 成 员 方 式 ” 的 赋 值 和“ 成 员 方 式 的 初 始 化 ”。

“ 成 员 方 式 的 赋 值 ” 由 从 一 个 对 象 到 另 一 个 对 象 的 拷 贝 过 程 组 成 。 每 次 赋 值 一个 成 员 , 就 像 每 个 成 员 单 独 赋 值 一 样 , “ 成 员 方 式 的 初 始 化 ” 也 是 由 从 一 个 对 象拷 贝 到 另 一 个 对 象 的 拷 贝 过 程 组 成 。 每 次 初 始 化 一 个 对 象 , 就 像 是 每 个 成 员 单独 被 初 始 化 。 这 两 种 方 式 的 主 要 区 别 是 : 成 员 方 式 赋 值 激 活 的 是 每 个 成 员 的 赋值 运 算 符 (operator=), 而 成 员 方 式 初 始 化 激 活 的 是 每 个 成 员 的 拷 贝 构 造 函 数 。 成 员 方 式 的 赋 值 仅 由 说 明 为 如 下 方 式 的 赋 值 运 算 符 来 实 现 的 。

type & type ::operator=([const|volatile] type &)

如 果 下 列 任 何 条 件 存 在 , 则 成 员 方 式 赋 值 的 缺 省 运 算 符 不 能 出 现 :

  • 某 成 员 类 中 有 const 成 员 。

  • 某 成 员 类 有 引 用 成 员 。

  • 某 成 员 类 或 其 基 类 有 一 个 私 有 的 赋 值 运 算 符 (operator=) 。

  • 某 成 员 类 或 其 基 类 没 有 赋 值 运 算 符 (operator=) 。

如 果 一 个 类 或 其 基 类 有 一 个 私 有 的 拷 贝 构 造 函 数 或 者 是 下 列 任 何 条 件 存 在 , 则 该类 的 对 于 成 员 方 式 初 始 化 的 缺 省 构 造 函 数 不 可 能 生 成 :

  • 某 成 员 类 有 const 成 员 。

  • 某 成 员 类 有 引 用 成 员 。

  • 某 成 员 类 或 其 基 类 有 一 个 私 有 的 拷 贝 构 造 函 数 。

  • 某 成 员 类 或 基 类 没 有 拷 贝 构 造 函 数 。

对 于 一 个 给 定 的 类 缺 省 的 赋 值 运 算 符 和 拷 贝 函 数 总 是 已 经 说 明 了 的 。 除 非 下 面的 两 个 条 件 都 得 以 满 足 , 否 则 它 们 是 未 定 义 的 。

  • 该 类 并 没 有 为 这 一 拷 贝 提 供 自 定 义 的 函 数 。

  • 程 序 要 求 提 供 此 函 数 。 如 果 碰 到 一 个 要 求 成 员 方 式 拷 贝 的 赋 值 运 算

符 或 者 初 始 化 符 , 或 者 取 了 类 的 operator= 函 数 的 地 址 则 这 一 要 求 是存 在 的 。

若 上 述 两 个 条 件 都 没 有 满 足 , 则 编 译 器 不 必 为 缺 省 的 赋 值 运 算 符 和 拷 贝 构 造 函 数产 生 代 码 (MSC 的 编 译 器 采 用 优 化 可 以 去 掉 这 些 代 码 ) 。 尤 其 当 类 说 明 了 一 个 自定 义 的 operator= 函 数 , 并 以 一 个 对 类 类 型 的 引 用 为 参 量 , 则 不 会 产 生 缺 省 的 赋值 运 算 符 函 数 。 若 类 说 明 了 一 个 拷 贝 构 造 函 数 , 则 也 不 会 产 生 缺 省 的 拷 贝 构 造 函数 。

因 此 对 于 给 定 的 类 A, 下 面 的 说 明 总 是 存 在 的 。

// 拷 贝 构 造 函 数 和 赋 值 操 作 的 隐 含 说 明A::A(const A&)

A& A::operator=(const A&)

按 上 面 的 标 准 , 这 一 定 义 只 是 在 需 要 的 时 候 才 要 提 供 。 上 面 例 子 中 的 拷 贝 构 造 函数 被 认 为 是 类 的 公 有 成 员 函 数 。

缺 省 的 赋 值 运 算 符 使 得 一 给 定 类 的 对 象 可 以 赋 值 给 其 公 有 基 类 型 的 对 象 。 考 虑下 面 的 代 码 :

class Account

{

public:

// 公 有 成 员 函 数

... private:

double _balance;

};

class Checking:public Account

{

private:

int _fOverdraftProtect;

};

...

Account account; Checking checking; account=checking;

在 上 面 的 例 子 中 , 选 用 的 赋 值 运 算 符 是 Account:operator= 。 因 为 缺 省 的operator= 函 数 带 有 一 个 类 型 Acconut& 的 参 量 , checking 的 Account 型 子 对 象拷 贝 给 account; 而 fOverdraftProtect 没 有 拷 贝 。

12 章 重 载

这 一 章 阐 述 如 何 使 用 C++ 的 重 载 函 数 以 及 重 载 运 算 符 。包 括 下 面 一 些 主 题 :

  • 重 载 概 述

  • 说 明 匹 配

  • 参 量 匹 配

  • 重 载 函 数 的 地 址

  • 重 载 运 算 符

重 载 概 述

有 了 C++ 语 言 , 你 就 可 以 重 载 函 数 和 运 算 符 。 重 载 是 一 种 应 用 , 它 在 同 一 范 围 中为 一 个 给 定 函 数 名 称 提 供 了 多 种 定 义 。 委 托 编 译 器 依 据 调 用 该 函 数 的 参 量 选 择合 适 的 函 数 或 运 算 符 的 版 本 。 例 如 :

double max(double d1,double d2)

{

return (d1>d2)?d1:d2;

}

int max (int i1, int i2)

{

return (i1>i2)?i1:i2;

}

作 为 一 个 重 载 函 数 , 函 数 max 在 程 序 中 使 用 如 下 :

main()

{

int i=max(12,8);

double d=max(32.9,17.4);

return i+(int)d;

}

在 第 一 个 例 子 中 , 要 求 出 两 个 整 型 变 量 的 最 大 值 , 故 调 用 函 数 (int,int) 。 然 而 , 在 第 二 种 情 况 下 , 两 个 参 量 是 浮 点 型 , 因 此 调 用 的 函 数 是 max(double,double)。

参 量 类 型 的 差 异

重 载 函 数 之 间 的 区 别 在 于 带 有 不 同 初 始 值 的 参 量 类 型 。 因 而 对 一 个 给 定 类 型 的参 量 以 及 对 于 该 类 型 的 引 用 , 在 重 载 的 意 义 上 来 说 是 完 全 相 同 的 。 它 们 被 看 成 是相 同 的 , 因 为 它 们 采 用 了 相 同 的 初 始 值 。 例 如 :max(double,double) 和 (double &,double &) 是 完 全 相 同 的 , 说 明 两 个 这 样 的 函 数 会 引 起 错 误 。

出 于 相 同 的 原 因 , 用 修 饰 符 const 和 volatile 进 行 修 饰 的 函 数 参 量 类 型 同 基 本

类 型 , 在 重 载 的 意 义 上 看 没 有 什 么 不 同 。

然 而 重 载 函 数 的 机 制 可 以 区 分 由 const 或 volatile 修 饰 的 引 用 以 及 基 本 类 型 的引 用 。 这 使 得 可 以 有 下 列 代 码 :

#include<iostream.h>

class Over

{

public:

Over() {cout << "Over default constructor\n"}-->

Over(over &o){cout << "Over &\n";}

Over(const Over &co) {cout << "const Over &\n";}

Over(volatile Over &vo) {cout << "volatile Over &\n";}

};

void main()

{

Over o1; // 调 用 缺 省 构 造 函 数

Over o2(o1); // 调用 Over(Over&)

const Over o3;// 调 用 缺 省 构 造 函 数

Over o4(o3); // 调用 Over(const Over &)

volatile Over o5; // 调 用 缺 省 构 造 函 数

Over o6(o5); // 调 用 Over(volatile Over &)

}

指 向 const 和 volatile 对 象 的 指 针 和 指 向 其 基 本 类 型 的 指 针 在 重 载 意 义 上 是 不同 的 。

重 载 函 数 的 限 制

  • 组 重 载 函 数 是 否 是 可 接 受 的 如 下 限 制 :

    • 该 组 重 载 函 数 中 任 何 两 个 都 必 须 有 不 同 的 参 量 表 。

    • 具 有 相 同 类 型 参 量 表 、 仅 在 返 回 值 类 型 上 不 同 的 重 载 函 数 会 引 起 错误 。

Microsoft 特 殊 处 →

用 户 可 以 仅 仅 基 于 返 回 类 型 不 同 而 重 载 new 运 算 符 。 尤 其 在 基 于 说 明 的 存 储 器模 式 修 饰 符 不 同 时 。

Microsoft 特 殊 处 结 束

  • 成 员 函 数 的 重 载 不 能 仅 基 于 一 个 说 明 为 静 态 的 , 另 一 个 说 明 为 非 静态 的 。

  • typedef 说 明 并 未 定 义 新 的 类 型 , 它 们 仅 为 已 存 在 的 类 型 引 入 了 一 个同 义 词 。 它 们 不 能 影 响 重 载 机 制 。 考 虑 下 面 的 代 码 :

typedef char * PSTR;

void Print(char * szToPrint); void Print(PSTR szToPrint);

前 面 的 两 个 函 数 有 相 同 的 参 量 表 ,PSTR 是 类 型 char * 的 同 义 词 。 在 成 员 范 围 中 ,

这 样 的 代 码 会 产 生 错 误 。

  • 枚 举 类 型 是 一 些 可 区 分 的 类 型 , 故 可 以 区 分 重 载 函 数 。

  • 从 区 分 重 载 函 数 的 意 义 上 说 , 类 型 “ 数 组 ” 和 “ 指 针 ” 是 相 同 的 。对 于 一 维 数 组 来 说 是 正 确 的 。 因 而 下 面 的 重 载 函 数 出 现 了 冲 突 , 并会 产 生 一 条 错 误 消 息 :

void Print (char * szToPrint); void Print (char szToPrint[]);

对 于 多 维 数 组 , 第 二 和 后 续 维 数 视 为 类 型 的 一 部 分 , 因 此 它 们 可 以 用 来 区 分 重 载函 数 :

void Print (char szToPrint []); void Print (char szToPrint [][7]);

void Print (char szToPrint [][9][42]);

说 明 匹 配

在 同 一 范 围 中 说 明 具 有 相 同 名 称 的 任 何 两 个 函 数 可 以 指 的 是 同 一 函 数 , 或 者 指 的是 重 载 的 两 个 不 同 函 数 。 如 果 说 明 的 参 量 列 表 含 有 相 同 的 参 量 类 型 ( 如 前 一 节 描述 的 ) , 则 此 函 数 说 明 指 的 是 同 一 函 数 ;否 则 它 们 指 的 是 用 重 载 选 择 的 不 同 函 数 。类 的 范 围 是 严 格 定 义 的 , 因 此 一 个 在 基 类 中 说 明 的 函 数 同 一 个 在 派 生 类 中 说 明的 函 数 有 不 同 的 范 围 。 如 果 一 个 在 派 生 类 中 说 明 的 函 数 同 一 个 基 类 中 说 明 的 函数 有 相 同 的 名 称 , 派 生 类 的 函 数 将 隐 藏 基 类 中 的 函 数 , 而 并 不 会 引 起 函 数 重 载 。

块 范 围 也 是 严 格 定 义 的 , 因 此 一 个 在 文 件 范 围 中 说 明 的 函 数 同 一 个 局 部 说 明 的

函 数 并 不 在 同 一 范 围 中 。 如 果 一 个 局 部 范 围 中 说 明 的 函 数 同 一 个 文 件 范 围 中 说明 的 函 数 具 有 相 同 的 名 称 , 则 局 部 说 明 的 函 数 将 隐 藏 文 件 范 围 中 说 明 的 函 数 , 而并 不 会 引 起 重 载 。 例 如 :

#include <iostream.h>

void func(int i)

{

cout << "Called file-scoped func: " << i << endl;

}

void func (char *sz)

{

cout << "Called locally declared func:" << sz << endl;

}

void main()

{

// 说 明 main 中 的 局 部 func 函 数

extern void func(char *sz);

func(3); // 错 误 : 函 数 func(int) 被 隐 藏 了

func("s");

}

上 面 的 代 码 给 出 了 函 数 func 的 两 个 定 义 。 以 char * 类 型 作 为 参 量 的 函 数 定 义对 于 main 函 数 是 局 部 的 , 因 为 有 extern 语 句 。 因 而 以 int 为 参 量 类 型 的 函 数 定义 被 隐 藏 了 起 来 , 故 第 一 个 对 func 函 数 的 调 用 是 错 误 的 。

对 于 重 载 的 成 员 函 数 , 不 同 版 本 的 函 数 可 以 给 于 不 同 的 访 问 权 限 。 它 们 仍 被 视 为在 类 的 范 围 中 , 因 而 它 们 是 重 载 函 数 。 考 虑 下 面 的 代 码 , 其 中 成 员 函 数 Deposit 被 重 载 了 ; 一 个 版 本 是 public 的 , 另 一 个 版 本 是 private 的 。

class Account

{

public:

Account();

double Deposit(double dAmount,char *szPassword); private:

double Deposit (double dAmount);

intValidate(char * szPassword);

};

上 面 的 代 码 要 提 供 一 个 Account 类 , 其 中 执 行 Deposit 函 数 , 要 求 提 供 一 个 正 确的 口 令 。 这 一 点 是 用 重 载 来 实 现 的 。 下 面 的 代 码 给 出 了 如 何 使 用 该 类 , 以 及 对于 私 有 成 员 函 数 Deposit 的 错 误 调 用 :

void main()

{

// 分 配 一 个 新 的 Account 类 型 的 对 象

Account *pAcct=new Account

// 存 $57.22 错 误 : 调 用 了 一 个 私 有 的 函 数

pAcct->Deposit(57.22);

// 存 $57.22 并 提 供 一 个 口 令 , 正 确 调 用 一 个 公 有 函 数

pAcct->Deposit(57.22,"pswd");

}

double Account::Deposit(double dAmount,char *s2Password)

{

if(Validate(szPassword))

return Deposit(dAmount);

else

return 0.0;

}

注 意 , 在 Account::Deposit 中 调 用 了 私 有 成 员 函 数 Deposit, 这 一 个 调 用 是 正确 的 。 因 为 Account::Deposit 是 一 个 成 员 函 数 , 因 而 有 权 访 问 该 类 的 私 有 成 员 。

参 量 匹 配

重 载 函 数 的 选 择 是 把 调 用 函 数 的 参 量 对 当 前 范 围 中 的 函 数 进 行 最 佳 匹 配 。 如 果

发 现 了 一 个 合 适 的 函 数 , 则 调 用 此 函 数 。“ 合 适 ” 在 此 处 的 含 义 是 下 列 之 一 :

  • 发 现 了 一 个 完 全 匹 配 。

  • 采 用 了 一 个 平 常 转 换 ( trival conversion )。

  • 执 行 了 一 个 整 型 参 量 提 升 。

  • 存 在 着 所 需 参 量 类 型 的 标 准 转 换 。

  • 存 在 着 所 需 参 量 类 型 的 用 户 自 定 义 型 的 转 换 ( 无 论 是 转 换 运 算 符 还是 构 造 函 数 ) 。

  • 发 现 了 由 (...) 表 示 的 参 量 。

编 译 器 为 每 一 个 参 量 创 建 了 一 个 候 选 函 数 集 。 候 选 函 数 是 那 些 在 该 位 置 上 的 实参 可 以 转 换 成 形 参 类 型 的 函 数 。 每 一 个 参 量 都 建 立 了 一 个 最 佳 匹 配 的 函 数 集 。被 选 择 的 函 数 就 是 所 有 这 些 集 合 的 交 集 。 如 果 交 集 包 含 有 多 个 函 数 , 重 载 就 是 有二 义 性 的 , 并 产 生 一 个 错 误 。 最 后 选 择 的 函 数 总 是 在 该 组 函 数 中 更 匹 配 , 至 少 有一 个 参 量 更 匹 配 一 些 。 如 果 情 况 不 是 这 样 ( 如 果 没 有 更 加 匹 配 者 ) , 则 函 数 调 用会 产 生 错 误 。

考 虑 下 面 的 代 码 中 的 说 明 ( 函 数 标 明 了 形 式 1, 形 式 2 和 形 式 3, 以 便 于 下 面 的 讨

论 ):

Fraction &Add(Fraction &f,long l);// 形 式 1

Fraction &Add(long l,Fraction &f); // 形式 2

Fraction &Add(Fraction &f,Fraction &f);// 形 式 3

Fraction F1,F2; 考 虑 下 面 的 语 句 :

F1=Add(F2,23);

前 面 的 语 句 构 造 了 两 个 集 合 :

集 合 1: 第 一 个 参 量 类 型 为Fraction 的 候选函数

集合 2: 可以把第二参量类型转换成整型的候选函数

形式 1 形式 1(int 可 用 标 准 转 换 转 换 为 long 型 )

形式 3

在 集 合 2 的 函 数 中 , 对 于 它 们 来 说 存 在 着 从 实 参 类 型 到 形 参 类 型 的 隐 含 转 换 。 在这 些 转 换 中 有 一 个 函 数 对 于 此 种 转 换 的 代 价 是 最 小 的 。

这 两 个 集 合 的 交 集 是 形 式 1 。 对 于 有 二 义 性 的 函 数 调 用 的 例 子 如 下 : F1=Add(3,6);

上 面 的 函 数 调 用 生 成 下 面 的 集 合 :

集合 1: 第 一 个 参 量 可 以 有 整 型 参 量的候选函数

形 式 2(int 可 以 用 标 准 转 换 转 换 成long 型 )

集合 2: 第二个参量可以有整型参量的候选函数

形式 1(int 型可以用标准转换转换成long 型 )

注 意 这 两 个 集 合 的 交 集 是 空 集 , 因 此 编 译 器 产 生 错 误 消 息 。 对 于 参 量 匹 配 , 一 个

有 n 个 缺 省 参 量 的 函 数 被 当 作 n+1 个 不 同 的 函 数 , 每 个 函 数 都 有 不 同 个 数 的 参量 。

(...) 是 作 为 通 配 符 ; 它 可 以 匹 配 任 何 实 参 。 如 果 你 不 是 极 为 小 心 地 设 计 你 的 重载 函 数 的 话 , 则 会 导 致 很 多 有 二 义 性 的 集 合 。

注 意 : 重 载 函 数 的 二 义 性 直 到 碰 到 函 数 调 用 时 才 会 发 现 。 在 那 时 , 为 函 数 调 用 的

每 个 参 数 都 建 立 了 集 合 , 并 且 你 可 以 发 现 是 否 有 非 二 义 性 的 重 载 存 在 。 这 意 味 着二 义 性 会 一 直 保 留 在 你 的 代 码 中 , 直 到 它 们 被 某 个 特 殊 的 函 数 调 用 所 发 现 。

参 量 匹 配 和 this 指 针

对 于 类 成 员 函 数 根 据 它 们 是 否 被 说 明 为 static, 将 按 不 同 的 方 式 对 待 。 因 为 非静 态 成 员 函 数 有 一 个 隐 含 的 参 量 以 支 持 this 指 针 。 非 静 态 函 数 被 视 为 比 静 态 函数 多 出 一 个 参 量 。

除 此 之 外 , 它 们 的 说 明 是 等 同 的 。 这 些 非 静 态 的 成 员 函 数 要 求 有 一 个 隐 含 的 this 指 针 以 匹 配 调 用 此 函 数 的 对 象 的 类 型 。 或 者 对 于 重 载 运 算 符 来 说 , 它 们 要 求 第 一个 参 量 以 匹 配 运 算 符 作 用 的 对 象 ( 有 关 重 载 操 纵 符 的 情 况 见 本 章 后 面 的 “ 重 载 运算 符 ” ) 。

同 重 载 函 数 的 其 它 参 量 不 同 , 在 对 this 指 针 参 量 进 行 匹 配 时 , 不 会 引 入 临 时 对象 , 也 不 会 试 图 进 行 转 换 ) 。

当 成 员 选 择 符 (->) 用 于 访 问 成 员 函 数 时 ,this 指 针 的 类 型 是 class - name *const 型 的 。 如 果 成 员 说 明 为 const 或 者 volatile, 则 类 型 相 应 地 是 const class - name

*const 或 volatile class -name * const 。

除 了 把 一 个 隐 含 的 &( 取 地 址 运 算 符 ) 加 在 对 象 名 称 的 前 面 之 外 , 成 员 选 择 符 (.) 也以 同 样 的 方 式 工 作 。 下 面 的 例 子 显 示 这 是 如 何 工 作 的 :

// 在 代 码 中 碰 到 的 表 达 式obj.name

// 编 译 器 如 何 对 待 它

(&obj)->name

在 参 量 匹 配 上 运 算 符 ->* 和 .*( 指 向 成 员 的 指 针 ) 的 左 操 作 数 同 运 算 符 . 和 -> 是 同样 对 待 的 。

参 量 匹 配 和 转 换

当 编 译 器 试 图 按 函 数 说 明 中 的 参 量 进 行 实 参 匹 配 时 , 如 果 没 有 找 到 精 确 的 匹 配 , 编 译 器 可 以 支 持 标 准 的 或 用 户 自 定 义 的 转 换 以 获 得 正 确 的 类 型 。 转 换 的 应 用 受如 下 规 则 的 限 制 :

  • 含 有 多 个 用 户 自 定 义 转 换 的 转 换 序 列 是 不 能 接 受 的 。

  • 通 过 移 去 中 间 转 换 即 可 缩 短 的 转 换 序 列 也 是 不 可 被 接 受 的 。

转 换 的 结 果 序 列 ( 如 果 有 的 话 ) 称 为 最 佳 匹 配 序 列 。 使 用 标 准 转 换 ( 第 3 章 “ 标 准转 换 ” 中 描 述 ), 可 有 几 种 办 法 把 一 个 int 型 对 象 转 换 成 unsigned long 型 :

  • 把 int 型 转 换 成 long 型 然 后 再 转 换 成 unsig n ed long 型

  • 把 int 型 直 接 转 换 成 unsigned long 型。

第 一 种 方 法 , 尽 管 达 到 了 所 希 望 的 目 标 , 但 不 是 一 个 最 佳 匹 配 的 序 列 , 因 为 存 在 着一 个 更 短 的 匹 配 序 列 。

表 12.1 给 出 了 一 组 转 换 ,称 为 平 常 转 换 。 它 们 在 对 决 定 哪 一 个 序 列 是 最佳 匹 配 序 列 中 起 有 限 的 作 用 。 平 常 转 换 影 响 选 择 序 列 的 步 骤 在 下 面 讨论 。

表 12.1 平 常 转 换

从类型 转换到类型

类型名称 类型名称 &

类型名称 & 类型名称

类型名称 [] 类型名称 *

类型名称 ( 参 量 表 ) (* 类型名称 )( 参量表 )

类型名称 const 类 型 名 称

类型名称 volatile 类型名称

类型名称 * const 类 型 名 称 *

类型名称 * volatile 类型名称 *

转 换 的 顺 序 按 如 下 进 行 试 探 :

5. 精 确 匹 配 。 在 调 用 函 数 的 参 量 类 型 和 函 数 原 型 中 说 明 的 类 型 之 间 的精 确 匹 配 总 是 最 好 的 匹 配 。 平 常 转 换 序 列 归 为 精 确 匹 配 。 然 而 一 般认 为 , 不 做 任 何 这 些 转 换 的 序 列 比 做 了 以 下 转 换 的 序 列 更 好 :

  • 从 指 针 到 指 向 const 的 指 针 (type* 到 const type*) 。

  • 从 指 针 到 指 向 volatile 的 指 针 (type * 到 volatle type*) 。

  • 从 引 用 到 对 const 的 引 用 (type & 到 const type &) 。

  • 从 引 用 到 对 volatile 的 引 用 (type & 到 volatile type &) 。

  1. 使 用 参 量 提 升 的 匹 配 。 任 何 不 归 类 为 精 确 匹 配 的 序 列 若 仅 含 有 必 要 的 参 量 提升 ( 从 float 到 double) , 则 平 常 转 换 的 序 列 将 归 类 为 使 用 参 量 提 升 的 匹 配 。 尽管 这 种 匹 配 没 有 精 确 匹 配 好 , 但 用 参 量 提 升 的 匹 配 比 用 标 准 转 换 的 匹 配 好 。

  2. 使 用 标 准 转 换 的 匹 配 。 任 何 不 能 归 为 精 确 匹 配 或 参 量 提 升 的 匹 配 的 任 何 序 列若 仅 含 有 标 准 转 换 , 则 平 常 转 换 被 归 类 为 使 用 标 准 转 换 的 匹 配 。 在 这 种 匹 配 中 , 要 遵 循 下 面 的 规 则 :

    • 把 一 个 派 生 类 的 指 针 转 换 为 一 个 指 向 其 直 接 基 类 或 非 直 接 基 类 的 指针 比 转 换 为 void * 或 const void* 更 合 适 一 些 。

    • 把 一 个 派 生 类 的 指 针 转 换 为 一 个 指 向 基 类 的 指 针 , 由 此 引 出 的 对 更靠 近 的 基 类 的 匹 配 是 转 换 为 直 接 基 类 的 指 针 。 假 设 如 图 12.1 所 示的 类 层 次 。

第 11 章 特 殊 成 员 函 数 - 图2

图 12.1 更 可 接 受 的 转 换 的 示 意 图

从 类 型 D* 转 换 为 类 型 C* 比 从 类 型 D* 转 换 为 类 型 B* 更 好 。 同 样 , 从 类 型 D* 到 类型 B* 的 转 换 比 从 类 型 D* 到 类 型 A* 的 转 换 更 好 。

同 样 的 规 则 也 适 用 于 引 用 的 转 换 。 从 类 型 D& 到 类 型 C& 比 从 类 型 D& 到 类 型 B& 更好 , 依 此 类 推 。

这 一 规 则 也 适 用 于 指 向 成 员 指 针 的 转 换 。 从 类 型 T D::* 到 类 型 T C::* 的 转 换 比从 类 型 T D::* 到 类 型 T B::* 的 转 换 更 好 , 依 此 类 推 ( 其 中 T 是 成 员 的 类 型 ) 。

上 面 的 规 则 适 用 于 在 同 一 个 给 定 的 派 生 路 径 中 。 考 虑 图 12.2 中 的 层 次 图 。

第 11 章 特 殊 成 员 函 数 - 图3

图 12.2 更 可 接 受 的 转 换 的 多 重 继 承 示 意 图

从 类 型 C* 到 类 型 B* 的 转 换 比 从 类 型 C* 到 类 型 A* 的 转 换 更 好 。 原 因 是 它 们 处 在同 一 路 径 之 上 。 但 , 从 类 型 C* 到 类 型 D* 的 转 换 并 不 比 从 类 型 C* 到 类 型 A* 更 好 , 因 为 转 换 是 沿 着 不 同 的 路 径 。

  1. 采 用 自 定 义 型 转 换 的 匹 配 。 这 种 序 列 不 能 归 类 为 精 确 匹 配 、 利 用 参 量 提 升 的匹 配 或 采 用 标 准 转 换 的 匹 配 。 归 为 采 用 自 定 义 转 换 的 匹 配 序 列 必 须 仅 含 有 用 户自 定 义 的 转 换 、 标 准 转 换 或 平 常 转 换 。 采 用 自 定 义 转 换 的 匹 配 比 采 用 省 略 符 的匹 配 要 好 些 , 但 比 用 标 准 转 换 的 匹 配 要 差 一 些 。

  2. 采 用 省 略 符 ( … ) 的 匹 配 。 任 何 匹 配 函 数 说 明 中 省 略 符 的 序 列 都 归 为 采 用 省 略符 的 匹 配 。 这 种 匹 配 是 最 差 的 匹 配 。

用 户 自 定 义 的 转 换 适 用 于 没 有 内 部 的 参 量 提 升 或 存 在 转 换 的 情 况 。 这 种 转 换 的选 择 基 于 要 被 匹 配 的 参 量 的 类 型 。 考 虑 下 面 的 代 码 :

class UDC

{

public:

operator int(); operator long();

};

void Print (int I );

UDC udc;

Print(udc);

对 于 类 UDC 可 用 的 用 户 自 定 义 的 转 换 是 int 型 和 long 型 , 因 而 编 译 器 可 以 接 受来 自 于 要 被 匹 配 的 对 象 的 类 型 的 转 换 。 存 在 着 一 个 从 UDC 到 int 型 的 转 换 , 故 选择 了 此 转 换 。

在 参 量 匹 配 过 程 中 , 标 准 转 换 可 以 用 于 参 量 及 用 户 自 定 义 转 换 的 返 回 值 。 因 而 , 下 面 的 代 码 是 可 以 工 作 的 :

void logToFile(long l);

UDC udc;

LongToFile(udc);

在 上 面 的 例 子 中 , 将 调 用 用 户 自 定 义 型 转 换 operator long 把 UDC 型 转 换 为 long 型 。 如 果 没 有 定 义 从 UDC 型到 long 型 的 转 换 , 此 转 换 将 按 如 下 过 程 进 行 : 先 用 自定 义 型 转 换 把 UDC 型 转 换 为 int 型 , 然 后 将 利 用 从 int 型 到 long 型 的 标 准 转 换来 匹 配 说 明 中 的 参 量 。

如 果 匹 配 参 量 需 要 自 定 义 型 转 换 , 则 在 评 价 最 佳 匹 配 时 是 不 考 虑 标 准 转 换 的 。 即使 有 多 个 候 选 函 数 要 求 用 户 自 定 义 转 换 , 也 不 对 标 准 转 换 进 行 考 虑 。 在 这 种 情 况下 , 这 些 函 数 是 等 同 的 。 例 如 :

class UDC1;

{

public:

UDC1(int); // 用 户 自 定 义 的 从 int 型 的 转 换

};

class UDC2

{

public:

UDC2(long); // 用 户 自 定 义 的 从 long 型 的 转 换

};

void Func(UDC1); void Fuce(UDC2);

Func(1);

这 两 个 版 本 的 Func 都 要 求 用 户 自 定 义 的 转 换 : 把 int 型 转 换 为 类 类 型 参 量 。 可能 的 转 换 如 下 :

  • 从 int 型 到 UDC1 的 转 换 ( 用 户 自 定 义 型 转 换 ) 。

  • 从 int 型 到 long 型 的 转 换 , 然 后 转 换 为 UDC2 型 ( 分 两 步 转 换 ) 。

尽 管 第 二 种 转 换 方 式 要 求 有 标 准 转 换 和 用 户 自 定 义 型 转 换 , 然 而 这 两 种 转 换 是 等同 的 。

注 意 : 用 户 自 定 义 的 转 换 被 视 为 通 过 构 造 函 数 的 转 换 或 通 过 初 始 化 的 转 换 ( 转 换函 数 ) 。 在 考 虑 最 佳 匹 配 时 , 这 两 种 方 法 视 为 是 等 同 的 。

重 载 函 数 的 地 址

不 带 参 量 表 使 用 一 个 函 数 的 名 称 会 返 回 该 函 数 的 地 址 , 例 如 : int Func(int I ,int j);

int Func(long l);

int (*pFunc)(int,int)=Func;

在 前 面 的 例 子 中 将 选 用 第 一 个 Func 函 数 , 并 且 它 的 地 址 被 拷 贝 到 pFunc 中 。 通过 参 量 表 同 目 标 的 完 全 匹 配 来 搜 索 函 数 , 编 译 器 可 以 决 定 选 择 哪 个 版 本 的 函 数 。在 重 载 函 数 中 说 明 的 参 数 表 对 照 下 列 之 一 进 行 匹 配 。

  • 一 个 被 初 始 化 的 对 象 ( 如 前 面 的 例 子 )

  • 赋 值 语 句 的 左 边

  • 一 个 函 数 的 形 参

  • 一 个 自 定 义 运 算 符 的 形 参

  • 函 数 的 返 回 值

如 果 没 有 找 到 完 全 的 匹 配 , 则 取 函 数 地 址 的 表 达 式 是 有 二 义 性 的 , 并 产 生 一 个 错误 。

注 意 , 尽 管 在 上 面 的 例 子 中 使 用 了 一 个 非 成 员 函 数 Func, 然 而 同 样 的 规 则 适 用于 取 重 载 的 成 员 函 数 的 地 址 。

重 载 运 算 符

使 用 C++, 你 可 以 重 新 定 义 大 多 数 内 部 运 算 符 的 函 数 , 这 些 函 数 可 以 基 于 全 局 方式 下 或 类 方 式 下 进 行 重 定 义 或 重 载 。 重 载 运 算 符 是 用 函 数 来 实 现 的 , 它 们 可 以 是成 员 函 数 也 可 以 是 全 局 函 数 。

  • 个 重 载 运 算 符 的 名 称 是 operator x , 其 中 x 是 出 现 在 表 12.2 中 的 运 算 符 。 例如 : 要 重 载 一 个 加 法 运 算 符 , 你 可 以 定 义 一 个 函 数 叫 作 operator+ 。 同 样 地 , 重 载

加 / 赋 值 运 算 符 (+=), 可 以 定 义 一 个 函 数 叫 作 :operator+= 。

尽 管 这 些 运 算 符 函 数 出 现 在 代 码 中 时 通 常 是 由 编 译 器 隐 含 调 用 的 , 但 它 们 可 以 按任 何 成 员 函 数 或 非 成 员 函 数 的 方 式 进 行 显 式 调 用 。

Point pt;

pt. operator + (3); // 调 用 加 法 运 算 符 向 pt. 加 3

表 12.2 可 重 定 义 的 运 算 符

运算符

名称

类型

,

逗号

双 目

!

逻辑非

单 目

!=

不等

双 目

%

取模

双 目

%=

取模 / 赋值

双 目

&

按位 AND

双 目

&

取地址

单 目

&&

逻辑和

双 目

&=

按位和 / 赋值

双 目

()

函 数 调 用

--

*

乘法

双 目

*

指针间接引用

单 目

*=

乘法 / 赋值

双 目

+

加法

双 目

+

单目加

单 目

++

增 1

单 目

+=

加法 / 赋值

双 目

-

减法

双 目

-

单 目 取 反

单 目

--

1

单 目

-=

减法 / 赋值

双 目

->

成 员 选 择

双 目

->*

指向成员的指针选择

双 目

/

除法

双 目

/=

除法 / 赋值

双 目

<

小于

双 目

<<

左移

双 目

<<=

左移 / 赋值

双 目

<=

小 于 等 于

双 目

=

赋值

双 目

==

等于

双 目

>

大于

双 目

>=

大于 / 赋值

双 目

>>

右移

双 目

>>=

右移 / 赋值

双 目

[]

数 组 下 标

— —

^

异或

双 目

^=

异或 / 赋值

双 目

|

按位或

双 目

|=

按位或 / 赋值

双 目

||

逻辑或

双 目

~

求补

单 目

delete

delete

— —

new

new

— —

* 有 两 种 方 式 的 增 1 和减 1 存 在 : 前 缀 方 式 和 后 缀 方 式 。

重 载 运 算 符 的 各 个 分 类 的 约 束 在 本 章 后 面 “ 单 目 运 算 符 ”、“ 双 目 运 算 符 ”、“ 赋值 ”、“ 函 数 调 用 ”、“ 下 标 运 算 ”、“ 类 成 员 访 问 ” 及 “ 增 1 和 减 1 ” 中 描 述 。

表 12.3 不 能 重 载 的 运 算 符

运算符 名称

. 成员选择

.* 指向成员指针的选择

:: 范围分辨符

?: 条件运算符

# 预处理符号

## 预处理符号

运 算 符 重 载 的 一 般 规 则

下 面 的 规 则 约 束 着 重 载 运 算 符 如 何 实 现 , 但 它 们 并 不 适 用 于 new 和 delete 运 算符 。 这 两 个 运 算 符 在 第 4 章 中 单 独 讨 论 。

  • 运 算 符 必 须 要 么 是 成 员 函 数 , 或 者 带 有 某 个 类 的 参 量 , 或 者 是 枚 举类 型 的 参 量 、 或 者 带 有 某 个 类 的 引 用 、 或 某 个 枚 举 的 类 型 的 引 用 。例 如 :

class Point

{

public:

point operator<(Point &); // 说 明 一 个 成 员 运 算 符 重 载

...

// 说 明 加 法 运 算 符

friend Point operator+(Point &,int);

friend Point operator+(int,Point &);

};

上 面 的 例 子 代 码 中 说 明 了 小 于 运 算 符 成 员 函 数 ; 然 而 加 法 运 算 符 是 作 为 全 局 函 数说 明 的 , 并 具 有 友 元 访 问 属 性 。 注 意 , 对 于 一 个 给 定 的 运 算 符 可 以 提 供 多 个 实 现 。在 上 面 的 加 法 运 算 符 的 例 子 中 , 为 了 方 便 加 法 的 交 换 性 , 提 供 了 两 个 实 现 。 正 如这 些 运 算 符 一 样 , 把 Point 加到 Point, 把 int 加 到 Point 的 运 算 符 也 可 以 实 现 。

  • 运 算 符 要 遵 守 它 们 同 内 部 类 型 一 起 使 用 所 指 定 的 优 先 原 则 、 分 组 及操 作 数 的 个 数 。 因 此 , 无 法 表 达 把 2 及 3 加 到 一 个 Point 对 象 中 的

含 义 , 除 了 把 2 加到 X 坐 标 中 , 把 3 加 到 Y 坐 标 中 。

  • 单 目 运 算 符 说 明 为 成 员 函 数 不 带 参 量 ; 如 果 说 明 为 全 局 函 数 , 要 带 一个 参 量 。

  • 双 目 运 算 符 说 明 为 成 员 函 数 只 带 一 个 参 量 ; 如 果 说 明 为 全 局 函 数 , 要带 两 个 参 量 。

  • 所 有 的 重 载 运 算 符 除 了 赋 值 (operator=) 外 均 可 被 派 生 类 继 承 。

  • 重 载 运 算 符 的 成 员 函 数 的 第 一 个 参 量 总 是 激 活 该 运 算 符 的 对 象 的 类类 型 参 量 ( 运 算 符 被 定 义 的 类 , 或 者 定 义 了 运 算 符 的 类 的 派 生 类 ) 。对 于 第 一 个 参 量 也 不 支 持 转 换 。

注 意 , 任 何 运 算 符 的 意 义 都 可 能 被 完 全 地 改 变 了 , 这 包 括 取 地 址 (&) 、 赋 值 (=) 、函 数 调 用 运 算 符 的 含 义 。, 同 理 , 内 部 的 类 型 可 由 于 使 用 了 运 算 符 重 载 而 改 变 。例 如 : 下 面 四 条 语 句 在 完 成 求 值 以 后 完 全 等 同 :

var=var+1; var+=1; var++;

++var;

对 于 重 载 了 运 算 符 的 类 类 型 来 说 , 这 种 确 信 是 靠 不 住 的 , 而 且 , 对 于 在 基 本 类 型中 使 用 这 些 运 算 符 的 隐 含 条 件 , 对 于 重 载 的 运 算 符 来 说 是 放 松 了 。 例 如 : 加 法 / 赋 值 操 纵 符 , 在 应 用 于 基 本 类 型 时 , 要 求 其 左 操 作 数 是 l 值 的 ; 但 此 运 算 符 重 载 以后 就 没 有 这 种 要 求 了 。

注 意 : 出 于 一 致 的 考 虑 , 最 好 还 是 遵 循 内 部 类 型 的 模 式 进 行 运 算 符 重 载 。 如 果 一个 重 载 的 运 算 符 同 它 在 其 他 上 下 文 中 的 意 义 不 同 , 则 它 只 会 引 起 迷 惑 而 不 是 更 有

用 。

单 目 运 算 符

表 12.4 给 出 了 单 目 运 算 符 。

表 12.4 可 重 载 的 单 目 运 算 符

运算符 名称

! 逻辑非

& 取地址

~ 求补

* 指针间接引用

+ 单目加

++ 增 1

- 单目取反

-- 减 1

在 表 12.4 中 所 给 出 的 运 算 符 中 , 后 缀 的 增 1 和减 1 运 算 符 在 “ 增 1 及 减 1 ” 一节 讨 论 。

说 明 一 个 单 目 运 算 符 为 一 个 非 静 态 成 员 , 必 须 按 如 下 形 式 进 行 说 明 :

ret-type operator op ()

其 中 ret-type 是 返 回 类 型 , 而 op 是 表 12.4 中 的 某 个 运 算 符 。说 明 一 个 单 目 运 算 符 为 全 局 函 数 , 必 须 按 如 下 形 式 说 明 :

ret-type operator op ( arg )

其 中 ret-typeop 的 含 义 同 成 员 运 算 符 函 数 中 的 描 述 , 而 arg 是 对 其 操 作 的 类 类型 参 量 。

注 意 : 对 于 单 目 运 算 符 的 返 回 值 没 有 限 制 。 例 如 , 对 于 一 个 逻 辑 非 运 算 符 , 返 回一 个 必 要 的 值 是 合 适 的 。 但 这 一 点 不 是 必 须 的 。

增 1 和 减 1

增 1 和减 1 运 算 符 放 入 特 殊 的 分 类 是 因 为 每 个 运 算 符 都 有 两 种 形 式 :

  • 前 缀 型 增 1 和 后 缀 型 增 1 。

  • 前 缀 型 减 1 和 后 缀 型 减 1 。

在 你 编 写 重 载 运 算 符 函 数 时 , 为 这 种 运 算 符 的 前 缀 和 后 缀 型 各 自 提 供 一 个 版 本 是很 有 用 的 。 为 分 清 这 两 个 版 本 , 要 遵 循 下 面 一 些 规 则 : 对 于 前 缀 型 运 算 符 可 按 其它 任 何 单 目 操 纵 符 完 全 相 同 的 方 式 说 明 , 后 缀 型 则 要 接 受 一 个 附 加 的 int 型 参量 。

重 要 : 当 为 后 缀 型 增 1 和 减 1 运 算 符 说 明 重 载 函 数 时 , 附 加 的 参 量 必 须 是 int 型的 , 说 明 任 何 其 它 的 类 型 都 会 产 生 错 误 。

下 面 的 例 子 显 示 了 如 何 为 类 Point 定 义 前 缀 型 和 后 缀 型 增 1 和减 1 运 算 符 函 数 : class Point

{

public:

// 说 明 前 缀 和 后 缀 型 增 1 运 算 符

Point& operator++(); // 前 缀 型 增 1 运 算 符

Point operator++(int); // 后 缀 型 增 1 运 算 符

// 说 明 前 缀 和 后 缀 型 减 1 运 算 符

Point& operator--(); // 前 缀 型 减 1 运 算 符

Point operator--(int); // 后 缀 型 减 1 运 算 符

// 说 明 缺 省 的 构 造 函 数

Point() {_x=_y=0;}

// 说 明 访 问 函 数

int x() {return _x;}

int y() {return _y;} private:

int _x,_y;

}

// 定 义 前 缀 型 增 1 运 算 符Point& Point::operator ++()

{

_x++;

_y++;

return *this;

}

// 定 义 后 缀 型 增 1 运 算 符

Point Point:: operator++(int)

{

Point temp =*this;

++*this;

return temp;

}

// 定 义 前 缀 型 减 1 运 算 符Point& Point::operator--()

{

_x--;

_y--;

return *this;

}

// 定 义 后 缀 型 减 1 运 算 符

Point Point::operator--(int)

{

Point temp= *this;

--*this;

return temp;

}

使 用 下 面 的 函 数 头 , 可 以 在 文 件 范 围 ( 全局 ) 定 义 同 样 的 运 算 符 : friend Point& operator++(Point&) // 前 缀 增 1

friend Point& operator++(Point&,int) // 后 缀 增 1 friend Point& operator--(Point&) // 前 缀 减 1 friend Point& operator--(Point&,int) // 后 缀 减 1

表 示 后 缀 型 增 1 减 1 运 算 符 的 int 型 参 量 并 不 是 普 通 地 用 作 参 量 传 递 。 它 通 常含 有 0 值 , 然 而 可 以 按 如 下 使 用 :

class Int

{

public:

Int& operator++(int n); private:

int _i;

};

Int& Int::operator++(int n);

{

if(n!=0)// 传 递 了 一 个 参 量 的 处 理 情 况

_i+=n;

else

_i++; // 没 有 传 递 参 量 的 处 理 情 况

return *this;

}

...

Int i;

i. operator++(25); // 加 25

如 上 面 所 示 的 , 除 了 显 式 调 用 之 外 没 有 别 的 语 法 可 以 用 于 使 用 增 1 减 1 运 算 符 传递 函 数 值 。 一 个 更 直 接 地 实 现 这 一 功 能 的 办 法 是 重 载 += 运 算 符 。

双 目 运 算 符

表 12.5 给 出 了 可 被 重 载 的 运 算 符 。

表 12.5 可 重 定 义 的 双 目 运 算 符

运算 符 名 称

, 逗号

!= 不等

% 取模

%= 取模 / 赋值

& 按位和

&& 逻辑和

&= 按位和 / 赋值

* 乘法

*= 乘法 / 赋值

+ 加法

+= 加法 / 赋值

- 减法

-= 减法 / 赋值

-> 成 员 选 择

->* 指向成员的指针选择

/ 除法

/= 除法 / 赋值

< 小于

<< 左移

<<= 左移 / 赋值

<= 小 于 等 于

= 赋值

== 等于

> 大于

>= 于 / 等于

>> 右移

>>= 右移 / 赋值

^ 异或

^= 异或 / 赋值

| 按位或

|= 按位或 / 赋值

|| 逻辑或

说 明 一 个 双 目 运 算 符 函 数 为 非 静 态 成 员 函 数 , 必 须 按 如 下 形 式 说 明 :

ret-type operator op ( arg )

其 中 ret-type 是 返 回 类 型 , op 是表 12.5 中 列 出 的 某 个 运 算 符 , 而 arg 则 是 一 个 任意 类 型 的 参 量 。

说 明 一 个 双 目 运 算 符 为 全 局 函 数 , 必 须 按 如 下 形 式 说 明 :

ret-type operator op ( arg1,arg2 )

其 中 ret-typeop 同 成 员 函 数 中 的 描 述 是 一 致 的 , 而 arg1arg2 是 参 量 。 至 少其 中 之 一 必 须 是 类 类 型 。

注 意 : 对 于 双 目 运 算 符 的 返 回 类 型 没 有 限 制 ; 然 而 大 多 数 用 户 自 定 义 型 双 目 运 算符 返 回 类 类 型 或 类 类 型 的 引 用 。

赋 值

赋 值 运 算 符 严 格 地 说 是 一 个 双 目 运 算 符 。 它 的 说 明 等 同 于 其 它 双 目 运 算 符 的 说明 , 但 下 列 情 况 除 外 。

  • 它 必 须 是 非 静 态 成 员 函 数 。 没 有 operator= 可 以 说 明 为 非 成 员 函 数 。

  • 它 不 能 被 派 生 类 继 承 。

  • 如 果 不 存 在 缺 省 的 operator= 函 数 , 则 编 译 器 会 为 该 类 生 成 一 个 缺 省的 函 数 ( 有 关 缺 省 的 operator= 函 数 见 第 11 章 “ 特 殊 成 员 函 数 ” 中

的 “ 成 员 方 式 的 赋 值 和 初 始 化 ” ) 。下 面 是 如 何 说 明 赋 值 运 算 符 的 示 例 :

class Point

{

public:

Point &operator=(Point &); // 右 边 是 参 量

...

}

// 定 义 赋 值 运 算 符

Point &Point::operator=(Point &ptRHS)

{

_x = ptRHS._x;

_y = ptRHS._y;

return *this ; // 赋 值 运 算 符 返 回 左 边

}

注 意 , 提 供 的 参 量 是 表 达 式 的 右 边 。 运 算 符 返 回 的 对 象 保 持 了 赋 值 运 算 符 的 行为 ( 赋 值 完 成 以 后 , 赋 值 运 算 符 将 返 回 的 是 左 边 的 值 ), 这 样 , 可 以 写 出 下 面 的 语句 :

pt1=pt2=pt3;

函 数 调 用

函 数 调 用 运 算 符 涉 及 使 用 括 号 , 它 是 双 目 运 算 符 。 函 数 的 语 法 如 下 :

语 法

primary-expression ( expression-list opt )

在 这 里 , primary-expression 是 第 一 个 操 作 数 , expression-list( 可 能 是 空 参 量 表 ) 是第 二 个 操 作 数 。 函 数 调 用 运 算 符 用 于 多 参 量 的 运 算 。 这 一 点 可 以 做 到 , 因 为expression-list 是 一 个 参 量 表 , 而 不 是 一 个 单 一 的 参 量 。 函 数 调 用 运 算 符 必 须 是一 个 非 静 态 的 成 员 函 数 。

在 函 数 调 用 运 算 符 被 重 载 以 后 , 它 不 会 改 变 函 数 被 调 用 的 行 为 , 而 只 会 影 响 到 该操 纵 符 运 用 于 某 给 定 类 类 型 时 如 何 理 解 该 运 算 符 。 例 如 : 下 面 的 代 码 通 常 是 无 意义 的 :

Point pt; pt(3,2);

给 出 适 当 的 重 载 函 数 调 用 运 算 符 , 这 一 语 法 就 可 用 来 给 X 坐 标 增 加 3 个 单 位 , 为Y 坐 标 增 加 2 个 单 位 。 下 面 的 代 码 说 明 上 述 定 义 :

class Point

{ public:

Point(){_x=_y=0;}

Point &operator()(int dx,int dy)

{ _x+=dx;_y+=dy;return *this;} private:

int _x,_y;

};

...

Point pt; pt(3,2);

注 意 , 函 数 调 用 运 算 符 适 用 于 对 象 名 , 而 不 是 函 数 名 称 。

下 标 运 算

就 像 函 数 调 用 运 算 符 一 样 , 下 标 运 算 符 ([]) 也 是 一 个 双 目 运 算 符 。 下 标 运 算 符 必须 是 一 个 非 静 态 成 员 函 数 , 并 带 有 一 个 参 量 。 这 一 参 量 可 以 是 任 何 类 型 , 并 指明 所 希 望 的 数 组 下 标 。

下 面 的 例 子 显 示 如 何 创 建 一 个 实 现 边 界 检 查 的 int 型 向 量 : #include <iostream.h>

class IntVector

{

public:

IntVector(int cElements);

~IntVector(){delete _iElements;}

int& operator[](int nSubscript); private;

int*_iElements;

int_iUpprtBound;

};

// 构 造 一 个 IntVector 对 象IntVector::IntVector(int cElements)

{

_iElements = new int[cElements];

_iUpperBound=cElements;

}

//InVetor 的 下 标 运 算 符

int& IntVector:: operator[] ( int nSubscript)

{

static int iErr=-1;

if(nSubscript>=0 && nSubscript< _iUpperBound)

return _iElements[nSubscript];

else

{

clog<<"Array bound violation."<<endl;

return iErr;

}

}

/ / 测 试 IntVector 类int main()

{

IntVector v(10);

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

v[i]=i;

v[3]=v[9];

for (i=0;i<=10;++i)

cout<<"Element:["<<i<<"]="<<v[i]<<endl;

return v[0];

}

在 上 面 的 例 子 中 , 当 i 到 10 时 ,operator[] 检 测 到 使 用 了 一 个 超 过 边 界 的 下 标 , 并 发 出 一 个 错 误 消 息 。

注 意 , 函 数 operator[] 返 回 的 是 一 个 引 用 类 型 。 这 使 它 是 一 个 l 值 的 , 使 得 在赋 值 号 的 两 边 可 以 使 用 下 标 表 达 式 。

类 成 员 访 问

类 成 员 的 访 问 可 以 通 过 重 载 成 员 选 择 符 (->) 加 以 控 制 。 在 使 用 中 , 这 一 个 运 算 符视 为 单 目 运 算 符 , 并 且 重 载 的 运 算 符 函 数 必 须 是 成 员 函 数 , 因 此 这 一 函 数 的 说 明是 :

class-type *operator->()

这 里 class-type 是 该 运 算 符 所 属 的 类 的 名 称 。 成 员 选 择 符 函 数 必 须 作 为 非 静 态 成员 函 数 。

该 运 算 符 ( 通 常 同 指 针 间 接 引 用 运 算 符 连 用 ) 用 于 实 现 “ 灵 敏 ” 指 针 , 该 指 针 使 得指 针 在 间 接 引 用 或 计 数 之 前 生 效 。

成 员 选 择 符 (.) 是 不 能 被 重 载 的 。