logo资料库

effect C++中文版.pdf

第1页 / 共259页
第2页 / 共259页
第3页 / 共259页
第4页 / 共259页
第5页 / 共259页
第6页 / 共259页
第7页 / 共259页
第8页 / 共259页
资料共259页,剩余部分请下载后查看
第 1 页 导读 学 会一 种程 序 设计 语言 , 是一 回事 儿 ;学 会如 何以 此 语言 设计 并 实现 有效 的 程序,又是一回事儿。 尤其如 此,因为 它很不寻常 地涵盖了 罕见的威 力和丰 富的表现力 ,不但建立在一个全功能的传统语言 ( )之上,更提供极为广泛的面 向 对 象( )性 质 ,以 及 对 和 ( 异 常 状 态 )的 支 持 假以适当运用, 是个可以让你感受愉悦的伙伴。各种不同的设计方式, 包括面向对象形式和传统形式,都可以直接在这个语言中表现并有效地实现出来 你可以定义新的数据型别 ,它们和语言内建的型别表面上无分轩轾,实质上则更 具 弹 性   明 智 地 选 用 一 些 谨 慎 设 计 的 自 动 完 成 内 存 管 理 、别 名 )处理 、初始化 动作与清 理动作 、型 别转换 ,以及软件 开发的其 它难题 与 祸根 可以 使程 序设 计更 容易 、更 直观 、更有 效 、更少 错误 。是 的 ,要写 出有效的 程序并不会太困难,如果你知道怎么做的话 如果没有什么训练与素养,就贸然使用 ,会导致写出来的代码不易理解, 不易维护,不易扩充,缺乏效率,而且容易出错。 关键在于找出 可能绊倒你的障碍有哪些,然后学习如何避开它们。这正 是本书的目的。我假设你已经认识 并对它有某种程度的使用经验。我提供一 些准则 ,让你更有效地使用这种语言,使你的软件容易理解 ,容易维护,容易扩 充,效率高,而且行为如所预期。 我提出的忠告分为两大类 :一般性的设计策略 ,以及特殊的(比较难得一见 的)语言特性。
第 2 页 设计方面的讨论集中在如何在不同的方法(从而得以用 达成某个目标) 之 间 取 舍 如 何 在 ( 继 承 )和 ( 模 板 )之 间 选 择 ?如 何 在 和 在 在 (泛型指针) 之间,在 (公开继承)和 ( 私有继承)之间,在 和 (分层技术)之间, (函数重载)和 (参数 默认值)之间, (虚拟函数)和 (非 虚 拟 函 数 ) 之 间 ,在 ( 传 值 )和 (传址)之间,进行选择?一开始就做 出 正确 的决 定是 很重 要的 ,因 为不 正确 的选 择或 许不 会一 下子 就产 生影 响 ,但是 在 开发 过程 的后 期 ,矫正 它往 往很 困难 ,很 花时 间 ,很混 乱 ,很令 人沮 丧 ,事倍 功半 ,成本很高。 即 使 在 你 确 切 知 道 你 要 做 什 么 之 后 ,把 它 做 对 恐 怕 也 不 是 件 太 容 易 的 事 。 什么是 运算符的适当传回型别?当 无法找出足够的 内存时,它该有怎样的行为? 何时应 该被声明为 你应该写一 个 (成员初始化列)吗?在如斯细节中努力,也颇具有决 定 性 ,因为 如果 不这 样 ,常会 导致 意料 之外 或神 秘难 解的 程序 行为 。更 糟的 是 , 这 类脱 轨行 为可 能不 会立 即浮 现 ,这些 恐怖 的代 码或 许能 通过 品质 检验 ,却 仍然 藏匿着许多未侦测出来的臭虫 不定时炸弹正等待引爆。 这 不 是 一 本 必 须 一 页 页 读 下 去 才 有 感 觉 的 书 籍 你 甚 至 不 需 要 按 顺 序 读 它 所有素材 被我分为 个 条款 ,每 一个 都相 当独 立 。不 过条 款之 间会 彼此 联系 , 所 以阅 读本 书的 一种 方法 是先 从感 兴趣 的条 款开 始 ,然后 遵循 其参 考指 示 ,进一 步读下去。 所 有条 款 被我 分为 七 大类 如 果你 对某 类 主题 特 别感 兴趣 ,例如 “内存 管理 ” 或 “面 向对 象设 计” ,可 以从 相关 章节 开始 ,一 路读 下去 ,或 是跳 跃前 进 。不过 最后你会发现,本书的所有内容对于高效的 程序设计而言 ,都十分基础而重 要,所以几乎每个条款最后都会和其它条款互有牵连 这并 不是 一本 参考工具书,也不是一本让你从头学习 的书。例如, 虽然我热切告 诉你一些有关“撰写自己的 ”的注 意 事 项( 条 款 ,
第 3 页 但是我假设你可以从其它地方获知, 必 须传回一个 ,其第一 引数( 的型别必须是 许多 语言书籍可以带给你这样的信息 这本书的目的是要强调那些其它 书籍往往浅浅带过(如果有的话)的 程 序设计概念。其它书籍描述的是 语言的各个成分,本书则告诉你如何将那些 成 分 组合 起 来 ,完 成 一 个有 效 的 程序 其 它 书籍 告 诉 你如 何 让 程序 顺 利 编译 ,本 书则告诉你如何避开编译器不会告诉你的一些问题。 和大部分语言一样, 有着丰富的“传统”,在程序员之间口耳相传,形 成这个语言的伟大传承的一部分 。我企图在这本书中以容易阅读的形式记录一些 长久累积而来的智慧 然而在此同时,我必须告诉你,本书仅限于正统的、可移植的 语 言 只 有 明列于 标准 (见 条款 )中的 性质 ,才 会被本书 采用本书 之中 , 可 移植 性是 个关 键考 虑 因素 。如 果你 想 要寻 找因 编译 器而 异 的特 殊技 法 ,本书 不 适合你 但是,啊呀,标准规格所描述的 与社区软件商店所卖的编译器 的表现, 多 少 有 点 出 入 所 以 当 我 指 出 某 个 新 的 语 言 特 性 颇 有 用 处 时 , 我 也 会 告 诉 你 如 何 在 缺 乏 那 些 特 性 的 情 况 下 做 出 有 效 的 软 件 毕 竟 在 确 知 未 来 即 将 如 何 如 何 之 际 , 却 忽略 那些 美丽 远景 而 尽做 些低 下的 劳 力工 作 ,容我 坦言 是 相当 愚蠢 的 ;但是 反 过来 看,你也 不能在 最新最伟 大的 编 译器 降临 世界之 前 ,空 自等待 而束 手无策呀你必须和你 手上可用的工具一起打拼 ,而本书正 打算帮助你这么做 。 注意 我说编译器 复 数 不 同 的 编 译 器 对 标 准 的满足程度各不 相 同 ,所以我鼓励你至少以两种编译器 来 开 发 程 序 这 么 做 可 以 帮 助 你 避 免 不 经 意 仰赖 某个 编译 器专 属 的语 言延 伸性 质 ,或 是误 用某 个编 译 器对 标准 规格 的错 误 阐 示 。这也 可以 帮助 你 避免 使用 过度 先 进的 编译 器特 殊技 术 ,例 如独 家厂 商才 做 得 出 来 的 某 种 语 言 新 特 性 如 此 特 性 往 往 实 现 不 够 精 良 ( “ 臭 虫 ” 多 , 要 不 就 是 表现迟缓,或两者兼具),而且 社群往往对这些特性缺 乏使用经验 ,无法给 你 应 用 上 的 忠 告 雷 霆 万 钧 之 势 固 然 令 人 兴 奋 , 但 当 你 的 目 标 是 要 产 出 可 靠 的 代 码时,恐怕还是要步步为营(并且能够与人合作)得好。
第 4 页 你在本书中找不到 的必杀秘技 ,也看不到通往 完美软件的唯一真 理 项条款中的每一个带给你的都只是准则,包括如何完成较好的设计,如何 避免常见的问题,如何得到更高的效率,但任何条款都不可能放之四海而皆准。软 件的定义和实现是极为复杂的工作,常会受到硬件、操作系统以及应用软件的束缚, 所以我能够做的最好事情就是提供一些准则 让你可以依此产生出比较好的程序。 如果 任何时 候你都 奉行每 一个条 款, 应该不 太可能 掉进最 常见的 一些 陷 阱 不过 准 则毕 竟 只 是准 则 ,可 能 存在 例 外 情况 。 那正 是 为什 么 每 个条 款 都带 有 堆 解释 的原 因 。这 些解 释是 本 书最 重要 的 资产 唯有 彻 底了 解一 个条 款 背后 的基 本 原理,你才能合理决定此条款是否适用于手上的项目,或你正苦苦探索的难题上。 本书的最佳用途,就是增进你对 行为的了解,知道它为什么有那样的表 现 , 以 及 如 何 将 其 行 为 转 化 为 你 的 利 益 盲 目 运 用 本 书 所 列 的 条 款 并 不 适 当 , 不 过话说回来 ,你或许不应该在缺乏充足理由的情况下 ,任意违反任何一项条款。 这 样 性 质 的 书籍 中 ,专 用 术 语 的 解 释并 非 重 点 所 在 那 样 的 工作 顶 好 是 留 给 语言界的“律师”去做。然而有少量 词汇是每个人都应该要懂的。以下术语 一再出现,所以有必要确定你我之间对它们有共同的认知。 所 谓 声 明( ,是用来 将一个 或 的型别名称告诉编译器。声明式并不带有细节信息。下面统统都是声明: 所谓定义 ( ,是 用 来 将 细 节 信 息 提 供 给 编 译 器 对 而 言 , 其定义式是编译器为它配置内存的地点 。对 其定义式提供函数本体( 。对 义式 必须列出该 或 的所有 或 或 : 而 言 , 而 言 , 其 定
第 5 页 / 这是对象的定义式 / 这是函数的定义式 此函数传回其参数的阿拉伯数字个数 这是 的定义式 这是 的定义式 上述程序代码把我们带往 所谓的 以“无需任何引数( 就 被调用 者 。这样的一个 意指可 如果不是 没有任何参数 ,就是每个参数都有默认值。通常当你需要定义对象数组时 ,就会 需要一个 调用 次
第 6 页 // ; / / 调 用 //每次都给引数 次 //这不是一个 错 误! 或许有时候你会发现,某个 的 有默认参数值,你的 编译器却拒不接受其对象数组 。例如某些编译器拒绝接受上述 的定义, 即使它 其实 符合 标准 这是存在 于 标准规格书和实际编译器行为之间 的一个矛盾例子 截至目前我所知道的每一个编译器 ,都有一些这类不兼容的缺 点 在编译器厂商追上 语言标准之前 ,请保持你的弹性,并安 慰自己,也许 不久后的 某一天, 编译器的表现就可以和 标准规格书所描述的一致了。 附带一提,如果你想要产生一个对象数组,但该对象型别没有提供 ,通常 的 做法 是 定 义一 个 指针 数 组取 而 代 之 ,然 后利 用 一一将每 个指 针初 始化: //没有调用任何 // 置并 建构 一个 对象 //同上 这个做法在任何场合几乎都够用了。 如果 不够 ,你 或许 得使用 条款 所 说 的更高层次(也因此更不为人知)的 ”方法。 回到术语上来,所谓 系以某对象作为另一同型对象的初值: : ( ) : ) //调用 //调用
第 7 页 调用 或许 最重要的用途就是用来定义何谓“以 方式传递和 传 回对象”。例如,考虑以下效率不佳的做法,以一个函数联结两个 对 象: 其中 需 要 两 个 对 象作为参数,并传回一个 对象作 为 运 算 结 果 不 论 参 数 或 运 算 结 果 都 是 以 方式传递,所以在 进行过程中,会有一个 被调用,用以将 当作 的 初值,再 有 一 个 被 调 用   用 以 将 当作 的 初 值 , 再 有 一 个 被 唤起 ,用 以将 当作 的 初 值 事 实 上 , 只 要 编 译 器 决 定 产 生 中介 的暂时性对象 ,就会需要一些 调 用 动 作 ( 见 条 款 重点是: 便 是 “调 用 ” 的 同 义 词 顺 带 一提 , 你 不 能 够真 的 像 上 述 那样 实 现 的 传回 个 是正确的(见条款 和 ,但是你应该 以 方式(见条款 )传递那两个参数 其实,如果你有外 援,则并 不需要为 撰 写 的确有外援 ,因为 标准程序 库(条款 就内含有一个 事实上你 型别 ,带 有 一个 ,做 的事情几乎就是上述 的 行 为 本 书 中 我 并 用 和 两者 (注意前者名称以大写开头 ,后者否),但方式不同 如果我只是需要一般字符串 ,不在意它是怎么做出来的 ,那么我便使用标准程序 库提供的 这也是你应该选择的行为 然而如果我打 算剖析 的 行为,
并因 而需 要某些 实现码 来示 范或验 证 ,我便使 用非标 准的 那个 为一个程序员 ,只要必须用到字符串,就应该尽可能使用标准的 第 8 页 。身 型别; 那种“开发自己的字符串类别,以象征具备 某种成熟功力”的日子已经过去 了 (不过 你 还 是 有 必要 了 解 开 发一 个 像 那样的 所 需 知道 的 课 题)。对“示范或验证” 目的(而且可说只对此种目的)而言, 很是方便 无论如何 ,除非你有很好的理由,否则都不应该再使用旧式的 字符 串。具有良好定义的 型别如今已能够在每一方面都比 更具优势, 并且更好 包括其执行效率 (见条款 和 条款 接 下 来 两 个 需 要 掌 握 的 术 语 是 ( 初 始 化 )和 ( 赋 值)。对象的初始化行为发生在它初次获得一个值的时候。对于“带有 之 或 ,初 始 化 总 是 经 由 调 用 某 个 达成。这和对象的 动作不同,后者发生于“已初始化之对象被赋值新值”的时候: (初始化) (初始化) (初始化) (赋值) 纯粹从操作 观点来看, 和 之 间的差异 在于前者由 执 行 , 后 者 由 执行 。换句话说 ,这两个动作对应不同的 函数动作 严格区分此二者,原因是上述两个函数所考虑的事情不同。 通常必须检验其引数的有 效性( ,而 大 部 分 运算符不必如此, 因 为其引数必然是合法的 (因为已被建构完成) 。另一方面 , 动作的 目标 对象 并非是 尚未构 造完 成的对 象,而 是可 能已经 拥有 配置得 来的资 源。 在新 资 源 可 被 赋 值 过 去 之 前 , 旧 资 源 通 常 必 须 先 行 释 放 这 里 所 谓 的 资 源 通 常 是 指 内 存 在 面是 运算符为一个新 值配置内存之前,必须先释放 旧值的内存。下 的 和 运算 符 的可能做法: //以下是一个可能的 : : ); ; 果指针 不是
分享到:
收藏