MXDX.s10



Similar documents
《C语言基础入门》课程教学大纲

,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,, ( ),,,, (, : ),, :,

何 秋 琳 张 立 春 视 觉 学 习 研 究 进 展 视 觉 注 意 视 觉 感 知


说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度, 对 2015 年 春 季 期 教 运 行 基

<433A5C446F63756D656E E E67735C41646D696E F725CD7C0C3E65CC2DBCEC4CFB5CDB3CAB9D3C3D6B8C4CFA3A8BCF2BBAFA3A95CCAB9D3C3D6B8C4CF31302D31392E646F63>

龚 亚 夫 在 重 新 思 考 基 础 教 育 英 语 教 学 的 理 念 一 文 中 援 引 的 观 点 认 为 当 跳 出 本 族 语 主 义 的 思 维 定 式 后 需 要 重 新 思 考 许 多 相 连 带 的 问 题 比 如 许 多 发 音 的 细 微 区 别 并 不 影 响 理 解 和

I

第2章 数据类型、常量与变量

马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则!! # #

国债回购交易业务指引

修改版-操作手册.doc

( ) 信 号 与 系 统 Ⅰ 学 科 基 础 必 修 课 教 周 2016 年 06 月 13 日 (08:00-09:35) ( )

( 二 ) 现 行 统 一 高 考 制 度 不 利 于 培 养 人 的 创 新 精 神,,,,,,,,,,,,, [ ],,,,,,,,,,, :, ;,,,,,,? ( 三 ) 现 行 统 一 高 考 制 度 不 利 于 全 体 学 生 都 获 得 全 面 发 展,, [ ],,,,,,,,,,,

Microsoft Word - 第7章 图表反转形态.doc

深圳市新亚电子制程股份有限公司

 编号:


评 委 : 李 炎 斌 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

类 似 地, 又 可 定 义 变 下 限 的 定 积 分 : ( ). 与 ψ 统 称 为 变 限 积 分. f ( ) d f ( t) dt,, 注 在 变 限 积 分 (1) 与 () 中, 不 可 再 把 积 分 变 量 写 成 的 形 式 ( 例 如 ) 以 免 与 积 分 上 下 限 的

HSK( 一 级 ) 考 查 考 生 的 日 常 汉 语 应 用 能 力, 它 对 应 于 国 际 汉 语 能 力 标 准 一 级 欧 洲 语 言 共 同 参 考 框 架 (CEF) A1 级 通 过 HSK( 一 级 ) 的 考 生 可 以 理 解 并 使 用 一 些 非 常 简 单 的 汉 语

抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 % # % % % ) % % # # + #, ) +, % % % % % % % %

导 数 和 微 分 的 概 念 导 数 的 几 何 意 义 和 物 理 意 义 函 数 的 可 导 性 与 连 续 性 之 间 的 关 系 平 面 曲 线 的 切 线 和 法 线 导 数 和 微 分 的 四 则 运 算 基 本 初 等 函 数 的 导 数 复 合 函 数 反 函 数 隐 函 数 以

名 称 生 命 科 学 学 院 环 境 科 学 1 生 物 学 仅 接 收 院 内 调 剂, 初 试 分 数 满 足 我 院 生 物 学 复 试 最 低 分 数 线 生 命 科 学 学 院 生 态 学 5 生 态 学 或 生 物 学 生 命 科 学 学 院

2006年顺德区高中阶段学校招生录取分数线


全国建筑市场注册执业人员不良行为记录认定标准(试行).doc

0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 考 核 ( 扣 原 因 ) 考 评 得 3 安 全 生 产 目 30 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 无 轻 伤 责 任 事 故 无 重 大 质 量

一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创 新 能 力, 综 合 运 用 所 学 知 识 发 现 问 题, 分 析 问 题 和 解 决

<4D F736F F D C3E6CFF2B6D4CFF3A3A8B5DAC8FDD5C220C0E0CCD8D0D4A3A92E646F63>

3 月 30 日 在 中 国 证 券 报 上 海 证 券 报 证 券 时 报 证 券 日 报 和 上 海 证 券 交 易 所 网 站 上 发 出 召 开 本 次 股 东 大 会 公 告, 该 公 告 中 载 明 了 召 开 股 东 大 会 的 日 期 网 络 投 票 的 方 式 时 间 以 及 审

用节点法和网孔法进行电路分析

<4D F736F F D20B9D8D3DAB0BABBAAA3A8C9CFBAA3A3A9D7D4B6AFBBAFB9A4B3CCB9C9B7DDD3D0CFDEB9ABCBBE C4EAC4EAB6C8B9C9B6ABB4F3BBE1B7A8C2C9D2E2BCFBCAE92E646F6378>

目 录 一 系 统 访 问... 1 二 门 户 首 页 申 报 用 户 审 核 用 户... 2 三 系 统 登 录 用 户 名 密 码 登 录 新 用 户 注 册 用 户 登 录 已 注 册 用

ETF、分级基金规模、份额变化统计

<4D F736F F D C4EAB9A4B3CCCBB6CABFCAFDD1A7D7A8D2B5BFCEBFBCCAD4B4F3B8D9D3EBD2AAC7F3>

思 想 政 治 理 论 经 核 查 无 误 思 想 政 治 理 论 经 核 查 无 误 思 想 政 治 理 论 经 核 查 无 误 思 想

¹ º ¹ º 农 业 流 动 人 口 是 指 户 口 性 质 为 农 业 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个 月 及 以 上 的 流 动 人 口 非 农 流 动 人 口 是 指 户 口 性 质 为 非 农 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个

教师上报成绩流程图

一、资质申请

中 国 软 科 学 年 第 期!!!

评 委 : 徐 岩 宇 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

一 从 分 封 制 到 郡 县 制 一 从 打 虎 亭 汉 墓 说 起

目 录 关 于 图 标... 3 登 陆 主 界 面... 3 工 单 管 理... 5 工 单 列 表... 5 搜 索 工 单... 5 工 单 详 情... 6 创 建 工 单... 9 设 备 管 理 巡 检 计 划 查 询 详 情 销 售 管

金 不 少 于 800 万 元, 净 资 产 不 少 于 960 万 元 ; (3) 近 五 年 独 立 承 担 过 单 项 合 同 额 不 少 于 1000 万 元 的 智 能 化 工 程 ( 设 计 或 施 工 或 设 计 施 工 一 体 ) 不 少 于 2 项 ; (4) 近 三 年 每 年

珠江钢琴股东大会

Microsoft Word - 文件汇编.doc

登录、注册功能的测试用例设计.doc


2 熟 悉 Visual Basic 的 集 成 开 发 环 境 3 了 解 可 视 化 面 向 对 象 编 程 事 件 驱 动 交 互 式 开 发 等 基 本 概 念 4 了 解 Visual Basic 的 特 点 环 境 要 求 与 安 装 方 法 1 Visual Basic 开 发 应 用

姓名

工 程 勘 察 资 质 标 准 根 据 建 设 工 程 勘 察 设 计 管 理 条 例 和 建 设 工 程 勘 察 设 计 资 质 管 理 规 定, 制 定 本 标 准 一 总 则 ( 一 ) 本 标 准 包 括 工 程 勘 察 相 应 专 业 类 型 主 要 专 业 技 术 人 员 配 备 技 术

随着执业中医师资格考试制度的不断完善,本着为我校中医学专业认证服务的目的,本文通过对我校中医类毕业生参加2012年和2013年的中医执业医师考试成绩及通过率、掌握率进行分析,并与全国的平均水平进行差异比较分析,以此了解我校执业中医师考试的现状,进而反映我校中医类课程总体教学水平,发现考核知识模块教学中存在的不足,反馈给相关学院和教学管理部门,以此提高教学和管理水平。

18 上 报 该 学 期 新 生 数 据 至 阳 光 平 台 第 一 学 期 第 四 周 至 第 六 周 19 督 促 学 习 中 心 提 交 新 增 专 业 申 请 第 一 学 期 第 四 周 至 第 八 周 20 编 制 全 国 网 络 统 考 十 二 月 批 次 考 前 模 拟 题 第 一 学

年 8 月 11 日, 公 司 召 开 2015 年 第 五 次 临 时 股 东 大 会, 审 议 通 过 了 关 于 公 司 <2015 年 股 票 期 权 激 励 计 划 ( 草 案 )> 及 其 摘 要 的 议 案 关 于 提 请 股 东 大 会 授 权 董 事 会 办 理 公

一 开 放 性 的 政 策 与 法 规 二 两 岸 共 同 的 文 化 传 承 三 两 岸 高 校 各 自 具 有 专 业 优 势 远 见 杂 志 年 月 日

现 场 会 议 时 间 为 :2016 年 5 月 19 日 网 络 投 票 时 间 为 :2016 年 5 月 18 日 年 5 月 19 日 其 中 通 过 深 圳 证 券 交 易 所 交 易 系 统 进 行 网 络 投 票 的 时 间 为 2016 年 5 月 19 日 9:30-

第二讲 数列

上海证券交易所会议纪要

3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05

Template BR_Rec_2005.dot

四川省农村义务教育学生

Microsoft Word - 第3章.doc

课程类 别

上证指数

i 1) 系 统 运 作 前 设 定 *1. [2.1 网 页 主 机 名 称 设 定 ] -- 设 定 校 务 系 统 的 主 机 IP 地 址, 以 供 其 他 个 人 电 脑 连 接 及 使 用 该 系 统 *2. [2.3.1 输 入 / 修 改 学 校 资 料 ] -- 输 入 系 统 使

2. 本 次 修 改 后, 投 资 者 申 购 新 股 的 持 有 市 值 要 求 市 值 计 算 规 则 及 证 券 账 户 使 用 的 相 关 规 定 是 否 发 生 了 变 化? 答 : 未 发 生 变 化 投 资 者 申 购 新 股 的 持 有 市 值 是 指, 以 投 资 者 为 单 位

新, 各 地 各 部 门 ( 单 位 ) 各 文 化 事 业 单 位 要 高 度 重 视, 切 实 加 强 领 导, 精 心 组 织 实 施 要 根 据 事 业 单 位 岗 位 设 置 管 理 的 规 定 和 要 求, 在 深 入 调 查 研 究 广 泛 听 取 意 见 的 基 础 上, 研 究 提

<4D F736F F D D323630D6D0B9FAD3A6B6D4C6F8BAF2B1E4BBAFB5C4D5FEB2DFD3EBD0D0B6AF C4EAB6C8B1A8B8E6>

( 此 页 无 正 文, 为 广 东 东 方 精 工 科 技 股 份 有 限 公 司 关 于 提 供 资 料 真 实 准 确 和 完 整 的 承 诺 函 之 签 署 页 ) 广 东 东 方 精 工 科 技 股 份 有 限 公 司 法 定 代 表 人 : 唐 灼 林 2016 年 7 月 28 日

国际财务报告准则第13号——公允价值计量

公 开 刊 物 须 有 国 内 统 一 刊 (CN), 发 表 文 章 的 刊 物 需 要 在 国 家 新 闻 出 版 广 电 总 局 ( 办 事 服 务 便 民 查 询 新 闻 出 版 机 构 查 询 ) 上 能 够 查 到 刊 凡 在 有 中 国 标 准 书 公 开

收 入 支 出 项 目 2016 年 预 算 项 目 2016 年 预 算 预 算 01 表 单 位 : 万 元 ( 保 留 两 位 小 数 ) 一 公 共 财 政 预 算 拨 款 一 人 员 经 费 一 般 财 力 人 员 支 出 成 品

<4D F736F F D20B9D8D3DA BDECB1CFD2B5C9FAC5C9C7B2B1A8B5BDB5C8D3D0B9D8B9A4D7F7B5C4CDA8D6AAA3A E362E38A3A92E646F63>

Microsoft Word - 资料分析练习题09.doc

<433A5C C6B73625C B746F705CB9FABCCAD6D0D2BDD2A9D7A8D2B5B8DFBCB6BCBCCAF5D6B0B3C6C6C0C9F3C9EAC7EBD6B8C4CFA3A CDA8D3C3B0E6A3A92E646F63>


证券代码: 证券简称:长城电脑 公告编号:

北 京 德 恒 律 师 事 务 所 关 于 一 汽 轿 车 股 份 有 限 公 司 2012 年 度 股 东 大 会 的 法 律 意 见 致 : 一 汽 轿 车 股 份 有 限 公 司 德 恒 D BJ-02 号 北 京 德 恒 律 师 事 务 所 ( 以 下 简

附 件 : 上 海 市 建 筑 施 工 企 业 施 工 现 场 项 目 管 理 机 构 关 键 岗 位 人 员 配 备 指 南 二 一 四 年 九 月 十 一 日 2

精 勤 求 学 自 强 不 息 Born to win! 解 析 : 由 极 限 的 保 号 性 知 存 在 U ( a) 当 a 时 f ( ) f ( a) 故 f ( ) 在 点 a 不 取 极 值 f ( ) f ( a) f ( ) f ( a) lim lim a a a a ( a)

正 规 培 训 达 规 定 标 准 学 时 数, 并 取 得 结 业 证 书 二 级 可 编 程 师 ( 具 备 以 下 条 件 之 一 者 ) (1) 连 续 从 事 本 职 业 工 作 13 年 以 上 (2) 取 得 本 职 业 三 级 职 业 资 格 证 书 后, 连 续 从 事 本 职 业

抗 日 战 争 研 究 年 第 期

《应用数学Ⅰ》教学大纲

21 业 余 制 -- 高 起 专 (12 级 ) 75 元 / 学 分 网 络 学 院 学 生 沪 教 委 财 (2005)49 号 江 西 化 校 工 科 22 业 余 制 -- 高 起 专 (12 级 ) 70 元 / 学 分 网 络 学 院 学 生 沪 教 委 财 (2005)49 号 吉

世华财讯模拟操作手册

!!

北京信息科技大学本科学生成绩管理办法

!!!!!

微 积 分 ( 二 ) 教 学 大 纲 2 (2010 版 ) 课 程 编 码 : 课 程 名 称 : 微 积 分 学 时 / 学 分 :36/2 先 修 课 程 : 初 等 数 学 立 体 几 何 平 面 解 析 几 何 微 积 分 ( 一 ) 适 用 专 业 : 人 力 资 源 管

年 第 期 % %! & % % % % % % &

<4D F736F F D20B3D6B2D6CFDEB6EEB1EDB8F1D7EED6D52E646F63>


上海证券交易所会议纪要

<443A5C6D B5C30312EB9A4D7F7CEC4B5B55C30322EBACFCDACCEC4B5B55C C30342EC8CBC9E7CCFC5C31332ECFEEC4BFC5E0D1B55C E30385C322EB2D9D7F7CAD6B2E12E646F63>

合 并 计 算 配 售 对 象 持 有 多 个 证 券 账 户 的, 多 个 证 券 账 户 市 值 合 并 计 算 确 认 多 个 证 券 账 户 为 同 一 配 售 对 象 持 有 的 原 则 为 证 券 账 户 注 册 资 料 中 的 账 户 持 有 人 名 称 有 效 身 份 证 明 文 件

电信系教学大纲的基本规范

三武一宗灭佛研究

定 位 和 描 述 : 程 序 设 计 / 办 公 软 件 高 级 应 用 级 考 核 内 容 包 括 计 算 机 语 言 与 基 础 程 序 设 计 能 力, 要 求 参 试 者 掌 握 一 门 计 算 机 语 言, 可 选 类 别 有 高 级 语 言 程 序 设 计 类 数 据 库 编 程 类

生产支援功能 使用说明书(IP-110 篇)

Transcription:

C + + 面 向 对 象 程 序 设 计 张 德 慧 周 元 哲 主 编 北 京

内 容 简 介 本 书 简 明 地 讲 述 了 面 向 对 象 程 序 设 计 的 基 本 概 念, 对 C + + 的 面 向 对 象 特 性 : 类 对 象 派 生 类 继 承 多 态 性 虚 函 数 C + + 标 准 库 等 作 了 深 入 浅 出 的 介 绍 本 书 还 使 用 大 量 简 单 的 实 例 循 序 渐 进 地 介 绍 了 C + + 面 向 对 象 程 序 设 计 的 基 本 编 程 方 法 本 书 论 述 清 晰, 系 统 性 强, 并 且 理 论 与 实 例 紧 密 结 合 本 书 既 可 作 为 高 等 院 校 计 算 机 或 相 关 专 业 的 教 材, 也 可 作 为 计 算 机 应 用 开 发 人 员 的 自 学 用 书 图 书 在 版 编 目 (CIP) 数 据 C + + 面 向 对 象 程 序 设 计 / 张 德 慧, 周 元 哲 主 编. 北 京 : 科 学 出 版 社, 2005 ( 面 向 21 世 纪 高 等 院 校 计 算 机 系 列 规 划 教 材 ) ISBN 7-03-015583-1 Ⅰ.C Ⅱ. 1 张 2 周 Ⅲ. C 语 言 程 序 设 计 高 等 学 校 教 材 Ⅳ. TP312 中 国 版 本 图 书 馆 CIP 数 据 核 字 (2005) 第 052972 号 责 任 编 辑 : 万 国 清 马 琳 / 责 任 校 对 : 耿 耘 责 任 印 制 : 吕 春 珉 / 封 面 设 计 : 王 浩 科 学 出 版 社 出 版 北 京 东 黄 城 根 北 街 16 号 邮 政 编 码 : 100 717 h ttp : / / www. sciencep. com 新 蕾 印 刷 厂 印 刷 科 学 出 版 社 发 行 各 地 新 华 书 店 经 销 * 2005 年 7 月 第 一 版 2006 年 7 月 第 二 次 印 刷 印 数 : 3 001 5 000 开 本 : 787 1092 1/16 印 张 : 17 3/4 字 数 : 405 000 定 价 :23.00 元 ( 如 有 印 装 质 量 问 题, 我 社 负 责 调 换 环 伟 ) 销 售 部 电 话 010-62136131 编 辑 部 电 话 010-62138978-8004 (HI02)

目 录 第 1 章 C++ 概 述 1 1.1 C++ 的 起 源 和 特 点 1 1.1.1 C++ 的 起 源 1 1.1.2 C++ 的 特 点 1 1.2 C++ 程 序 的 结 构 2 1.2.1 C 程 序 与 C++ 程 序 比 较 2 1.2.2 C++ 程 序 结 构 3 1.2.3 C++ 程 序 的 编 辑 编 译 和 运 行 5 1.3 C++ 的 新 特 性 6 1.3.1 单 行 注 释 和 新 的 I/O 流 6 1.3.2 const 修 饰 符 8 1.3.3 内 联 函 数 10 1.3.4 函 数 原 型 12 1.3.5 带 缺 省 参 数 的 函 数 14 1.3.6 函 数 名 重 载 15 1.3.7 new 和 delete 运 算 符 16 1.3.8 引 用 17 习 题 20 第 2 章 类 和 对 象 22 2.1 类 和 对 象 的 定 义 22 2.1.1 C++ 对 结 构 的 扩 展 23 2.1.2 C++ 中 类 的 定 义 23 2.1.3 C++ 类 中 的 成 员 函 数 定 义 25 2.1.4 C++ 中 对 象 的 定 义 和 使 用 26 2.1.5 C++ 中 类 的 接 口 与 实 现 29 2.1.6 类 声 明 与 类 定 义 31 2.1.7 结 构 struct 与 类 class 的 比 较 32 2.2 构 造 函 数 和 析 构 函 数 32 2.2.1 构 造 函 数 32 2.2.2 析 构 函 数 34 2.2.3 重 载 构 造 函 数 36 2.2.4 组 合 类 和 对 象 成 员 的 初 始 化 38

iv C++ 面 向 对 象 程 序 设 计 2.3 类 与 const 40 2.3.1 常 成 员 函 数 40 2.3.2 常 对 象 40 习 题 41 第 3 章 面 向 对 象 程 序 设 计 概 述 42 3.1 对 象 与 类 42 3.1.1 对 象 42 3.1.2 类 43 3.1.3 对 象 与 类 的 关 系 43 3.2 消 息 和 方 法 44 3.2.1 消 息 44 3.2.2 方 法 45 3.3 面 向 对 象 程 序 设 计 46 3.3.1 结 构 化 程 序 设 计 方 法 46 3.3.2 面 向 对 象 程 序 设 计 方 法 48 3.4 数 据 抽 象 49 3.4.1 抽 象 49 3.4.2 数 据 抽 象 和 抽 象 数 据 类 型 50 3.5 封 装 性 和 信 息 隐 藏 51 3.6 继 承 性 与 软 件 重 用 52 3.7 多 态 性 54 3.8 面 向 对 象 的 程 序 设 计 语 言 54 3.8.1 面 向 对 象 程 序 设 计 语 言 的 发 展 概 况 54 3.8.2 几 种 典 型 的 面 向 对 象 程 序 设 计 语 言 55 3.9 面 向 对 象 的 软 件 工 程 56 习 题 58 第 4 章 进 一 步 学 习 类 和 对 象 59 4.1 对 象 数 组 59 4.1.1 对 象 数 组 的 定 义 和 使 用 59 4.1.2 对 象 数 组 的 初 始 化 59 4.2 指 向 对 象 的 指 针 60 4.2.1 对 象 指 针 的 用 法 60 4.2.2 对 象 指 针 与 对 象 数 组 61 4.3 this 指 针 62 4.4 对 象 的 赋 值 64 4.5 对 象 作 为 函 数 参 数 66 4.5.1 传 值 调 用 66 4.5.2 传 址 调 用 67 4.6 从 函 数 返 回 对 象 69

目 录 v 4.7 类 的 静 态 成 员 70 4.7.1 静 态 数 据 成 员 70 4.7.2 静 态 成 员 函 数 73 4.8 类 的 友 元 75 4.8.1 友 元 函 数 76 4.8.2 友 元 类 77 习 题 80 第 5 章 堆 与 复 制 构 造 函 数 82 5.1 堆 82 5.2 new 和 delete 83 5.2.1 需 要 new 和 delete 运 算 符 的 原 因 83 5.2.2 在 堆 上 创 建 对 象 84 5.3 默 认 的 复 制 构 造 函 数 85 5.4 自 定 义 复 制 构 造 函 数 86 5.4.1 自 定 义 的 复 制 构 造 函 数 88 5.4.2 复 制 构 造 函 数 与 函 数 参 数 89 5.4.3 复 制 构 造 函 数 与 初 始 化 91 5.4.4 在 返 回 对 象 时 使 用 复 制 构 造 函 数 93 习 题 96 第 6 章 继 承 性 : 派 生 类 98 6.1 派 生 类 的 概 念 98 6.2 单 继 承 100 6.2.1 公 有 继 承 101 6.2.2 私 有 继 承 103 6.2.3 保 护 继 承 104 6.3 派 生 类 的 构 造 函 数 和 析 构 函 数 107 6.4 多 重 继 承 113 6.4.1 多 重 继 承 的 概 念 113 6.4.2 多 重 继 承 中 的 二 义 性 问 题 115 6.4.3 虚 基 类 118 6.5 赋 值 兼 容 规 则 120 6.6 应 用 举 例 123 习 题 125 第 7 章 运 算 符 重 载 128 7.1 运 算 符 重 载 的 目 的 128 7.2 运 算 符 重 载 语 法 130 7.3 成 员 运 算 符 函 数 131 7.4 友 元 运 算 符 函 数 134 7.5 成 员 运 算 符 函 数 与 友 元 运 算 符 函 数 比 较 136

vi C++ 面 向 对 象 程 序 设 计 7.6 ++ 和 -- 的 重 载 138 7.7 赋 值 运 算 符 = 的 重 载 141 7.8 下 标 运 算 符 [ ] 与 函 数 调 用 运 算 符 () 的 重 载 145 7.9 构 造 函 数 用 于 类 型 转 换 148 7.10 应 用 举 例 155 习 题 159 第 8 章 多 态 性 和 虚 函 数 161 8.1 多 态 性 概 述 161 8.2 静 态 联 编 和 动 态 联 编 162 8.3 虚 函 数 163 8.4 纯 虚 函 数 和 抽 象 类 169 8.5 应 用 举 例 173 习 题 179 第 9 章 模 板 181 9.1 模 板 的 概 念 181 9.2 函 数 模 板 181 9.3 重 载 函 数 模 板 186 9.4 类 模 板 的 定 义 189 9.5 使 用 类 模 板 194 9.6 应 用 举 例 197 习 题 209 第 10 章 类 库 和 C++ 的 标 准 模 板 库 STL 210 10.1 类 库 210 1.1.1 类 库 的 概 念 210 1.1.2 分 析 利 用 类 库 211 1.1.3 类 库 的 特 点 212 1.1.4 类 库 是 面 向 对 象 的 软 件 开 发 环 境 的 核 心 212 10.2 C++ 的 标 准 模 板 库 STL 213 10.2.1 名 字 空 间 简 介 213 10.2.2 C++ 标 准 库 的 构 成 219 10.2.3 标 准 模 板 库 STL 简 介 220 10.2.4 标 准 模 板 库 STL 应 用 举 例 221 习 题 228 第 11 章 输 入 / 输 出 流 229 11.1 C++ 的 输 入 / 输 出 流 229 11.1.1 C++ 的 输 入 / 输 出 流 229 11.1.2 C++ 流 类 库 230 11.2 重 载 输 入 / 输 出 运 算 符 231 11.2.1 重 载 输 出 运 算 符 << 232

目 录 vii 11.2.2 重 载 输 入 运 算 符 >> 233 11.2.3 综 合 应 用 举 例 234 11.3 输 入 / 输 出 格 式 控 制 237 11.3.1 使 用 ios 的 成 员 函 数 来 控 制 输 入 / 输 出 数 据 的 格 式 237 11.3.2 使 用 操 纵 符 来 控 制 输 入 / 输 出 数 据 的 格 式 240 11.4 文 件 的 输 入 / 输 出 操 作 243 11.4.1 文 件 的 打 开 与 关 闭 244 11.4.2 文 本 文 件 的 读 写 246 11.4.3 二 进 制 文 件 的 读 写 247 11.4.4 文 件 的 随 机 访 问 250 习 题 252 第 12 章 异 常 处 理 253 12.1 异 常 处 理 的 概 念 253 12.2 C 语 言 处 理 异 常 的 方 法 255 12.2.1 检 查 函 数 的 返 回 值 来 发 现 异 常 错 误 255 12.2.2 使 用 signal() 和 raise() 函 数 255 12.2.3 使 用 非 局 部 的 跳 转 Goto 函 数 255 12.3 C++ 语 言 的 异 常 处 理 方 法 256 12.3.1 C++ 程 序 处 理 异 常 的 一 般 形 式 256 12.3.2 捕 获 函 数 内 部 抛 出 的 异 常 257 12.3.3 多 个 catch 语 句 257 12.3.4 非 正 常 的 程 序 终 止 258 12.3.5 自 定 义 运 行 终 止 函 数 259 12.3.6 捕 获 所 有 的 异 常 260 12.4 异 常 类 和 C++ 标 准 异 常 262 12.4.1 异 常 类 262 12.4.2 C++ 语 言 中 的 标 准 异 常 263 12.4.3 C++ 异 常 处 理 机 制 的 好 处 266 习 题 266 主 要 参 考 文 献 274

第 6 章 继 承 性 : 派 生 类 继 承 是 面 向 对 象 程 序 设 计 的 一 个 重 要 机 制, 该 机 制 自 动 地 为 一 个 类 提 供 来 自 另 一 个 类 的 操 作 和 数 据 结 构, 这 使 得 程 序 员 只 需 在 新 类 中 定 义 已 有 类 中 没 有 的 成 员 来 建 立 新 类 即, 它 允 许 在 既 有 类 的 基 础 上 创 建 新 的 类, 新 类 可 以 从 一 个 或 多 个 既 有 类 中 继 承 函 数 和 数 据, 可 以 重 新 定 义 或 加 进 新 的 数 据 和 函 数, 从 而 新 类 不 但 可 以 共 享 原 有 类 的 属 性, 并 且 具 有 新 的 特 性, 这 样 就 形 成 类 的 层 次 理 解 继 承 是 理 解 面 向 对 象 程 序 设 计 的 关 键, 本 章 是 全 书 的 核 心 内 容 之 一 6.1 派 生 类 的 概 念 首 先, 看 一 个 飞 机 的 分 类 图 示, 如 图 6.1 所 示 飞 机 无 人 驾 驶 飞 机 有 人 驾 驶 飞 机 军 用 飞 机 民 用 飞 机 图 6.1 简 单 的 飞 机 分 类 图 如 图 6.1 所 示, 这 个 分 类 图 是 一 种 层 次 结 构, 最 高 层 是 最 普 遍 的 也 是 最 一 般 的 情 况, 其 下 每 一 层 都 比 它 的 上 一 层 的 情 况 更 为 具 体 从 图 6.1 中 可 以 看 到 有 人 驾 驶 飞 机 和 无 人 驾 驶 飞 机 拥 有 飞 机 的 全 部 特 征, 也 就 是 说, 有 人 驾 驶 飞 机 和 无 人 驾 驶 飞 机 继 承 了 飞 机 的 全 部 属 性, 而 军 用 飞 机 和 民 用 飞 机 也 继 承 了 有 人 驾 驶 飞 机 的 全 部 属 性 又 比 如, 当 我 们 描 述 鸭 子 的 特 征 时, 可 以 回 答 说 : 它 是 一 种 嘎 嘎 叫 的 鸟 鸭 子 是 鸟 类 的 派 生, 所 以 鸭 子 是 鸟 类 的 一 种, 鸭 子 又 同 时 具 有 自 己 的 特 征, 会 嘎 嘎 叫 嘎 嘎 叫 是 区 别 于 其 他 鸟 类 的 属 性 由 于 鸟 类 的 特 点 都 清 楚, 所 以 用 鸟 类 来 描 述 鸭 子, 只 要 举 出 鸭 子 自 己 所 具 有 的 特 点 就 行 了 继 承 使 我 们 描 述 事 物 的 能 力 大 大 增 强, 可 更 加 简 单 明 了 在 C++ 语 言 中, 通 过 继 承, 可 以 让 一 个 类 拥 有 另 一 个 类 的 全 部 的 属 性, 或 者 说 是 让 一 个 类 继 承 另 一 个 类 的 全 部 属 性 把 被 继 承 的 类 称 为 基 类 或 者 父 类, 而 继 承 的 类 或 者 说 是 派 生 出 来 的 新 类 称 之 为 派 生 类 或 者 子 类 对 于 图 6.1, 飞 机 类 作 为 有 人 驾 驶 飞 机 类 和 无

第 6 章 继 承 性 : 派 生 类 105 人 驾 驶 飞 机 类 的 父 类, 而 民 用 飞 机 类 和 军 用 飞 机 类 又 是 有 人 驾 驶 飞 机 类 的 子 类 或 者 派 生 类 如 果 对 于 一 个 派 生 类 只 有 一 个 基 类, 称 为 单 继 承 如 果 同 时 有 多 个 基 类, 称 为 多 重 继 承 单 继 承 可 以 看 成 是 多 重 继 承 的 一 个 最 简 单 特 例, 而 多 重 继 承 可 以 看 成 是 多 个 单 继 承 的 组 合, 如 图 6.2 所 示 汽 车 父 亲 母 亲 运 输 汽 车 专 用 汽 车 孩 子 (a) 单 继 承 (b) 多 继 承 图 6.2 单 继 承 与 多 继 承 图 6.2(a) 是 单 继 承, 运 输 汽 车 类 和 专 用 汽 车 类 就 是 从 汽 车 类 中 派 生 而 来 ; 图 6.2(b) 是 多 继 承, 孩 子 类 从 母 亲 类 和 父 亲 类 两 个 类 综 合 派 生 而 来 例 6.1 继 承 派 生 的 概 念 class Student int number; char *name; float score; public: Student(int number1, char* name1, float score1); void modify(float score1); void print(); ; 上 面 的 代 码 定 义 了 一 个 类 Student, 但 如 果 学 校 的 某 部 门 除 了 知 道 学 生 的 学 号 姓 名 分 数 外, 还 需 要 知 道 学 生 的 专 业 我 们 可 以 重 新 声 明 一 个 新 类 Ustudent class Ustudent int number; // 此 行 原 来 已 有 char *name; // 此 行 原 来 已 有 float score; // 此 行 原 来 已 有 char major; // 新 增 加 的 public: Ustudent(int number1, char* name1, float score1); void modify(float score1); void print(); ; 可 以 看 到, 类 Ustudent 代 码 中 相 当 一 部 分 都 是 原 来 已 有 的, 我 们 做 了 很 多 的 重 复 工

106 C++ 面 向 对 象 程 序 设 计 作 那 么, 是 否 有 一 种 方 法 在 利 用 原 有 声 明 的 类 作 为 基 础, 只 需 加 上 新 增 加 的 内 容, 从 而 减 少 这 些 重 复 的 工 作 量 的 方 法 呢? 答 案 是 肯 定 的,C++ 提 供 了 一 种 机 制 - 继 承 来 实 现 这 个 功 能 请 参 看 如 下 代 码 : class Student int number; char *name; float score; public: Student(int number1, char* name1, float score1); void modify(float score1); void print(); ; class Ustudent : public Student // Ustudent 为 大 学 生 类 char major; // 专 业 public: Ustudent(int number1,char *name1,char *major,float score1); void print(); ; 从 例 6.1 可 以 看 到, 声 明 的 类 Ustudent 是 利 用 原 有 声 明 的 类 Student 作 为 基 础, 只 是 在 类 Student 上 新 增 加 了 学 生 所 在 系 别 major, 而 没 有 类 Student 中 学 生 的 学 号 姓 名 分 数 再 重 复 书 写 总 之,C++ 中 类 的 继 承 层 次 自 然 地 表 达 了 人 们 分 析 问 题 时 所 用 的 分 类 结 构, 从 而 使 得 软 件 具 有 可 重 用 性, 大 大 改 善 了 软 件 系 统 的 可 理 解 性 和 可 维 护 性 利 用 派 生 类 实 现 了 代 码 的 重 用, 减 少 重 复 的 工 作 量 6.2 单 继 承 一 般 来 讲, 只 有 一 个 基 类 的 派 生 类 称 为 单 继 承, 其 形 式 如 下 : class 派 生 类 名 : 继 承 方 式 基 类 名 派 生 类 成 员 定 义 ; ; 声 明 中 的 基 类 名 是 已 有 的 类 的 名 称, 派 生 类 名 是 继 承 原 有 类 的 特 性 而 生 成 的 新 类 的 名 称 基 类 必 须 在 声 明 派 生 类 之 前 已 经 声 明 过, 否 则 会 导 致 编 译 错 误 继 承 方 式 制 定 了 派 生 类 成 员 以 及 类 外 对 象 对 于 派 生 类 从 基 类 继 承 来 的 成 员 的 访 问 权 限, 其 关 键 字 为 public private 和 protected, 分 别 表 示 它 们 的 继 承 方 式 是 公 有 继 承 ( 公 有 派 生 ) 私 有 继 承 ( 私 有 派 生 ) 和 保 护 继 承 ( 保 护 派 生 ), 如 果 省 略, 系 统 将 默 认 是 私 有 继 承

第 6 章 继 承 性 : 派 生 类 107 6.2.1 公 有 继 承 当 类 继 承 方 式 为 公 有 继 承 时, 基 类 中 的 公 有 (public) 成 员 和 保 护 (protected) 成 员 的 访 问 权 限 在 派 生 类 中 保 持 不 变, 而 基 类 的 私 有 (private) 成 员 不 可 访 问 也 就 是 说, 基 类 的 公 有 成 员 和 保 护 成 员 被 派 生 类 继 承 过 来 作 为 派 生 类 的 公 有 和 保 护 成 员, 而 基 类 的 私 有 成 员 在 派 生 类 中 不 能 直 接 使 用 例 6.2 公 有 继 承 #include <iostream> Using namespace std; class A // 基 类 A 的 定 义 public : // 公 有 成 员 void setx(int m) x=m; void sety(int n) y=n; int getx() const return x; int gety() const return y; protected : // 保 护 成 员 int x; private : // 私 有 成 员 int y; ; class B: public A // 派 生 类 B 对 基 类 A 的 公 有 继 承 public: int getsum() return x +gety(); // 直 接 访 问 从 基 类 继 承 来 的 保 护 成 员 x; 但 基 类 的 私 有 成 员 y, 只 能 通 过 接 口 函 数 访 问 ; main() B b; // 定 义 B 类 对 象 b.setx(5); // 外 部 对 象 可 以 访 问 基 类 的 公 有 数 据 成 员 b.sety(8); cout<<"x =" << b.getx()<< endl; // 访 问 基 类 的 公 有 成 员 函 数 cout<<"x+y="<<b.getsum()<< endl; // 访 问 派 生 类 的 公 有 成 员 函 数 程 序 运 行 结 果 : X=5 X+Y=13 在 例 6.2 中, 基 类 A 派 生 出 新 的 B 类, 其 继 承 方 式 是 公 有 继 承 在 基 类 A 中 声 明 了 数 据 成 员 和 成 员 函 数, 派 生 类 B 继 承 基 类 A 的 所 有 成 员 ( 缺 省 的 构 造 和 析 构 函 数 除 外 ), 从 而 实 现 了 代 码 重 用 ; 同 时, 声 明 了 自 身 的 成 员 函 数 getsum(), 加 入 了 自 身 的 独 有 特

108 C++ 面 向 对 象 程 序 设 计 征, 达 到 了 程 序 的 扩 充 因 此, 在 派 生 类 B 中, 实 际 所 拥 有 的 成 员 的 成 员 就 是 从 基 类 继 承 过 来 的 成 员 与 派 生 类 新 定 义 的 成 员 的 总 和 B 的 内 存 布 局 如 图 6.3 所 示 This 指 针 B 继 承 A 的 部 分 B 对 象 大 小 B 新 增 加 的 部 分 图 6.3 类 B 的 内 存 布 局 在 布 局 上,b 对 象 的 最 初 部 分 是 A 数 据 成 员, 指 向 b 的 this 指 针 也 就 是 指 向 A 对 象 的 指 针 由 此 看 出,b 对 象 中 包 含 有 A 对 象 部 分,b 用 this 指 针 访 同 A 成 员 与 访 问 自 己 增 加 的 成 员 没 有 差 别 在 实 现 派 生 类 B 的 成 员 函 数 getsum() 时, 直 接 访 问 从 基 类 A 继 承 来 的 保 护 成 员 x; 但 基 类 A 的 私 有 成 员 y, 只 能 通 过 基 类 的 公 有 成 员 函 数 gety() 间 接 访 问 若 写 成 int getsum() return x+ y; 则 将 出 现 编 译 错 误, 这 是 因 为 在 公 有 继 承 的 情 况 下, 派 生 类 的 成 员 无 法 直 接 访 问 基 类 中 的 私 有 成 员 下 面 说 明 程 序 的 保 护 成 员 以 公 有 方 式 被 继 承 后 的 访 问 特 性 例 6.3 保 护 成 员 以 公 有 方 式 被 继 承 后 的 访 问 特 性 #include<iostream > Using namespace std; class base // 基 类 base 的 定 义 protected: int a,b; public: void setab(int n,int m)a=n;b=n; ; class derive:public base // 派 生 类 derive 对 基 类 base 的 继 承 int c; public: void setc(int n) c=n; void showabc() cout<<a<<' '<<b<<' '<<c<<endl; ; main() derive obj;

第 6 章 继 承 性 : 派 生 类 109 obj.setab(2,4); obj.setc(3); obj.showabc(); return 0; 注 意 : 由 于 a 和 b 是 基 类 base 的 保 护 成 员, 而 且 被 派 生 类 以 公 有 方 式 继 承, 所 以 它 们 可 以 被 派 生 的 成 员 函 数 访 问 但 是 基 类 的 保 护 成 员 a 与 b 不 能 被 外 部 函 数 访 问 6.2.2 私 有 继 承 当 类 继 承 方 式 为 私 有 继 承 时, 基 类 中 的 公 有 成 员 和 保 护 成 员 都 将 以 私 有 成 员 身 份 出 现, 基 类 的 私 有 成 员 与 公 有 继 承 相 同, 不 可 访 问 也 就 是 说, 基 类 的 公 有 成 员 和 保 护 成 员 被 派 生 类 继 承 过 来 作 为 派 生 类 的 私 有 成 员, 而 基 类 的 私 有 成 员 在 派 生 类 中 不 能 直 接 使 用 例 6.4 私 有 继 承 #include <iostream > Using namespace std; class A // 基 类 A 的 定 义 public : // 公 有 成 员 void setx(int m) x=m; void sety(int n) y=n; int getx() const return x; int gety() const return y; protected : // 保 护 成 员 int x; private : // 私 有 成 员 int y; ; class B: private A // 派 生 类 B 对 基 类 A 的 私 有 继 承 public : void setbx(int m) x=m; void setby(int n) y=n; int getbx() const return getx(); int getby() const return gety(); int getsum() return x +gety(); // 直 接 访 问 从 基 类 继 承 来 的 保 护 成 员 x; 但 基 类 的 私 有 成 员 y, 只 能 通 过 接 口 函 数 访 问 ;

110 C++ 面 向 对 象 程 序 设 计 main() B b; // 定 义 B 类 的 对 象 b.setbx(5); b.setby(8); cout<< X = << b.getbx()<< endl; cout<< X+Y= <<b.getsum()<< endl; // 访 问 派 生 类 的 公 有 成 员 函 数 程 序 运 行 结 果 : X=5 X+Y=13 在 例 6.4 中, 基 类 A 派 生 出 新 的 B 类, 其 继 承 方 式 是 私 有 继 承 在 基 类 A 中 声 明 的 公 有 和 保 护 的 数 据 成 员 和 成 员 函 数 被 类 B 继 承 过 来 作 为 自 身 的 私 有 成 员 因 此, 派 生 类 B 的 对 象 无 法 直 接 访 问 基 类 A 中 的 任 一 成 员 若 在 主 函 数 中 写 成 b.setx(5); cout<< X = << b.getx()<< endl; 则 将 出 现 编 译 错 误, 这 就 必 须 在 类 B 中 专 门 定 义 公 有 成 员 函 数, 用 于 访 问 从 基 类 A 中 继 承 过 来 的 成 员 至 此, 总 结 公 有 继 承 和 私 有 继 承 的 访 问 特 性 如 表 6.1 所 示 表 6.1 公 有 继 承 和 私 有 继 承 的 访 问 特 性 基 类 成 员 基 类 私 有 成 员 基 类 公 有 成 员 继 承 方 式 私 有 公 有 私 有 公 有 派 生 类 成 员 不 可 访 问 不 可 访 问 可 访 问 可 访 问 外 部 函 数 不 可 访 问 不 可 访 问 不 可 访 问 可 访 问 6.2.3 保 护 继 承 从 6.2.1 和 6.2.2 节 中 可 以 知 道, 无 论 是 私 有 继 承 还 是 公 有 继 承, 派 生 类 是 无 权 直 接 访 问 它 的 基 类 的 私 有 成 员, 派 生 类 只 有 通 过 调 用 基 类 的 成 员 函 数 来 实 现 对 于 基 类 私 有 成 员 的 访 问 这 对 于 频 繁 访 问 基 类 私 有 成 员 的 派 生 类 是 十 分 不 方 便 的, 为 此, 引 入 保 护 成 员 保 护 成 员 可 以 被 派 生 类 的 成 员 函 数 访 问, 但 是 对 于 外 界 是 隐 藏 起 来 的, 外 部 函 数 不 能 访 问 它 例 6.5 保 护 成 员 #include <iostream > Using namespace std; class samp private: int a;

第 6 章 继 承 性 : 派 生 类 111 protected: int b; public: int c; samp( int n,int m) a=n; b=m; int geta() return a; int getb() return b; ; void main() samp obj(20,30); obj.b=3; // 非 法, 类 的 保 护 成 员 不 能 被 外 部 函 数 访 问 obj.c=4; // 合 法, 类 的 公 有 成 员 能 被 外 部 函 数 访 问 cout<<obj.geta()<<' '; // 合 法 cout<<obj.getb()<<' '<<obj.c<<endl; // 合 法 return 0; 当 类 继 承 方 式 为 保 护 继 承 时, 基 类 中 的 公 有 成 员 和 保 护 成 员 都 将 以 保 护 成 员 身 份 出 现, 基 类 的 私 有 不 可 访 问 也 就 是 说, 基 类 的 公 有 成 员 和 保 护 成 员 被 派 生 类 继 承 过 来 作 为 派 生 类 的 保 护 成 员, 而 基 类 的 私 有 成 员 在 派 生 类 中 不 能 直 接 使 用 例 6.6 保 护 继 承 #include <iostream> Using namespace std; class A // 基 类 A 的 定 义 public : // 公 有 成 员 void setx(int m) x=m; void sety(int n) y=n; int getx() const return x; int gety() const return y; protected : // 保 护 成 员 int x; private : // 私 有 成 员 int y; ; class B: protected A // 派 生 类 B 对 基 类 A 的 保 护 继 承

112 C++ 面 向 对 象 程 序 设 计 public : void setbx(int m) x=m; void setby(int n) y=n; int getbx() const return getx(); int getby() const return gety(); int getsum() return x +gety();// 直 接 访 问 从 基 类 继 承 来 的 保 护 成 员 x; 但 基 类 的 私 有 成 员 y, 只 能 通 过 接 口 函 数 访 问 ; main() B b; // 定 义 B 类 的 对 象 b.setbx(5); b.setby(8); cout<<"x =" << b.getbx()<< endl; cout<<"x+y="<<b.getsum()<< endl; // 访 问 派 生 类 的 公 有 成 员 函 数 程 序 运 行 结 果 : X=5 X+Y=13 在 例 6.6 中, 基 类 A 派 生 出 新 的 B 类 的 继 承 方 式 是 保 护 继 承, 但 对 它 的 使 用 方 法 却 与 例 6.4 的 私 有 继 承 完 全 相 同 也 就 是 说, 在 直 接 派 生 的 类 B 中, 所 有 的 成 员 的 访 问 属 性 在 保 护 继 承 和 私 有 继 承 上 都 是 完 全 相 同 的 但 是, 当 派 生 类 B 作 为 新 的 基 类, 继 续 派 生, 二 者 的 区 别 就 出 现 了 1 如 果 B 类 以 私 有 方 式 继 承 了 A 类, 那 么 A 类 中 的 公 有 和 保 护 成 员 在 B 类 中 都 是 私 有 成 员 B 类 又 作 为 新 的 基 类 派 生 出 C 类, 那 么 C 类 的 成 员 和 对 象 都 是 不 能 访 问 间 接 从 A 类 中 继 承 来 的 成 员 2 如 果 B 类 以 保 护 方 式 继 承 了 A 类, 那 么 A 类 中 的 公 有 和 保 护 成 员 在 B 类 中 都 是 保 护 成 员 B 类 又 作 为 新 的 基 类 派 生 出 C 类,A 类 中 的 公 有 和 保 护 成 员 被 C 间 接 的 继 承, 由 于 B 到 C 的 派 生 方 式 不 同, 有 可 能 是 保 护 或 者 私 有, 那 么 C 类 的 成 员 和 对 象 有 可 能 访 问 间 接 从 A 类 中 继 承 来 的 成 员 至 此, 我 们 总 结 3 类 继 承 中 基 类 和 派 生 类 的 访 问 特 性, 如 表 6.2 所 示 如 表 6.2 所 示, 在 公 有 继 承 的 情 况 下, 基 类 中 所 有 成 员 的 访 问 特 性 在 派 生 类 中 维 持 不 变 ; 在 私 有 继 承 情 况 下, 基 类 中 所 有 成 员 在 派 生 类 中 成 为 私 有 成 员 在 保 护 继 承 情 况 下, 基 类 中 所 有 成 员 在 派 生 类 中 成 为 保 护 成 员

第 6 章 继 承 性 : 派 生 类 113 表 6.2 同 继 承 类 型 中 相 应 基 类 及 派 生 类 的 访 问 特 性 继 承 类 型 基 类 成 员 特 性 派 生 类 成 员 特 性 公 有 继 承 私 有 继 承 保 护 继 承 公 有 保 护 私 有 公 有 保 护 私 有 公 有 保 护 私 有 公 有 保 护 不 可 访 问 私 有 私 有 不 可 访 问 保 护 保 护 不 可 访 问 6.3 派 生 类 的 构 造 函 数 和 析 构 函 数 系 统 在 创 建 每 一 个 类 的 对 象 时 都 自 动 的 调 用 相 应 的 构 造 函 数, 对 其 进 行 初 始 化 ; 在 终 止 一 个 对 象 时, 都 自 动 的 调 用 相 应 的 析 构 函 数 对 其 进 行 善 后 处 理 对 于 派 生 类, 它 不 能 继 承 基 类 中 的 构 造 函 数 和 析 构 函 数 作 为 自 己 的 构 造 函 数 和 析 构 函 数 派 生 类 和 基 类 都 有 其 各 自 相 应 的 构 造 函 数 和 析 构 函 数, 那 么 它 们 的 执 行 顺 序 是 什 么? 通 常 情 况 下, 当 创 建 派 生 类 对 象 时, 首 先 执 行 基 类 的 构 造 函 数, 随 后 再 执 行 派 生 类 的 构 造 函 数 ; 当 撤 销 派 生 类 对 象 时, 析 构 函 数 的 执 行 顺 序 则 正 好 与 构 造 函 数 的 顺 序 相 反, 即 先 执 行 派 生 派 的 析 构 函 数, 随 后 再 执 行 基 类 的 析 构 函 数 换 句 话 说, 构 造 函 数 按 其 引 入 的 顺 序 执 行, 析 构 函 数 按 其 引 入 的 相 反 顺 序 执 行 为 什 么 构 造 函 数 按 其 引 入 的 顺 序 执 行? 这 是 因 为 基 类 对 任 何 派 生 类 没 有 了 解, 基 类 需 要 执 行 初 始 化 独 立 于 和 超 前 于 派 生 类 执 行 的 任 何 初 始 化, 所 以 基 类 必 须 首 先 执 行 为 什 么 析 构 函 数 按 其 引 入 相 反 的 顺 序 执 行? 这 是 因 为 基 类 是 派 生 类 的 基 础, 析 构 基 类 就 意 味 着 析 构 派 生 类, 所 以 派 生 类 的 析 构 必 须 在 对 象 彻 底 撤 销 之 前 执 行 下 面, 通 过 例 6.7 来 了 解 一 下 基 类 的 构 造 函 数 析 构 函 数 和 派 生 类 的 构 造 函 数 析 构 函 数 执 行 顺 序 是 如 何 的 例 6.7 基 类 与 派 生 类 的 构 造 函 数 与 析 构 函 数 的 调 用 顺 序 #include <iostream > Using namespace std; class Animal // 基 类 : 哺 乳 动 物 Animal 类 public : // 公 有 成 员 Animal() cout<<" constructing Animal"<< endl;// 基 类 构 造 函 数 ~Animal() cout<<" destructing Animal"<< endl;// 基 类 析 构 函 数 void setm( int a=0 ) age =a; int getage() return age;

114 C++ 面 向 对 象 程 序 设 计 ; private : int age; // 私 有 成 员 class Dog: public Animal // 派 生 类 Dog 对 基 类 Animal 的 公 有 继 承 public : void setd(int a) setm(a); // 调 用 基 类 公 有 成 员 函 数 Dog () cout<<" constructing Dog"<< endl; // 派 生 类 构 造 函 数 ~Dog() cout<<" destructing Dog"<< endl; // 派 生 类 析 构 函 数 ; void main() // 主 函 数 Dog MyDog; // 定 义 Dog 类 的 对 象 MyDog.setd(2); // 设 置 MyDog 的 年 龄 cout<<"the age of Dog is " << MyDog.getage()<<"years old"<<endl; 程 序 运 行 结 果 : constructing Animal constructing Dog The age of Dog is 2 years old destructing Dog destructing Animal 如 例 6.7 中, 基 类 含 有 的 是 不 带 参 数 的 构 造 函 数 当 基 类 含 有 带 参 数 的 构 造 函 数 时, 其 基 类 与 派 生 类 是 如 何 运 行 的? 派 生 类 从 基 类 继 承 了 非 私 有 成 员 函 数 和 数 据 成 员, 但 是 在 建 立 派 生 类 的 对 象 时, 系 统 只 执 行 派 生 类 的 构 造 函 数, 而 不 会 自 动 执 行 基 类 的 构 造 函 数 也 就 是 说, 基 类 的 构 造 函 数 是 不 能 继 承 的 如 果 基 类 的 构 造 函 数 包 含 对 变 量 的 初 始 化, 那 么 在 建 立 派 生 类 的 对 象 时, 由 于 没 有 执 行 基 类 的 构 造 函 数, 因 而 就 会 使 基 类 的 变 量 未 初 始 化 所 以, 在 设 计 派 生 类 的 构 造 函 数 时, 不 仅 要 考 虑 派 生 类 所 增 加 的 变 量 初 始 化, 还 应 当 考 虑 基 类 的 变 量 初 始 化 在 执 行 派 生 类 的 构 造 函 数 时, 应 当 调 用 基 类 的 构 造 函 数 例 6.8 派 生 类 的 构 造 函 数 #include <string> #include< iostream> Using namespace std; Class stud Protected: Int num; // 基 类 stud

第 6 章 继 承 性 : 派 生 类 115 Char name[10]; Char sex; Public: Stud(int n,char nam[], char s) num = n; Strcpy(name,nam); Sex =s; ~stud() ; Class student : public stud // 派 生 类 student private: Int age; Char addr[30]; Public: Student(int n,char nam[], char s, int a, char ad[]):stud( n, nam, s) age = a; Strcpy(addr,ad); Void show() Cout<< "num:" << num << endl; Cout<< "name:" << name << endl; Cout<< "sex:" << sex << endl; Cout<< "age:" << age << endl; Cout<< "address:" << addr << endl; ~student() ; Void main() Student a(10010,"wang_li",'f',19, "115 Beijing Road,Shanghai"); Student b(10011,"zhang_zhou",'m',20, "213 Beijing Road, Shanghai"); a.show(); b.show(); 在 例 6.8 中, 当 基 类 含 有 带 参 数 的 构 造 函 数 时, 派 生 类 的 构 造 函 数 必 须 说 明 基 类 所 需 要 的 所 有 参 数, 其 构 造 函 数 一 般 格 式 如 下 : 派 生 类 构 造 函 数 名 ( 参 数 表 ): 基 类 构 造 函 数 名 ( 参 数 表 )

116 C++ 面 向 对 象 程 序 设 计 下 面, 对 例 6.8 中 派 生 类 构 造 函 数 给 予 分 析 Student(int n,char nam[], char s, int a, char ad[]):stud(n, nam,s) 派 生 类 构 造 函 数 后 面 括 号 内 的 参 数 表 包 括 参 数 的 类 型 和 参 数 名 基 类 构 造 函 数 后 面 括 号 内 的 参 数 表 只 包 括 参 数 的 参 数 名 而 不 包 括 参 数 的 类 型 从 基 类 的 声 明 中 可 以 看 到 基 类 的 构 造 函 数 stud 有 三 个 参 数 (n,nam,s), 派 生 类 构 造 函 数 student 有 五 个 参 数, 前 三 个 是 用 来 传 递 给 基 类 构 造 函 数 的, 后 面 两 个 (a,ad) 是 用 来 对 派 生 类 所 增 加 的 变 量 初 始 化 的 在 main() 函 数 中 建 立 对 象 a 时 指 定 了 五 个 实 参, 它 们 按 顺 序 传 递 给 派 生 类 构 造 函 数 的 形 参 然 后, 派 生 类 构 造 函 数 将 前 面 三 个 参 数 传 递 给 基 类 构 造 函 数 的 参 数, 如 图 6.4 所 示 ( 注 意 箭 头 的 指 向 ) Student a(10010," Wang_li",'f',19, "115 Beijing Road,Shanghai"); Student(int n,char nam[], char s, int a, char ad[]):stud( n, nam, s) 图 6.4 传 递 参 数 (1) 通 过 stud(n,nam,s ) 把 3 个 值 再 传 给 基 类 构 造 函 数, 如 图 6.5 所 示 stud(n, nam, s ) Stud(int n,char nam[], char s) 图 6.5 传 递 参 数 (2) 当 派 生 类 中 含 有 对 象 成 员 时, 其 构 造 函 数 一 般 格 式 如 下 派 生 类 构 造 函 数 名 ( 参 数 表 ): 基 类 构 造 函 数 名 ( 参 数 表 ), 对 象 成 员 名 ( 参 数 表 ) 在 定 义 派 生 类 对 象 时, 构 造 函 数 的 执 行 顺 序 如 下 : 1 基 类 的 构 造 函 数 2 对 象 成 员 的 构 造 函 数

第 6 章 继 承 性 : 派 生 类 117 3 派 生 类 的 构 造 函 数 撤 销 对 象 时, 析 构 函 数 的 调 用 顺 序 与 构 造 函 数 的 调 用 顺 序 正 好 相 反 例 6.9 基 类 含 有 子 对 象 时, 派 生 类 的 构 造 函 数 与 析 构 函 数 的 调 用 顺 序 #include <iostream> Using namespace std; class A // 基 类 A 的 定 义 public : // 公 有 成 员 A(int n) // 基 类 构 造 函 数 cout<< constructing Function of Based class A << endl; x=n; ~A() cout<< destructing Function of Based class A << endl; // 基 类 析 构 函 数 void showx() cout<<x<< endl; private : // 私 有 成 员 int x; ; class B: public A // 派 生 类 B 对 基 类 A 的 公 有 继 承 private : A a; //a 为 基 类 对 象, 作 为 派 生 类 的 对 象 成 员 public : B(int n ):A(n),a(n) // 派 生 类 B 构 造 函 数 cout<< constructing Function of Derived class B << endl; ~B() // 派 生 类 B 析 构 函 数 cout<< destructing Function of Derived class B << endl; ; int main() // 主 函 数 B b(5); // 定 义 B 类 的 对 象 b.showx (); return 0; 程 序 运 行 结 果 : constructing Function of Based class A // 基 类 的 构 造 函 数 constructing Function of Based class A // 对 象 成 员 的 构 造 函 数 constructing Function of Derived class B // 派 生 类 的 构 造 函 数

118 C++ 面 向 对 象 程 序 设 计 5 destructing Function of Derived class B destructing Function of Based class A destructing Function of Based class A 两 点 说 明 : 1 由 于 析 构 函 数 是 不 带 参 数 的, 在 派 生 类 中 是 否 要 定 义 析 构 函 数 与 它 所 属 的 基 类 无 关, 故 基 类 的 析 构 函 数 不 会 因 为 派 生 类 没 有 析 构 函 数 而 得 不 到 执 行, 它 们 各 自 是 独 立 的 2 当 基 类 构 造 函 数 不 带 参 数 时, 派 生 类 不 一 定 需 要 定 义 构 造 函 数, 然 而 当 基 类 的 构 造 函 数 带 有 至 少 一 个 参 数, 它 所 有 的 派 生 类 都 必 须 定 义 构 造 函 数 例 6.10 多 个 基 类 的 情 况 #include<iostream > Using namespace std; class base1 public: base1() cout << "Constructing base1\n"; ~ base1() cout << "Destructing base1\n"; ; class base2 public: base2() cout << "Constructing base2\n"; ~ base2() cout << "Destructing base2\n"; ; class derived : public base1, public base2 // 派 生 类 derived 从 基 类 base1 和 base2 公 有 继 承 public: derived() cout << "Constructing derived \n"; ~derived() cout << "Destructing derived \n"; ; int main() derived ob; // construct and destruct ob return 0; 程 序 运 行 结 果 : constructing base1 constructing base2 constructing derived destructing derived

第 6 章 继 承 性 : 派 生 类 119 destructing base2 destructing base1 可 以 看 到, 构 造 函 数 按 其 引 入 的 顺 序 执 行 从 左 到 右, 而 析 构 函 数 按 其 引 入 相 反 的 顺 序 执 行 若 程 序 中 的 派 生 类 的 继 承 顺 序 如 下 : class derived : public base2, public base1 则, 程 序 运 行 结 果 为 : constructing base2 constructing base1 constructing derived destructing derived destructing base1 destructing base2 6.4 多 重 继 承 6.4.1 多 重 继 承 的 概 念 一 个 派 生 类 有 两 个 或 者 两 个 以 上 的 基 类 的 派 生 类 称 为 多 重 继 承 或 多 继 承 多 继 承 可 以 看 作 是 单 继 承 的 扩 充, 派 生 类 与 每 个 基 类 之 间 的 关 系 仍 是 一 个 单 继 承, 因 此, 多 继 承 可 以 看 作 是 多 个 单 继 承 的 组 合, 单 继 承 可 以 看 作 是 多 继 承 的 特 例, 其 形 式 如 下 class 派 生 类 名 : 继 承 方 式 基 类 名 1, 继 承 方 式 基 类 名 2,.., 继 承 方 式 基 类 名 n 派 生 类 成 员 定 义 ; ; 例 如, 类 D 是 多 重 继 承 的 派 生 类, 它 有 三 个 基 类 : 类 A 类 B 和 类 C class A ; class B ; class C ; class D:public A,public B,public C

120 C++ 面 向 对 象 程 序 设 计 ; 派 生 类 D 继 承 了 三 个 基 类, 继 承 方 式 都 是 公 有 继 承, 那 么 类 D 的 成 员 将 包 含 类 A 类 B 和 类 C 的 成 员 以 及 类 D 自 身 的 成 员 需 要 注 意, 每 一 个 基 类 必 须 制 定 其 继 承 的 方 式, 如 果 省 略, 相 应 的 基 类 则 取 私 有 继 承 类 型, 而 不 是 和 前 一 个 基 类 取 相 同 的 继 承 类 型 例 如 : class A ; class B ; class C:public A,B ; 在 这 个 例 子 中, 从 类 A 派 生 出 类 C 时 采 用 的 是 公 有 继 承, 即 类 A 中 的 公 有 成 员 和 保 护 成 员 被 类 C 继 承 过 来 并 且 成 员 类 型 保 持 不 变, 但 类 C 中 的 成 员 函 数 不 能 访 问 类 A 的 私 有 成 员 ; 对 于 类 B, 由 于 表 达 式 中 没 有 显 式 列 出 继 承 的 方 式, 系 统 将 为 其 取 默 认 值 私 有 继 承, 这 样, 类 B 中 的 公 有 成 员 和 保 护 成 员 被 类 C 继 承 过 来 作 为 类 B 的 私 有 成 员 在 多 重 继 承 时, 派 生 类 的 构 造 函 数 形 式 如 下 : class 派 生 类 名 ( 总 参 数 表 ): 基 类 名 1( 参 数 表 1), 基 类 名 2( 参 数 表 2) 派 生 类 构 造 函 数 体 ; ; 需 要 强 调 的 是 各 个 基 类 构 造 函 数 的 执 行 顺 序 取 决 于 定 义 派 生 类 时 所 制 定 的 各 个 基 类 的 顺 序, 而 与 派 生 类 构 造 函 数 的 成 员 初 始 化 列 表 中 给 定 的 基 类 顺 序 无 关 例 6.11 多 重 继 承 #include <iostream> Using namespace std; class A // 基 类 A 的 定 义 public : // 公 有 成 员 void setx(int a) x=a; void sety(int b) y=b; int getx()const return x; int gety()const return y; protected : // 保 护 成 员

第 6 章 继 承 性 : 派 生 类 121 int x; private : // 私 有 成 员 int y; ; class B // 基 类 B 的 定 义 public : // 公 有 成 员 void setz(int c) z=c; int getz()const return z; protected: // 保 护 成 员 int z; ; class C: public A,private B // 派 生 类 C 对 基 类 A 的 公 有 继 承 和 对 基 类 B 的 私 有 继 承 public : void setcz(int c)setz(c); int getcz()const return z; int getsum()const return x + gety()+ z; ; void main() // 主 函 数 C c; // 定 义 C 类 的 对 象 c.setx(2); c.sety(3); c.setcz(5); cout<<"x =" << c.getx()<<"\t Y="<<c.gety()<< "\t Z="<<c.getCz() << endl; cout<<"x +Y+ Z="<<c.getsum()<< endl; 程 序 运 行 结 果 : X =2 Y=3 Z=5 X +Y+ Z=10 6.4.2 多 重 继 承 中 的 二 义 性 问 题 在 多 重 继 承 的 情 况 下, 可 能 会 造 成 派 生 类 对 基 类 成 员 访 问 的 不 唯 一 性, 即 二 义 性 问 题 下 面 介 绍 两 种 情 况 :

122 C++ 面 向 对 象 程 序 设 计 1. 调 用 不 同 基 类 的 相 同 成 员 时 可 能 出 现 二 义 性 例 6.12 产 生 二 义 性 情 况 之 一 class A // 基 类 A public : void f(); ; class B // 基 类 B public : void f(); void g(); ; class C : public A,public B public : void g(); void h(); // 派 生 类 C ; void main() C c; // 定 义 派 生 类 C 的 对 象 c c.f(); // 存 在 二 义 性 在 例 6.12 中, 主 函 数 的 c.f(); 便 存 在 二 义 性, 系 统 不 能 自 行 判 断 通 过 它 们 的 派 生 类 C 的 对 象 名 访 问 的 是 同 名 成 员 f() 中 哪 一 个 解 决 的 方 法 可 以 使 用 作 用 域 运 算 符 ::, 用 类 名 对 成 员 加 以 限 定 c.a::f(); // 表 明 类 A 的 f() 函 数 c.b::f(); // 表 明 类 B 的 f() 函 数 2. 访 问 共 同 基 类 的 成 员 时 可 能 出 现 二 义 性 当 一 个 派 生 类 有 多 个 基 类, 而 这 些 基 类 中 的 部 分 或 全 部 又 有 一 个 公 共 的 基 类, 在 这 些 具 有 公 共 基 类 的 多 个 直 接 基 类, 从 上 一 级 公 共 基 类 中 继 承 而 来 的 成 员 就 有 相 同 的 名 称 对 这 个 共 同 基 类 中 的 成 员 的 访 问 可 能 出 现 二 义 性, 见 例 6.13 例 6.13 产 生 二 义 性 情 况 之 二 class A // 基 类 A public : void fun() ;

第 6 章 继 承 性 : 派 生 类 123 class B:public A // 派 生 类 B 对 基 类 A 公 有 继 承 public: void funb() ; class C : public A // 派 生 类 C 对 基 类 A 公 有 继 承 public : void func() ; class D : public B, public C // 派 生 类 D 继 承 基 类 B 和 基 类 C public : void fund() ; void main() D d; // 定 义 派 生 类 D 的 对 象 d d.fun(); // 存 在 二 义 性 在 例 6.13 中, 类 D 的 派 生 过 程 如 图 6.6 所 示 我 们 看 到,B 和 C 是 继 承 了 A, 而 D 继 承 了 B 和 C, 这 就 A 意 味 着 两 个 A 的 拷 贝 出 现 在 一 个 类 型 为 D 的 对 象 中 所 以 在 d.fun(); 的 表 达 式 中, 究 竟 引 用 哪 个 fun(), 是 B 中 还 是 C 中? 由 此, 语 句 是 二 义 性 的, 如 图 6.6 所 示 可 以 采 用 两 种 方 B C 法 来 修 改 这 个 二 义 性 错 误 方 法 一 : 类 D 要 访 问 其 间 接 的 基 类 A 时, 就 必 须 指 定 要 访 问 的 哪 个 路 径 上 的 A 的 拷 贝 故, 其 解 决 的 方 法 仍 是 使 用 作 用 D 域 运 算 符 ::, 用 类 名 对 成 员 加 以 限 定 d.b::fun(); 图 6.6 公 共 基 类 的 类 图 示 d.c::fun(); 需 要 注 意 : 在 程 序 中 必 须 采 用 派 生 类 的 直 接 基 类 名 来 限 定, 而 不 是 需 要 访 问 成 员 所 在 的 类 的 类 名 若 : d.a::fun(); 就 是 错 误 的, 它 不 能 消 除 二 义 性 运 用 ::, 程 序 可 以 选 择 是 类 B 或 者 类 C 的 fun() 形 式, 但 是 这 种 方 法 提 出 一 个 更 为 深 刻 的 问 题 : 如 果 实 际 只 需 要 A 的 一 个 拷 贝 将 怎 么 办? 有 没 有 办 法 避 免 两 个 拷 贝 包 含 在 D 中? 答 案 是 肯 定 的 这 就 是 第 二 种 方 法 : 通 过 使 用 虚 基 类 来 实 现, 这 部 分 内 容 将 在 6.4.3 节 介 绍 纵 上 所 示, 由 于 存 在 二 义 性, 所 以 一 个 类 不 能 直 接 从 同 一 个 类 继 承 一 次 以 上, 比 如 : class person

124 C++ 面 向 对 象 程 序 设 计 public : int age; ; class couple: public person, public person ; 就 是 错 误 的, 根 本 无 法 访 问 person 类 中 的 成 员 对 于 这 种 情 况 应 该 放 弃 继 承 的 方 式, 而 采 用 在 类 中 使 用 对 象 成 员 的 方 法 来 解 决 可 将 上 例 改 写 为 : class person public : int age; ; class couple : public person person husband,wife; //husband,wife 为 基 类 对 象, 作 为 派 生 类 couple 的 对 象 成 员 ; 6.4.3 虚 基 类 如 果 一 个 派 生 类 是 从 多 个 基 类 派 生 出 来 的, 而 这 些 基 类 的 部 分 或 者 全 部 又 有 一 个 公 共 的 基 类, 那 么, 在 这 个 派 生 类 将 包 含 其 间 接 公 共 基 类 的 两 个 或 多 个 副 本, 而 往 往 只 需 要 使 用 这 多 个 副 本 中 的 任 一 个 C++ 为 了 允 许 程 序 中 只 有 公 共 基 类 的 一 个 副 本 ( 或 者 一 个 拷 贝 ), 引 入 了 虚 基 类 (virtual) 的 概 念 当 将 公 共 基 类 声 明 为 虚 基 类 时, 而 且 还 可 以 节 省 内 存 空 间, 程 序 也 更 简 洁 在 声 明 派 生 类 时, 虚 基 类 的 声 明 形 式 如 下 : class 派 生 类 名 : virtual 派 生 类 型 基 类 名 例 如 : class B private: int b;; class B1 : virtual public B private: int b1;; // 为 了 对 祖 先 类 只 继 承 一 次,C++ 引 入 了 虚 基 类 的 概 念 class B2 : virtual public B private: int b2;; // 为 了 对 祖 先 类 只 继 承 一 次,C++ 引 入 了 虚 基 类 的 概 念 class C : public B1, public B2 private: float d; 下 面 的 访 问 是 正 确 的 : C d; d.b; 虚 基 类 的 构 造 函 数 与 一 般 的 多 重 继 承 的 构 造 函 数 在 语 法 上 一 样, 但 构 造 函 数 的 调 用

第 6 章 继 承 性 : 派 生 类 125 顺 序 不 同, 其 调 用 顺 序 如 下 : 1 若 同 一 层 次 中 包 含 多 个 虚 基 类, 这 些 虚 基 类 的 构 造 函 数 按 它 们 说 明 的 先 后 次 序 调 用 2 若 虚 基 类 由 非 虚 基 类 派 生 而 来, 则 仍 然 先 调 用 基 类 构 造 函 数, 再 调 用 派 生 类 的 构 造 函 数 3 若 同 一 层 次 中 同 时 包 含 虚 基 类 和 非 虚 基 类, 应 先 调 用 虚 基 类 的 构 造 函 数, 再 调 用 非 虚 基 类 的 构 造 函 数, 最 后 调 用 派 生 类 构 造 函 数 例 6.14 虚 基 类 构 造 函 数 的 执 行 #include <iostream > Using namespace std; class A // 基 类 A 的 定 义, 类 A 为 虚 基 类 public : // 公 有 成 员 A(int a) x=a; cout<<" 调 用 类 A 构 造 函 数 "<<endl; ~A() ; int getx()const return x; protected : // 保 护 成 员 int x; ; class B : virtual public A // 类 B 的 定 义 public : // 公 有 成 员 B():A(1)cout<<" 调 用 类 B 构 造 函 数 "<<endl; ~B() ; ; class C : virtual public A // 类 C 的 定 义 public : // 公 有 成 员 C():A(2) cout<<" 调 用 类 C 构 造 函 数 "<<endl; ~C() ; ; class D: public B,private C // 类 D 的 定 义 public : // 公 有 成 员 D( int d ):A(d) cout<<" 调 用 类 D 构 造 函 数 "<<endl; ~D() ; ;

126 C++ 面 向 对 象 程 序 设 计 void main() // 主 函 数 D d(3); // 定 义 D 类 的 对 象 cout<< X= <<d.getx()<< endl; 程 序 运 行 结 果 : 调 用 类 A 构 造 函 数 调 用 类 B 构 造 函 数 调 用 类 C 构 造 函 数 调 用 类 D 构 造 函 数 X =3 在 这 个 结 果 中, 我 们 知 道 类 A 构 造 函 数 仅 执 行 了 一 次, 且 类 A 中 数 据 成 员 是 用 声 明 类 D 对 象 时 指 定 的 值 初 始 化 的, 但 类 B 类 C 对 类 A 的 初 始 化 并 没 有 执 行 至 此, 我 们 给 虚 基 类 小 结 如 下 : (1) 虚 基 类 的 应 用 用 于 有 共 同 基 类 的 场 合 (2) 定 义 以 virtual 修 饰 说 明 基 类 例 : class B1:virtual public B (3) 虚 基 类 的 作 用 1 主 要 用 来 解 决 多 继 承 时 可 能 发 生 的 对 同 一 基 类 继 承 多 次 而 产 生 的 二 义 性 问 题 2 为 最 远 的 派 生 类 提 供 唯 一 的 基 类 成 员, 而 不 重 复 产 生 多 次 拷 贝 (4) 注 意 事 项 在 第 一 级 继 承 时 就 要 将 共 同 基 类 设 计 为 虚 基 类 6.5 赋 值 兼 容 规 则 赋 值 兼 容 规 则 是 指 在 公 有 继 承 时, 任 何 可 以 使 用 基 类 对 象 的 地 方 都 可 以 用 其 派 生 类 的 对 象 替 代 使 用 具 体 的 赋 值 规 则 如 下 (1) 可 以 用 派 生 类 的 对 象 给 基 类 对 象 赋 值 class A ; class B : public A void fun() ; A a; B b; a=b; // 将 对 象 b 中 所 含 类 A 成 员 的 值 赋 给 对 象 a 赋 值 的 结 果 是 将 派 生 类 对 象 中 所 含 基 类 成 员 的 值 赋 给 基 类 对 象 中 相 同 的 成 员 (2) 可 以 用 派 生 类 的 对 象 初 始 化 基 类 对 象 的 引 用 class A class B : public A void fun() ; A a;

第 6 章 继 承 性 : 派 生 类 127 B b; A &a=b; // 用 派 生 类 对 象 初 始 化 基 类 对 象 的 引 用 (3) 可 以 用 派 生 类 的 对 象 的 地 址 给 基 类 对 象 的 指 针 赋 值 class A ; class B : public A void fun() ; A a; B b; A *pa =& b; // 用 派 生 类 对 象 的 地 址 初 始 化 基 类 对 象 的 指 针 例 6.15 赋 值 兼 容 规 则 class A // 基 类 A public: void print1(); ; class B: public A // 派 生 类 B 公 有 继 承 基 类 A public: void print2(); ; void main() A op1,*ptr; B op2; ptr = &op2; // 基 类 对 象 的 指 针 指 向 派 生 类 对 象 ptr->print1(); // 正 确, 可 以 调 用 基 类 继 承 的 成 员 ptr->print2(); // 错 误, 不 能 访 问 派 生 类 的 成 员 由 例 6.15, 我 们 总 结 出 赋 值 兼 容 规 则 的 注 意 事 项 如 下 : 1 不 能 将 一 个 派 生 类 对 象 的 指 针 指 向 其 基 类 对 象 2 通 过 指 向 基 类 对 象 的 指 针 可 以 指 向 它 的 公 有 派 生 的 对 象, 但 不 能 指 向 私 有 派 生 的 对 象 3 这 种 指 针 只 能 访 问 从 基 类 中 继 承 的 公 有 成 员, 不 能 访 问 派 生 类 本 身 的 成 员 例 6.16 赋 值 兼 容 规 则 #include <iostream> Using namespace std; class A; void fun(a *p); class A // 基 类 A 的 定 义 public : // 公 有 成 员 void display () cout<< 调 用 类 A 的 函 数 <<endl; int x; ;

128 C++ 面 向 对 象 程 序 设 计 class B : public A // 类 B 的 定 义 public : // 公 有 成 员 void display() cout<< 调 用 类 B 的 函 数 <<endl; int y; ; class C : public B // 类 C 的 定 义 public : // 公 有 成 员 void display() cout<< 调 用 类 C 的 函 数 <<endl; ; A a; B b; C c; c.x=1; c.y=2; a=c; b=c; void main() // 主 函 数 cout<<"a.x="<<a.x<< "\t b.y="<<b.y<<endl; b.x=10; b.y=20; a=b; cout<<"a.x="<<a.x<< "\t b.y="<<b.y<<endl; A *ap; ap=&a; fun(ap); ap=&b; fun(ap); ap=&c; fun(ap); void fun( a *p)

第 6 章 继 承 性 : 派 生 类 129 p->display(); 程 序 运 行 结 果 : a.x=1 b.y=2 a.x=10 b.y=20 调 用 类 A 的 函 数 调 用 类 A 的 函 数 调 用 类 A 的 函 数 6.6 应 用 举 例 例 6.17 判 断 两 个 圆 的 位 置 关 系 功 能 描 述 : 我 们 都 知 道 两 个 圆 的 位 置 关 系 有 5 种, 外 离 (away) 外 切 (extang) 相 交 (intersect) 内 切 (intang) 和 内 含 (inner) 分 析 : 我 们 定 义 一 个 基 类 point 类, 由 其 公 共 继 承 派 生 出 circle 类 distance 类 用 来 求 两 个 圆 心 的 距 离 程 序 代 码 : #include <iostream > #include <cmath > Using namespace std; class point // 基 类 :point 类 public : // 公 有 成 员 point ( double xx, double yy ) x=xx; y =yy; // 基 类 构 造 函 数 ~point() // 基 类 析 构 函 数 double getx() return x; double gety() return y; virtual void print(); // 定 义 为 虚 函 数 private : // 私 有 成 员 double x,y ; ; class circle : public point // 派 生 类 circle 对 基 类 point 的 公 有 继 承 public : circle(double x0,double y0, double r): point( x0, y0) //circle 的 构 造 函 数

130 C++ 面 向 对 象 程 序 设 计 radius = r; ~circle() // 派 生 类 析 构 函 数 double getr() return radius; void print(); private : double radius; // 半 径 ; class distance // 类 distance public : distance( circle c1, circle c2); //distance 的 构 造 函 数 ~distance () //distance 的 析 构 函 数 double getdis() return dis; void print(); // 判 断 两 个 圆 的 位 置 关 系, 并 输 出 private : circle cir1,cir2; double dis; ; void point :: print() //point 类 的 print(), 输 出 为 (x,y) cout<< ("<<x <<", "<<y<<")" << endl; void circle :: print() //circle 类 的 print(), 输 出 为 圆 心 和 半 径 cout<<"the center of the circle is :" << endl; point :: print (); cout<<"the radius of it is :" <<getr()<< endl; distance :: distance( circle c1, circle c2 ): cir1(c1), cir2(c2) //distance 类 的 构 造 函 数 的 实 现 cout<<"the distance of the two circle s center is :" << endl; dis = sqrt((cir1.getx()-cir2.getx())*(cir1.getx()-cir2.getx())+ (cir1.gety()-cir2.gety())*(cir1.gety()-cir2.gety())); void distance :: print() // print() 输 出 为 圆 心 距 离 和 位 置 关 系 cout<<dis<< endl; double sumr = cir1.getr()+ cir2.getr(); // 两 个 圆 的 半 径 之 和 double minr = abs(cir1.getr()- cir2.getr()); // 两 个 圆 的 半 径 之 差

第 6 章 继 承 性 : 派 生 类 131 if (dis > sumr) //d>r1+r2, 外 离 cout<<"the relationship of the two circle is AWAY!" << endl; else if (dis == sumr) //d=r1+r2, 外 切 cout<<"the relationship of the two circle is EXTANG!" << endl; else if (dis > minr) // r1 - r2 < d < r1+r2, 相 交 cout<<"the relationship of the two circle is INTERSECT!" << endl; else if (dis == minr) //d= r1-r2, 内 切 cout<<"the relationship of the two circle is INTANG!"<< endl; else if (dis < sumr) //d< r1- r2, 内 含 cout<<"the relationship of the two circle is INNER!"<< endl; void main() // 主 函 数 circle circle1( 2,4,1), circle2( 4,4,1); // 定 义 两 个 圆 对 象 circle1.print(); // 输 出 两 个 圆 的 圆 心 和 半 径 circle2.print(); distance circledistance(circle1, circle2); // 定 义 两 个 圆 的 关 系 circledistance.print(); // 输 出 两 个 圆 的 关 系 程 序 运 行 结 果 : The center of the circle is (1,4)the radius of it is :1 The center of the circle is (4,4)the radius of it is :1 The distance of the two circle s center is :2 The relationship of the two circle is EXTANG! 习 题 1. 什 么 是 继 承 性? 请 举 例 说 明 继 承 性 有 哪 些 好 处? 2. 什 么 是 单 继 承? 什 么 是 多 继 承 方 式? 请 举 例 说 明 3. 受 保 护 protected 成 员 有 哪 些 特 性? 受 保 护 成 员 以 公 有 方 式 或 私 有 方 式 被 继 承 后 的 访 问 限 制 如 何? 4. 在 多 重 继 承 中, 容 易 出 现 什 么 问 题? 5. 虚 基 类 的 作 用 是 什 么? 6. 什 么 是 赋 值 兼 容 规 则? 请 举 例 说 明 7. 下 面 是 一 个 含 有 派 生 类 的 C++ 程 序, 请 编 译 并 运 行 这 个 C++ 程 序 #include <iostream.h> class A protected:

132 C++ 面 向 对 象 程 序 设 计 int m,n; public: void set(int a, int b) m=a; n=b; void show() cout<<m<<" "<<n<<'\n"; ; class B:public A int s; public: void sets() s=m*n; void shows() cout<<s<<endl; ; int main() B obj; obj.set(2,3); obj.show(); obj.sets(); obj.shows(); return 0; 在 这 个 程 序 中, 类 B 的 函 数 能 否 访 问 类 A 中 的 变 量 m 和 n? 为 什 么? 修 改 该 程 序 将 类 A 中 的 m 和 n 定 义 为 私 有 成 员, 重 新 编 译 这 个 程 序, 观 察 编 译 程 序 会 给 出 什 么 编 译 信 息, 为 什 么? 8. 已 知 下 面 的 C++ 程 序 框 架, 请 按 照 注 释 中 的 提 示 补 充 细 节, 并 编 译 运 行 该 程 序 #include <iostream.h> class planet protected: double distance; // miles from the sun int revolve; // in days public: planet(double d,int r) distance=d; revolve=r; ;

第 6 章 继 承 性 : 派 生 类 133 class earth: public planet double circumference; // circumference of orbit public: /* Create earth(double d,int r). Have it pass the distance and days of revolution back to planet. Have it compute the circumference of the orbit. (Hint: circumference=2*r*3.1416) */ // Create a function called show( ) that displays the information. ; int main( ) earth ob(93000000,365); ob.show( ); return 0; 9. 从 下 面 的 基 类 Person 派 生 出 一 个 Student 类, 并 在 main() 函 数 中 测 试 这 个 类 编 译 并 单 步 运 行 你 的 C++ 程 序, 注 意 观 察 其 中 的 基 类 和 派 生 类 的 构 造 函 数 和 析 构 函 数 的 执 行 顺 序 并 请 在 你 的 练 习 本 上 记 录 这 种 执 行 顺 序 #include <iostream> using namespace std; class Person public: Person(const char* s) name = new char[strlen(s)+1]; strcpy(name, s); ~Person() delete [] name; protected: char* name; ; 10. 编 译 并 运 行 下 面 两 个 程 序, 如 果 出 现 错 误, 请 你 尝 试 修 改 该 程 序 直 到 能 够 正 确 运 行 说 明 虚 基 类 有 何 用 处 // virtual base class. #include <iostream> using namespace std; class base public: int i; ;

134 C++ 面 向 对 象 程 序 设 计 class derived1 : public base public: int j; ; class derived2 : public base public: int k; ; /* Here, derived3 inherits both derived1 and derived2.*/ class derived3 : public derived1, public derived2 public: int product() return i * j * k; ; int main() derived3 ob; ob.i = 10; ob.j = 3; ob.k = 5; cout << "Product is " << ob.product() << '\n'; return 0;

第 7 章 运 算 符 重 载 运 算 符 重 载 就 是 为 运 算 符 赋 予 多 重 含 义 C++ 语 言 允 许 程 序 员 为 一 个 特 定 的 类 定 义 某 个 运 算 符 的 新 含 义 例 如, 为 字 符 串 类 重 新 定 义 + 运 算 符, 使 其 还 可 以 表 示 字 符 串 的 连 接 操 作, 从 而 扩 展 了 + 运 算 符 的 功 能 运 算 符 经 过 重 载 以 后, 它 的 原 始 含 义 并 未 丢 失, 但 其 功 能 却 扩 展 增 强 了 例 如 为 字 符 串 类 重 载 + 运 算 符 后, + 仍 然 可 以 表 示 数 字 的 相 加 运 算 为 一 个 类 重 载 运 算 符 后, 就 可 以 在 程 序 中 使 用 运 算 符 来 表 示 该 类 对 象 的 运 算 使 用 运 算 符 来 表 示 对 象 的 运 算 比 使 用 函 数 调 用 方 式 更 加 简 洁 自 然, 因 而 受 到 广 大 程 序 员 的 欢 迎 运 算 符 重 载 与 函 数 重 载 是 紧 密 相 连 的 如 果 要 为 一 个 类 重 载 一 个 运 算 符, 必 须 定 义 当 该 运 算 符 应 用 到 该 类 的 对 象 时 相 应 运 算 的 含 义 我 们 可 以 创 建 一 个 运 算 符 函 数 来 定 义 运 算 符 的 行 为 运 算 符 函 数 可 以 是 类 的 成 员 函 数, 也 可 以 是 类 的 友 元 函 数 在 本 章 中, 我 们 将 分 别 讨 论 这 两 种 运 算 符 重 载 的 方 法 7.1 运 算 符 重 载 的 目 的 C++ 语 言 中 预 定 义 了 丰 富 的 数 据 类 型 和 运 算 符, 可 以 直 接 使 用 常 规 的 运 算 符 ( 如 + * 等 ) 但 是, 这 些 远 不 能 满 足 实 际 应 用 的 需 要 例 如, 对 于 复 数 类 型 及 其 各 种 运 算 就 没 有 定 义 当 然, 我 们 会 想 到 自 定 义 一 个 复 数 类, 其 中 包 括 两 个 double 类 型 的 数 据 成 员, 分 别 为 复 数 的 实 部 和 虚 部 这 样, 复 数 类 的 对 象 就 可 以 存 储 复 数 了, 例 如 : class complex // 复 数 类 声 明 public: complex(double r=0.0,double I=0.0) real = r ; imag =I ; // 构 造 函 数 void display(); // 显 示 复 数 的 值 private: double real; // 实 部 double imag; // 虚 部 ; 于 是, 我 们 可 以 声 明 复 数 类 的 对 象 : complex a(10,20),b(5,7); 但 是, 复 数 的 运 算 如 何 实 现 呢? 能 够 写 a + b 这 样 的 表 达 式 吗? 当 然 是 不 能 的, 因 为 在 C++ 中 预 定 义 的 运 算 符 只 能 作 用 于 基 本 的 数 据 类 型, 不 允 许 直 接 将 这 些 运 算 符 作 用

136 C++ 面 向 对 象 程 序 设 计 于 用 户 自 定 义 类 型 例 7.1 计 算 需 付 人 民 币 数 量 ( 分 别 用 了 成 员 函 数 和 运 算 符 成 员 函 数 两 种 方 法 ) #include <iostream > Using namespace std; class RMB // 人 民 币 类 public : RMB(double d) yuan = d; jf =(d yuan)/ 100; RMB interest(double rate); // 计 算 利 息 RMB add(rmb d); // 人 民 币 加 Void display() cout <<(yuan + jf /100.0)<< endl; RMB operator +(RMB d) return RMB(yuan + d.yuan +(jf + d.jf) /100); // 人 民 币 加 的 运 算 符 重 载 RMB operator *(double rate) return RMB((yuan +jf /100)* rate); Private : Unsigned int yuan; // 元 Unsigned int jf; // 角 分 ; RMB RMB::interest(double rate) return RMB((yuan +jf /100.0)* rate); RMB RMB::add(RMB d) return RMB(yuan + d.yuan + jf /100.0 + d.jf/100.0); // 以 下 是 计 算 需 付 人 民 币 的 两 个 版 本 expense() RMB expense1(rmb principle, double rate) // 成 员 函 数 法 RMB interest = principle.interest(rate); // 本 金 乘 利 息 Return principle.add(interest); // 连 本 带 利 RMB expense2(rmb principle, double rate) // 运 算 符 成 员 函 数 法 RMB interest = principle*rate; // 本 金 乘 利 息 Return principle + interest; // 连 本 带 利

第 7 章 运 算 符 重 载 137 void main() RMB x = 10000.0 ; // 本 金 Double yrate = 0.035; // 利 息 Expense1(x, yrate).display(); Expense2(x, yrate).display(); 运 行 结 果 为 : 10350 10350 expense() 的 两 个 版 本 都 可 以 计 算 需 付 人 民 币, 运 行 的 结 果 也 相 同 但 expense2() 可 读 性 更 好, 符 合 我 们 用 运 算 符 +,* 计 算 的 习 惯 如 果 不 定 义 运 算 符 重 载, 在 expense2() 中 principle*rate 和 principle + interest 则 非 法, 因 为 参 加 运 算 的 是 类 对 象 而 不 是 浮 点 数 7.2 运 算 符 重 载 语 法 运 算 符 重 载 使 得 用 常 规 运 算 符 来 表 示 自 定 义 类 型 的 数 据 的 运 算 变 为 可 能 运 算 符 重 载 是 对 已 有 的 运 算 符 赋 予 多 重 含 义, 使 同 一 个 运 算 符 作 用 于 不 同 类 型 的 数 据 导 致 不 同 类 型 的 行 为, 即 对 运 算 符 进 行 重 新 定 义, 赋 予 已 有 符 号 以 新 的 功 能 的 要 求 使 标 准 运 算 符 作 用 于 类 的 对 象, 从 而 使 对 象 的 计 算 操 作 表 示 得 既 自 然 又 符 合 常 规 C++ 将 运 算 符 看 作 一 种 特 殊 类 型 的 函 数, 运 算 符 的 重 载 是 通 过 对 运 算 符 函 数 的 重 载 实 现 的 运 算 符 函 数 名 由 关 键 字 operator 和 重 载 的 运 算 符 组 成 下 面 我 们 介 绍 运 算 符 重 载 的 一 般 语 法 形 式 类 型 类 名 ::operator 重 载 的 运 算 符 ( 参 数 列 表 ) 操 作 ; // 运 算 符 处 理 程 序 代 码 从 这 个 定 义, 我 们 知 道 定 义 一 个 重 载 的 运 算 符 和 定 义 一 个 函 数 类 似, 函 数 的 名 字 必 须 以 operator 开 头 所 以, 运 算 符 重 载 的 实 质 就 是 函 数 重 载, 每 个 运 算 符 对 应 各 自 的 运 算 符 函 数, 根 据 操 作 数 的 不 同 调 用 不 同 的 同 名 函 数 其 参 数 个 数 是 由 以 下 两 个 因 素 决 定 : 1 该 操 作 符 是 一 元 操 作 符 还 是 二 元 操 作 符 2 当 运 算 符 重 载 为 类 的 成 员 函 数 时, 函 数 的 参 数 个 数 比 原 有 操 作 数 个 数 要 少 一 个 ( 后 置 ++ -- 除 外 ), 也 就 是 说, 一 元 操 作 符 的 参 数 个 数 为 0, 二 元 操 作 符 的 参 数 个 数 为 1; 而 运 算 符 重 载 为 类 的 友 元 函 数 时, 函 数 的 参 数 个 数 与 原 有 操 作 数 个 数 相 同 ( 后 置 ++ -- 除 外 ), 也 就 是 说, 一 元 操 作 符 的 参 数 个 数 为 1, 二 元 操 作 符 的 参 数 个 数 为 2 这 是 因 为, 当 重 载 为 类 的 成 员 函 数 时, 如 果 某 个 对 象 使 用 重 载 了 的 成 员 函 数, 自 身 的 数 据 可 以 直 接 访 问, 就 不 用 再 放 在 函 数 表 中 进 行 传 递, 这 样 该 类 本 身 也 作 为 一 个 操

138 C++ 面 向 对 象 程 序 设 计 作 符 参 与 了 计 算, 那 么, 少 了 的 操 作 数 就 是 该 对 象 本 身 而 重 载 为 类 的 友 元 函 数 时, 友 元 函 数 对 某 个 对 象 的 数 据 进 行 操 作, 就 必 须 通 过 该 对 象 的 名 字 来 进 行, 因 此 参 数 的 使 用 必 须 进 行 传 递, 操 作 数 的 个 数 也 就 不 会 有 所 减 少 变 化 在 表 7.1 中, 运 算 符 重 载 具 有 如 下 规 则 : 1 在 C++ 中, 除 了 5 个 运 算 符 不 能 重 载 之 外, 其 余 全 部 都 可 以 重 载 2 运 算 符 重 载 可 以 改 变 运 算 符 原 来 的 行 为, 但 是 重 载 之 后 运 算 符 的 优 先 性 结 合 性 和 操 作 数 个 数 都 不 会 改 变, 只 能 重 载 已 有 的 这 些 运 算 符 3 运 算 符 重 载 后 的 功 能 与 原 有 的 功 能 相 似, 运 算 符 重 载 函 数 的 参 数 至 少 有 一 个 必 须 是 自 定 义 类 型 4 在 重 载 运 算 符 () [ ] -> 或 者 = 时, 运 算 符 重 载 函 数 必 须 声 明 一 个 为 类 的 一 个 成 员, 对 于 其 他 运 算 符, 运 算 符 重 载 函 数 可 以 是 成 员 函 数 或 者 是 友 元 函 数 表 7.1 可 重 载 和 不 可 重 载 的 运 算 符 运 算 符 + * / % ^ & ~! = < > += = 可 重 载 *= /= %= ^= &= = << >> >>= <<= ==!= <= >= && ++ [ ] (), -> new delete 不 可 重 载. *( 指 针 ) ::? : sizeof 7.3 成 员 运 算 符 函 数 首 先, 我 们 给 出 运 算 符 重 载 为 类 的 成 员 函 数 的 一 般 语 法 形 式 : 函 数 类 型 operator 重 载 的 运 算 符 ( 形 参 列 表 ) 函 数 体 ; 运 算 符 重 载 为 类 的 成 员 函 数 时, 就 可 以 总 是 通 过 该 类 的 对 象 来 访 问 重 载 的 运 算 符, 分 以 下 两 种 情 况 : 1 当 运 算 符 为 双 目 运 算 符 时, 是 左 边 对 象 对 operator 函 数 的 调 用 即 产 生 一 个 操 作 数 是 对 象 本 身 的 数 据, 由 this 指 针 给 出, 另 一 个 操 作 数 则 需 要 通 过 运 算 符 重 载 函 数 的 参 数 表 来 传 递 我 们 以 + 为 例, 如 果 要 重 载 + 为 类 的 成 员 函 数, 使 之 实 现 表 达 式 a+b, 其 中 a 是 类 A 的 对 象, 则 应 当 把 + 重 载 为 类 A 的 成 员 函 数, 该 函 数 只 有 一 个 形 参,

第 7 章 运 算 符 重 载 139 形 参 的 类 型 是 b 所 属 的 类 型 经 过 重 载 后, 表 达 式 a+b 就 相 当 于 函 数 调 用 a.operator+(b) 2 当 运 算 符 为 单 目 运 算 符 时, 操 作 数 由 对 象 的 this 指 针 给 出, 就 不 需 要 任 何 参 数 来 传 递 我 们 以 前 置 单 目 运 算 符 -- 为 例, 如 果 要 重 载 -- 为 类 的 成 员 函 数, 使 之 实 现 表 达 式 --a, 其 中 a 是 类 A 的 对 象, 则 应 当 把 -- 重 载 为 类 A 的 成 员 函 数, 该 函 数 没 有 形 参 经 过 重 载 后, 表 达 式 --a 就 相 当 于 函 数 调 用 a.operator--() 对 于 后 置 单 目 运 算 符 ++ 为 例, 如 果 要 重 载 ++ 为 类 的 成 员 函 数, 使 之 实 现 表 达 式 a++, 其 中 a 是 类 A 的 对 象, 则 应 当 把 ++ 重 载 为 类 A 的 成 员 函 数, 该 函 数 只 有 一 个 带 有 整 型 的 形 参 经 过 重 载 后, 表 达 式 a++ 就 相 当 于 函 数 调 用 a.operator++(0) 如 表 7.2 所 示, 成 员 运 算 符 函 数 operator @ 实 际 上 是 由 双 目 运 算 符 左 边 的 对 象 a 调 用 的, 尽 管 双 目 运 算 符 函 数 的 参 数 表 只 有 一 个 操 作 数 b, 但 其 实 左 边 的 操 作 数 是 由 对 象 a 通 过 this 指 针 隐 含 的 传 递 表 7.2 C++ 中 表 达 式 与 其 等 价 的 函 数 调 用 表 示 表 达 式 a+b 成 员 函 数 表 示 a.operator+(b) - - a a.operator- -() a++ a.operator++(0) 成 员 函 数 重 载 双 目 运 算 符 @ 后, 可 以 用 以 下 两 种 方 法 来 使 用 A @ b // 隐 式 调 用 a.operator @ b // 显 示 调 用 例 7.2 复 数 类 加 减 法 运 算 重 载 - 成 员 函 数 形 式 #include <iostream > Using namespace std; class complex // 基 类 complex 的 定 义 public : // 公 有 成 员 complex(double r=0.0,double i=0.0); // 构 造 函 数 complex operator +(const complex &c);// 二 元 加 重 载 成 员 函 数 complex operator -(const complex &c);// 二 元 减 重 载 成 员 函 数 complex operator -(); // 一 元 减 重 载 成 员 函 数, 即 取 反 void print(); // 输 出 复 数 private: // 私 有 成 员 double real; // 复 数 实 部 double imag; // 复 数 虚 部 ; complex:: complex(double r, double i) real = r; imag =i;

140 C++ 面 向 对 象 程 序 设 计 complex complex :: operator +(const complex &c)// 二 元 加 重 载 函 数 实 现 double r= real + c.real; double i= imag + c.imag; return complex(r,i); complex complex :: operator -(const complex &c)// 二 元 减 重 载 函 数 实 现 double r= real - c.real; double i= imag - c.imag; return complex(r,i); complex complex :: operator -() // 一 元 减 重 载 函 数 实 现 return complex(-real, -imag); void complex:: print() // 输 出 复 数 函 数 实 现 cout<<"("<<real<<","<<imag<<")"<<endl; void main() // 主 函 数 complex c1(5.2,14.3),c2(2.4,9.7); // 定 义 复 数 类 的 对 象 c1,c2 complex c; c=c1-c2; // c=c1.operator-(c2); c.print(); c=c1+c2; // c=c1.operator+(c2); c.print(); c=-c1; // c=c1.operator-(); c.print(); return 0; 程 序 运 行 结 果 : (2.7, 4.5) (7.6, 24.1) (-5.2, -14.3) 如 上 所 述, 我 们 总 结 出 成 员 运 算 符 函 数 具 有 如 下 特 点 : 1 重 载 为 类 成 员 函 数 时, 参 数 个 数 = 原 操 作 数 个 数 1 ( 后 置 ++ 除 外 ) 2 当 类 的 对 象 调 用 这 种 运 算 符 函 数 时, 对 象 中 的 成 员 数 据 就 可 以 是 一 个 操 作 数, 另 一 个 操 作 数 通 过 参 数 传 递 来 获 得 如 :

第 7 章 运 算 符 重 载 141 class complex public: complex operator +(complex c2); 7.4 友 元 运 算 符 函 数 首 先, 我 们 给 出 运 算 符 重 载 为 类 的 友 元 函 数 的 一 般 语 法 形 式 : friend 函 数 类 型 operator 重 载 的 运 算 符 ( 形 参 列 表 ) 函 数 体 ; 运 算 符 重 载 为 类 的 友 元 函 数 时, 运 算 符 所 需 要 的 操 作 数 必 须 通 过 函 数 的 形 参 来 传 递, 在 参 数 表 中 形 参 从 左 到 右 的 顺 序 就 是 运 算 符 操 作 数 的 顺 序 使 用 friend 函 数 重 载 运 算 符, 因 为 friend 不 是 类 的 成 员,friend 函 数 没 有 this 指 针, 故, 重 载 friend operator 函 数 需 要 显 式 地 传 递 操 作 数, 其 也 分 以 下 两 种 情 况 : 1 当 运 算 符 为 双 目 运 算 符 时 我 们 以 + 为 例, 如 果 要 重 载 + 为 类 的 友 元 函 数, 使 之 实 现 表 达 式 a+b, 其 中 a 是 类 A 的 对 象, 则 应 当 把 + 重 载 为 类 A 的 友 元 函 数, 该 函 数 有 两 个 形 参, 其 中 a 的 类 型 是 A 类,b 的 类 型 是 它 所 属 的 类 型 经 过 重 载 后, 表 达 式 a+b 就 相 当 于 函 数 调 用 operator+(a,b) 2 当 运 算 符 为 单 目 运 算 符 时 我 们 以 前 置 单 目 运 算 符 为 例, 如 果 要 重 载 为 类 的 友 元 函 数, 使 之 实 现 表 达 式 a, 其 中 a 是 类 A 的 对 象, 则 应 当 把 重 载 为 类 A 的 友 元 函 数, 该 函 数 的 形 参 为 A 类 的 对 象 a 经 过 重 载 后, 表 达 式 a 就 相 当 于 函 数 调 用 operator (a) 对 于 后 置 单 目 运 算 符 ++ 为 例, 如 果 要 重 载 ++ 为 类 的 友 元 函 数, 使 之 实 现 表 达 式 a++, 其 中 a 是 类 A 的 对 象, 则 应 当 把 ++ 重 载 为 类 A 的 友 元 函 数, 该 函 数 有 两 个 形 参, 一 个 是 A 类 对 象 a; 另 一 个 是 带 有 整 型 的 形 参 经 过 重 载 后, 表 达 式 a++ 就 相 当 于 函 数 调 用 operator++(a,0) 表 7.3 C++ 中 表 达 式 与 其 等 价 的 函 数 调 用 表 示 表 达 式 a+b a a++ 友 元 函 数 表 示 operator+(a,b) operator (a) operator++(a,0) 例 7.3 复 数 类 加 减 法 运 算 重 载 - 友 元 函 数 形 式 #include <iostream > Using namespace std; class complex // 基 类 complex 的 定 义

142 C++ 面 向 对 象 程 序 设 计 public : // 公 有 成 员 complex(double r=0.0,double i=0.0); // 构 造 函 数 friend complex operator +(const complex &c1, const complex &c2); // 二 元 加 重 载 成 员 函 数 friend complex operator -(const complex &c1, const complex &c2); // 二 元 减 重 载 成 员 函 数 friend complex operator -(const complex &c); // 一 元 减 重 载 成 员 函 数 void print() const; // 输 出 复 数 private: // 私 有 成 员 double real; // 复 数 实 部 double imag; // 复 数 虚 部 ; complex:: complex(double r, double i) real = r; imag =i; complex operator +(const complex &c1, const complex &c2) // 二 元 加 重 载 函 数 实 现 double r= c1.real + c2.real; double i= c1.imag + c2.imag; return complex(r,i); complex operator -(const complex &c1, const complex &c2) // 二 元 减 重 载 函 数 实 现 double r= c1.real c2.real; double i= c1.imag c2.imag; return complex(r,i); complex operator -(const complex &c) // 一 元 减 重 载 函 数 实 现 return complex(-c.real, -c.imag); void complex:: print() const; // 输 出 复 数 函 数 实 现 cout<<"("<<real<<","<<imag<<")"<<endl; void main() // 主 函 数 complex c1(5.2,14.3),c2(2.4,9.7); // 定 义 复 数 类 的 对 象 c1,c2

第 7 章 运 算 符 重 载 143 complex c; c=c1-c2; // c=c1.operator-(c2); c.print(); c=c1+c2; // c=c1.operator+(c2); c.print(); c=-c1; // c=c1.operator-(); c.print(); return 0; 程 序 运 行 结 果 : (2.7, 4.5) (7.6, 24.1) (-5.2, -14.3) 我 们 将 例 7.2 和 例 7.3 相 比 就 会 发 现, 两 个 程 序 运 行 结 果 完 全 相 同, 主 函 数 main() 没 有 任 何 改 变, 而 变 化 只 是 复 数 类 complex 的 成 员 我 们 总 结 友 元 运 算 符 函 数 具 有 如 下 特 点 : 1 重 载 为 友 元 函 数 时, 参 数 个 数 = 原 操 作 数 个 数, 且 至 少 应 该 有 一 个 自 定 义 类 型 的 形 参 2 友 元 函 数 是 类 以 外 的 函 数, 调 用 这 种 运 算 符 函 数 时, 所 有 的 操 作 数 都 要 通 过 参 数 传 递 来 获 得 如 : class complex public: friend complex operator +(complex c1, complex c2); 3 friend operator 函 数 不 能 重 载 =,(),-> 运 算 符 4 在 重 载 增 值 和 减 值 运 算 符 时, 用 friend operator 函 数 需 要 使 用 引 用 参 数 7.5 成 员 运 算 符 函 数 与 友 元 运 算 符 函 数 比 较 从 7.3 节 和 7.4 节, 可 以 对 成 员 运 算 符 函 数 和 友 元 运 算 符 函 数 作 一 比 较, 得 出 如 下 结 论 : 1 对 双 目 运 算 符 而 言, 成 员 运 算 符 函 数 带 有 一 个 参 数, 而 友 元 运 算 符 函 数 带 有 两 个 参 数 ; 对 单 目 运 算 符 而 言, 成 员 运 算 符 函 数 不 带 参 数, 而 友 元 运 算 符 函 数 带 有 一 个 参 数 2 双 目 运 算 符 一 般 可 以 被 重 载 为 成 员 运 算 符 函 数 和 友 元 运 算 符 函 数 但, 有 一 种 例 外 情 况, 必 须 使 用 友 元 运 算 符 函 数 例 如 : 表 达 式 3.4+x, + 运 算 符 重 载 为 complex 类 的 友 元 函 数 时, 这 个 表 达 式 相 当 于 operator +(complex(3.4),x); 但 若 将 + 运 算 符 重 载 为 complex 类 的 成 员 函 数 时, 这 个 表 达 式 相 当 于 3.4. operator +(x) 由 于 3.4 是 一 个 double 类 型 的 操 作 数, 不 能 进 行. 操 作, 所 以 此 时 表 达 式 3.4 + x 就 是 错 误 的

144 C++ 面 向 对 象 程 序 设 计 又 例 如 : complex complex::operator +(int x) class temp; temp.a= a+ x; temp.b= b+ x; return temp; 若 类 class 的 对 象 ob 做 赋 值 运 算 和 加 法 运 算, 执 行 下 一 条 语 句 : ob = 100+ ob; 则 上 式 也 是 不 能 工 作 的, 因 为 运 算 符 的 左 操 作 数 是 一 个 整 数, 而 整 数 是 一 个 内 部 数 据 类 型,100 不 能 产 生 对 成 员 运 算 符 函 数 的 调 用 在 以 上 两 例 中, 只 有 当 用 友 元 函 数 来 重 载 运 算 符 函 数 时, 两 个 参 数 才 能 显 式 地 传 递 给 运 算 符 函 数, 才 能 解 决 了 运 算 符 + 左 操 作 数 的 内 部 数 据 类 型 所 带 来 的 问 题 3 若 运 算 符 所 需 的 操 作 数 ( 尤 其 是 第 一 个 操 作 数 ) 需 要 是 隐 式 类 型 的 转 换, 那 么 运 算 符 重 载 必 须 用 友 元 函 数, 而 不 能 用 成 员 函 数 4 一 般 而 言, 对 于 双 目 运 算 符, 将 其 重 载 为 一 个 友 元 运 算 符 函 数 比 重 载 为 一 个 成 员 运 算 符 函 数 更 便 于 使 用 而 如 果 一 个 运 算 符 的 操 作 需 要 修 改 类 的 对 象 的 状 态, 那 么 选 择 重 载 为 成 员 运 算 符 比 较 好 5 一 般 情 况 下, 运 算 符 函 数 的 调 用 形 式, 如 表 7.4 所 示 表 7.4 运 算 符 函 数 的 调 用 形 式 表 达 式 友 元 函 数 表 示 成 员 函 数 表 示 a+b operator+(a,b) a.operator+(b) - - a operator- -(a) a.operator- -() a++ operator++(a,0) a.operator++(0) 例 如 : 成 员 函 数 友 元 函 数 class complex double re,im; public: complex(double r, double I) re=r; im=i; complex() re=im=0; complex operator+(complex); complex operator++(); friend complex operator++(complex); friend complex operator+(complex,complex); 单 目 运 算 符 ;

第 7 章 运 算 符 重 载 145 7.6 ++ 和 -- 的 重 载 运 算 符 ++ 和 -- 可 以 是 前 置 运 算 符, 也 可 以 是 后 置 运 算 符 对 于 前 置 方 式, 如 ++a 若 重 载 为 成 员 函 数, 则 为 a.operator++(); 若 重 载 为 友 元 函 数, 则 为 operator++(a) 对 于 后 置 方 式, 则 将 其 看 作 二 元 操 作 符, 增 加 一 个 int 型 参 数 如 a-- 若 重 载 为 成 员 函 数, 则 为 a.operator--(int); 若 重 载 为 友 元 函 数, 则 为 operator--(a,int) 调 用 时, 参 数 int 并 不 参 与 运 算, 只 是 用 来 区 分 重 载 函 数, 也 就 是 说, 函 数 内 部 不 需 要 访 问 这 个 参 数, 因 此 没 有 必 要 为 其 指 定 名 字, 一 般 传 递 值 为 0 例 7.4 ++ 与 -- 运 算 符 重 载 #include <iostream > Using namespace std; class test // 基 类 test 的 定 义 public : // 公 有 成 员 test(int, int); // 构 造 函 数 test operator + +(); // 前 置 运 算 符 test operator - -(); // 前 置 运 算 符 test operator + +(int); / 后 置 运 算 符 test operator - -(int); // 后 置 运 算 符 private: // 私 有 成 员 int t1,t2; ; test:: test(int a, int b) t1 = a; t2 =b; test test :: operator + +() cout<<" + + test \n"<<endl; + + t1; + + t2; return *this; test test :: operator + +(int) cout<<" test + + \n"<<endl; int tmp1=t1; int tmp2=t2; t1++;

146 C++ 面 向 对 象 程 序 设 计 t2++; return test(tmp1,tmp2); test test :: operator - -() cout<<" - - test \n"<<endl; - - t1; - - t2; return *this; test test :: operator - -(int) cout<<" test - - \n"<<endl; int tmp1=t1; int tmp2=t2; t1- -; t2- -; return test(tmp1,tmp2); void main() // 主 函 数 test b(3,4); // 定 义 test 类 的 对 象 b + + b; // t1=4; t2=5; b + +; // t1=4; t2=5; - - b; // t1=3; t2=4; b - -; // t1=3; t2=4; return 0; 例 7.5 时 钟 类 有 如 下 要 求 : 1 运 算 符 前 置 ++ 和 后 置 ++ 重 载 为 时 钟 类 的 成 员 函 数 2 操 作 数 是 时 钟 类 的 对 象 3 前 置 单 目 运 算 符, 重 载 函 数 没 有 形 参 4 对 于 后 置 单 目 运 算 符, 重 载 函 数 需 要 有 一 个 整 型 形 参 5 实 现 时 间 增 加 1 秒 钟 // 源 程 序 代 码 #include<iostream > Using namespace std; class Clock // 时 钟 类 声 明