C++问题:怎么把六年级奥数二进制问题打成十进制,八进制,十六进制,且以制表符整齐格式排列

当前位置: >>
C++入门经典(第4版)
THE EXPERT’S VOICE? IN CBeginning C++C++入门经典(第4版)[美] Ivor Horton
石 磊 著 译 C++入门经典(第 4 版)[美] Ivor Horton 著 石 磊 译北京 Ivor Horton Beginning C++ EISBN:978-1- Original English language edition published by Apress Media. Copyright ? 2015 by Apress Media. Simplified Chinese-language edition copyright ? 2015 by Tsinghua University Press. All rights reserved. 本书中文简体字版由 Apress 出版公司授权清华大学出版社出版。未经出版者书面许可,不得以任何方式复制 或抄袭本书内容。 北京市版权局著作权合同登记号 图字:01-本书封面贴有清华大学出版社防伪标签,无标签者不得销售。 版权所有,侵权必究。侵权举报电话:010- 图书在版编目(CIP)数据 C++入门经典(第 4 版) / (美)霍尔顿(Horton,I.) 著;石磊 译. ―北京:清华大学出版社,2015 书名原文:Beginning C++ ISBN 978-7-302-40628-0 Ⅰ.C… Ⅱ.①霍…②石… Ⅲ. ①C 语言―程序设计 Ⅳ.TP312中国版本图书馆 CIP 数据核字(2015)第 150327 号 责任编辑:王 军 于 平装帧设计:孔祥峰 责任校对:成凤进 责任印制: 出版发行:清华大学出版社 网 地 址:.cn, 址:北京清华大学学研大厦 A 座 邮 邮 编:100084 购:010-社 总 机:010-投稿与读者服务:010-, c-service@tup. 质量反馈:010-, zhiliang@tup. 印 刷 者: 装 订 者: 经 开 版 印 定 销:全国新华书店 本:185mm×260mm 次:2015 年 8 月第 1 版 数:1~5800 价:69.80 元 印 印 张:38 字 数:973 千字 次:2015 年 8 月第 1 次印刷―――――――――――――――――――――――――――――――――――――――――――――― 产品编号: 译 者 序科学计算、分布式应用、嵌入式行业、智能控制、算法研究,乃至学术讨论和上机 考试都会有一种语言的身影,那就是大名鼎鼎的 C++语言。原汁原味的 C++目前已经执 行到 C++14 标准。听到这门编程语言,多数人伴随而来的是晦涩、复杂、强大等关键词! 这样的感觉实际上是真实的。 对于这门复杂的编程语言,本书首先从一个最简短的 C++程序讲起,通过对这个完 整的程序的实际编写引申出一些相关的知识,然后在后面的教程中对该程序不断地扩大 和完善,这样,读者不至于一上来就被 C++吓坏,同时也能深刻地理解 C++的各个特性 的设置目的。 有抱负的程序员必将面对三重障碍: 首先,必须掌握遍布程序设计语言中的各类术语。术语是专业人士及优秀业余爱好 者之间的交流必不可少的,本书不仅详细讲解了这些术语,还强调如何自如地在各种环 境下使用它们。 其次,必须理解如何使用语言元素,而不仅仅只是知道它们的概念。本书采用了代 码片段来帮助理解语言元素的语法和作用,还用一些实际应用示例展示语言特性如何应 用于特定的问题。 最后,必须领会如何在实际场景中应用该语言。本书针对特定的问题应用所学的知 识,给出合理的解决方案,不仅帮助读者获得开发应用程序的能力与信心,了解如何联 合以及更大范围地应用语言元素;而且能让读者了解设计实际应用程序与管理实际代码 会碰到的问题。 读者使用本书时,记住只有通过动手实践才能学会编程,在学习的过程中,肯定会 时不时犯许多错误而感到沮丧。当觉得自己完全停滞时,你要做的就是坚持。最终你一 定会体验到成功的喜悦,回头想想,你会觉得它并没有你想象中的那么难。 本版与上一版的出版时间相距 10 年, 期间 C++已经有了很大的变化, 所以本版对前 一版进行了彻底的修订,不仅内容经过了重新组织,添加了不少新内容,对每个示例都 做了相应的修改,使它们能在新的 C++ 14 标准下编译运行。而且显著改善了可读性,充 分体现了 C++语言的最新进展和当前的业界最佳实践。 本书是 C++初学者的权威指南。无论你是从事软件开发还是其他领域的工作,本书 将为你打开程序开发之门。本书还是中高级程序员的必备参考。通过观察程序设计大师 如何处理编程中的各种问题,使你获得新的领悟和指引。 IIC++入门经典(第 4 版)在这里要感谢清华大学出版社的编辑们,他们为本书的翻译投入了巨大的热情并付 出了很多心血。没有你们的帮助和鼓励,本书不可能顺利付梓。 本书由石磊翻译,参加本次翻译活动的还有孔祥亮、陈跃华、杜思明、熊晓磊、曹 汉鸣、陶晓云、王通、方峻、李小凤、曹晓松、蒋晓冬、邱培强、洪妍、李亮辉、高娟 妮、曹小震、陈笑。对于这本经典之作,译者本着“诚惶诚恐”的态度,在翻译过程中 力求“信、达、雅” ,但是鉴于译者水平有限,错误和失误在所难免,如有任何意见和建 议,请不吝指正。 最后, 希望读者通过阅读本书能早日步入 C++语言编程的殿堂, 领略 C++语言之美!译者 作 者 简 介Ivor Horton 毕业于数学系,被人以获得巨大荣誉且代价 极低而诱入信息技术行业。尽管他做了大量的工作,获得的 荣誉并不多,但他继续在计算机领域工作。他主要关注编程、 系统设计、咨询、非常复杂的项目的管理和实现。 Ivor 在工程设计和生产控制系统的设计、实现方面有多 年的经验。他偶尔使用各种编程语言开发有用的应用程序, 主要教科学家和工程师开发这些程序。他目前出版的著作包 括 C、C++和 Java 方面的教材。目前,他不编写编程图书或 给他人提供建议时,会钓鱼、旅游、享受生活。 技术编辑简介Michael Thomas 作为独立撰稿人、团队领袖、项目经 理和工程负责人,在软件开发方面工作了 20 余年。Michael 在移动设备方面有 10 余年的经验。他当前的重点是医疗 领域,使用移动设备加速患者和医护人员之间的信息传递 速度。 前言欢迎使用 《C++入门经典(第 4 版)》 。 本书修订并更新了上一版 (Beginning ANSI C++) 。 自上一版出版以来,C++语言有了很大的扩展和改进,但不太可能把 C++的所有内容压 缩到一本书中。 本书提供的 C++语言基础知识和标准库功能, 足以让读者编写自己的 C++ 应用程序。掌握了本书介绍的知识,读者应能毫无困难地扩展 C++专业知识的深度和广 度。C++要比许多人想象的更容易理解。本书不需要读者具备任何编程知识。如果你非 常渴望学习,并具备逻辑思考的能力,掌握 C++就会比想象的更容易。开发 C++技巧, 学习数百万人已在使用的语言,掌握 C++技能,它提供了在几乎任何环境下开发应用程 序的能力。 本书的 C++语言对应最新的 ISO 标准, 一般称为 C++ 14。 C++ 14 对以前的标准 C++ 11 进行了较小的扩展, 所以本书的内容大都不专用于 C++ 14。 本书的所有示例都可以使 用目前遵循 C++ 11 的编译器来编译和执行。使用本书要通过本书学习 C++, 需要一个遵循 C++ 11 标准的编译器和一个适合编写程序代码 的文本编辑器。目前,有几个编译器兼容 C++ 11,其中一些是免费的。 GNU Project 生产的 GCC 编译器全面支持 C++ 11,是一个开源产品,可免费下载。 安装 GCC 并将它与合适的编辑器一起使用,对新手而言略有难度。安装 GCC 和合适编 译器的一种简单方法是,从 http://www.codeblocks.org 上下载 Code::Blocks。Code::Blocks 是 Linux、Apple Mac OS X 和 Microsoft Windows 的一个免费 IDE,它允许使用几个编译 器 (包括用于 GCC、 Clang 和 open Watcom 的编译器) 开发程序。 这表示, 安装 Code::Blocks 会获得 C、C++和 Fortran 的支持。 另一种方法是使用在 Microsoft Windows 下运行的 Microsoft Visual C++, 它不仅完全 兼容 C++ 11,而且已经安装好了。其免费版本是 Microsoft Visual Studio 2013 Express。 编写本书时,它可以编译本书的大多数示例,最终应能编译所有示例。Microsoft Visual C++ 可以从 /en-us/download/details.aspx?id=43733 上下载。与 GCC 相比,Microsoft Visual C++编译器的限制多一些,但根据它对 C++ 11 的支持力度, 这是一个专业的编译器,还支持其他语言,例如 C#和 Basic。当然,也可以安装这两个 编辑器。还有其他支持 C++ 11 的编辑器,在网上搜索会很快找到它们。 VIC++入门经典(第 4 版)本书的内容循序渐进,所以读者应从头开始一直阅读到最后。但是,没有人能仅从 一本书中获得所有的编程技巧。本书仅介绍如何使用 C++编程,读者应自己输入所有的 例子,而不是从下载文件中复制它们,再编译和执行输入的代码,这似乎很麻烦,但输 入 C++语句可以帮助理解 C++,特别是觉得某些地方很难掌握时,自己输入代码就显得 非常有帮助。如果例子不工作,不要直接从书中查找原因,而应在自己输入的例子代码 中找原因,这是编写 C++代码时必须做的一个工作。 犯错误也是学习过程中不可避免的,练习应提供大量犯错误的机会,最好自己编几 个练习题。如果不确定如何编写代码,应翻看前面的内容。犯的错误越多,对 C++的功 能和错误的原因认识得就越深刻。读者应完成所有的练习,记住不要看答案,直到肯定 不能自己解决问题为止。许多练习都涉及某章内容的一个直接应用,换言之,它们仅是 一种实践,但也有一些练习需要多动脑子,甚至需要一点灵感。 希望每个人都能成功驾驭 C++。 ―Ivor Horton 目录第 1 章 基本概念 ...................................... 1 1.1 现代 C++ ..................................... 1 1.2 C++程序概念.............................. 2 1.2.1 注释和空白 ......................... 2 1.2.2 预处理指令和头文件 ........... 3 1.2.3 函数 .................................... 3 1.2.4 语句 .................................... 4 1.2.5 数据输入输出 ...................... 4 1.2.6 return 语句........................... 5 1.2.7 名称空间 ............................. 5 1.2.8 名称和关键字 ...................... 6 1.3 类和对象 ..................................... 6 1.4 模板 ............................................. 7 1.5 程序文件 ..................................... 7 1.6 标准库 ......................................... 7 1.7 代码的表示样式 ........................ 7 1.8 创建可执行文件 ........................ 8 1.9 表示数字 ..................................... 9 1.9.1 二进制数 ............................. 9 1.9.2 十六进制数 ....................... 11 1.9.3 负的二进制数 .................... 12 1.9.4 八进制数 ........................... 141.9.5 1.9.6 Big-Endian 和 Little-Endian 系统 .................................. 14 浮点数 .............................. 151.11.1 1.11.2三字符序列 ...................... 18 转义序列.......................... 181.12 1.13 1.14过程化编程方法和面向 对象编程方法 ......................... 20 本章小结 ................................. 21 练习 .......................................... 22第 2 章 基本数据类型 ............................23 2.1 变量、数据和数据类型 .......... 23 2.1.1 定义整型变量 .................... 24 2.1.2 定义有固定值的变量 ......... 26 2.2 整型字面量 ............................... 26 2.2.1 十进制整型字面量 ............. 27 2.2.2 十六进制的整型字面量 ...... 27 2.2.3 八进制的整型字面量 ......... 27 2.2.4 二进制的整型字面量 ......... 28 2.3 整数的计算 ............................... 28 2.4 op=赋值运算符 ......................... 33 2.5 using 声明和指令 ..................... 34 2.6 sizeof 运算符 ............................. 34 2.7 整数的递增和递减 ................... 35 2.8 定义浮点变量 ........................... 37 2.8.1 浮点字面量 ........................ 38 2.8.2 浮点数的计算 .................... 38 2.8.3 缺点 .................................. 38 2.8.4 无效的浮点结果................. 39 2.9 数值函数 ................................... 40 2.10 流输出的格式化 ..................... 43 2.11 混合的表达式和类型转换 .... 45 2.11.1 显式类型转换 ................ 46表示字符 ................................. 16 1.10.1 ASCII 码 ...................... 16 1.10.2 UCS 和 Unicode ............. 17 1.11 C++源字符 .............................. 17 1.10 VIIIC++入门经典(第 4 版) 老式的强制转换 ............ 48 逻辑非运算符 .................... 912.11.24.4.32.12 2.13确定数值的上下限 ................ 49 使用字符变量......................... 50 2.13.1 使用 Unicode 字符 ......... 51 2.13.2 auto 关键字 ................... 52 2.13.3 lvalue 和 rvalue .............. 52 本章小结 ................................. 53 练习 ......................................... 542.14 2.154.5 4.6 4.7 4.8 4.9 4.10条件运算符 ............................... 92 switch 语句 ................................ 94 无条件分支 ............................... 98 语句块和变量作用域 ............... 99 本章小结 ................................. 100 练习 ........................................ 100第 3 章 处理基本数据类型 ................... 55 3.1 运算符的优先级和相关性 ...... 55 3.2 按位运算符 ............................... 57 3.2.1 移位运算符 ....................... 58 3.2.2 使用按位与运算符............. 60 3.2.3 使用按位或运算符............. 61 3.2.4 使用按位异或运算符 ......... 63 枚举数据类型 ........................... 67 数据类型的同义词 .................. 70 变量的生存期 ........................... 70 3.5.1 定位变量的定义 ................ 71 3.5.2 全局变量 ........................... 71 3.5.3 静态变量 ........................... 74 3.5.4 外部变量 ........................... 75 3.6 本章小结 ................................... 75 3.7 练习 ........................................... 76 3.3 3.4 3.5 第 4 章 决策 ............................................ 77 4.1 比较数据值 ............................... 77 4.1.1 应用比较运算符 ................ 78 4.1.2 比较浮点数值 .................... 79 4.2 if 语句 ........................................ 80 4.2.1 嵌套的 if 语句 ................... 82 4.2.2 不依赖编码的字符处理 ..... 84 4.3 if-else 语句 ................................ 85 4.3.1 嵌套的 if-else 语句............. 87 4.3.2 理解嵌套的 if 语句 ............ 88 4.4 逻辑运算符 ............................... 89 4.4.1 逻辑与运算符 .................... 90 4.4.2 逻辑或运算符 .................... 90第 5 章 数组和循环 ............................. 103 5.1 数据数组 ................................. 103 5.2 理解循环 ................................. 105 5.3 for 循环 .................................... 106 5.3.1 避免幻数 ......................... 1075.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 用初始化列表定义 数组的大小 ...................... 109 确定数组的大小............... 109 用浮点数值控制 for 循环 ................................ 110 使用更复杂的循环 控制表达式 ...................... 112 逗号运算符 ...................... 113 基于区域的 for 循环 ......... 1145.4 5.5 5.6 5.7 5.8 5.9 5.10while 循环................................ 115 do-while 循环 .......................... 119 嵌套的循环 ............................. 120 跳过循环迭代 ......................... 123 循环的中断 ............................. 125 字符数组 ................................. 128 多维数组 ............................... 131 5.10.1 初始化多维数组........... 1345.10.2 5.10.3 在默认情况下设置 维数 ............................ 135 多维字符数组 .............. 1365.11数组的替代品 ....................... 137 5.11.1 使用 array&T,N&容器 ... 1385.11.2 5.11.3 使用 std::vector&T& 容器 ............................ 142 矢量的容量和大小 ....... 143 ■ 目录IX5.11.4删除矢量容器中的 元素 ............................ 1455.12 5.13本章小结 ............................... 145 练习 ....................................... 146第 6 章 指针和引用.............................. 149 6.1 什么是指针 ............................. 149 6.1.1 地址运算符 ..................... 151 6.1.2 间接运算符 ..................... 152 6.1.3 为什么使用指针 .............. 153 6.2 char 类型的指针..................... 154 6.3 常量指针和指向常量的 指针 ......................................... 158 6.4 指针和数组 ............................. 159 6.4.1 指针的算术运算 .............. 160 6.4.2 计算两个指针之间的差 ... 1626.4.3 使用数组名的指针 表示法 ............................ 162第 7 章 操作字符串 ............................. 183 7.1 更好的 string 类型 .................. 183 7.1.1 定义 string 对象 ............... 184 7.1.2 string 对象的操作 ............. 186 7.1.3 访问字符串中的字符 ....... 188 7.1.4 访问子字符串 .................. 190 7.1.5 比较字符串 ...................... 191 7.1.6 搜索字符串 ...................... 196 7.1.7 修改字符串 ...................... 203 7.2 7.3 7.4 7.5 7.6 国际字符串 ............................. 207 包含 Unicode 字符串的 对象 .......................................... 208 原字符串字面量 ..................... 208 本章小结 ................................. 209 练习 .......................................... 2106.5动态内存分配 ......................... 165 6.5.1 栈和堆 ............................ 165 6.5.2 运算符 new 和 delete ........ 166 6.5.3 数组的动态内存分配 ....... 167 6.5.4 通过指针选择成员........... 169 动态内存分配的危险 ............ 169 6.6.1 内存泄漏 ......................... 169 6.6.2 自由存储区的碎片........... 170第 8 章 定义函数 ..................................211 8.1 程序的分解 ............................. 211 8.1.1 类中的函数 ...................... 212 8.1.2 函数的特征 ...................... 212 8.2 定义函数 ................................. 212 8.2.1 函数体 ............................. 213 8.2.2 函数声明 ......................... 215 给函数传送参数 ..................... 217 8.3.1 按值传送机制 .................. 217 8.3.2 按引用传送 ...................... 223 8.3.3 main()的参数 ................... 227 8.4 默认的参数值 ......................... 228 8.5 从函数中返回值 ..................... 231 8.5.1 返回指针 ......................... 231 8.5.2 返回引用 ......................... 235 8.3 内联函数 ................................. 236 静态变量 ................................. 237 函数的重载 ............................. 239 8.8.1 重载和指针参数............... 241 8.8.2 重载和引用参数............... 241 8.8.3 重载和 const 参数 ............ 243 8.8.4 重载和默认参数值 ........... 244 8.9 函数模板 ................................. 245 8.6 8.7 8.86.6原指针和智能指针 ................ 170 6.7.1 使用 unique_ptr&T&指针 .. 172 6.7.2 使用 shared_ptr&T&指针 ... 173 6.7.3 比较 shared_ptr&T&对象 ... 177 6.7.4 weak_ptr&T&指针 ............ 177 6.8 理解引用 ................................. 178 6.8.1 定义左值引用 .................. 179 6.76.8.2 6.8.3 在基于区域的 for 循环中 使用引用变量 .................. 180 定义右值引用 .................. 1806.9 本章小结 ................................. 181 6.10 练习 ....................................... 181 XC++入门经典(第 4 版) 创建函数模板的实例 ....... 246 显式指定模板参数........... 247 函数模板的特例 .............. 248 函数模板和重载 .............. 249 带有多个参数的 函数模板 ......................... 250 8.9.6 非类型的模板参数........... 2518.9.1 8.9.2 8.9.3 8.9.4 8.9.59.9练习 .......................................... 2838.10 拖尾返回类型....................... 252 8.11 函数指针 ............................... 253 8.12 递归 ....................................... 256 8.12.1 应用递归 ..................... 259 8.12.2 Quicksort 算法 ............. 259 8.12.3 main()函数 .................. 260 8.12.4 extract_words()函数 ..... 261 8.12.5 swap()函数 .................. 262 8.12.6 sort()函数 .................... 2628.12.7 max_word_length() 函数 ............................ 263 8.12.8 show_words()函数 ....... 264第 10 章 程序文件和预处理指令 ...... 285 10.1 理解转换单元 ....................... 285 10.1.1 “一个定义”规则 ......... 286 10.1.2 程序文件和链接........... 286 10.1.3 确定名称的链接属性 ... 286 10.1.4 外部名称 ..................... 28710.1.5 具有外部链接属性的 const 变量 .................... 28710.2 10.3 10.4 10.58.13 8.14本章小结 ............................... 265 练习 ....................................... 266 lambda 表达式 ....................... 26910.6第9章 9.1 lambda 表达式简介................ 269 9.2 定义 lambda 表达式 .............. 269 9.3 lambda 表达式的命名 ........... 270 9.4 把 lambda 表达式传递给 函数 ......................................... 2729.4.1 9.4.2 9.4.3 接受 lambda 表达式 变元的函数模板 .............. 272 lambda 变元的函数 参数类型 ......................... 273 使用 std::function 模板类型 ......................... 274预处理源代码 ....................... 288 定义预处理标识符 ............... 289 包含头文件 ........................... 290 名称空间 ............................... 292 10.5.1 全局名称空间 .............. 293 10.5.2 定义名称空间 .............. 293 10.5.3 应用 using 声明 ............ 296 10.5.4 函数和名称空间........... 296 10.5.5 未命名的名称空间 ....... 299 10.5.6 名称空间的别名........... 299 10.5.7 嵌套的名称空间........... 300 逻辑预处理指令 ................... 301 10.6.1 逻辑#if 指令 ................ 301 10.6.2 测试指定标识符的值 ... 302 10.6.3 多个代码选择 .............. 302 10.6.4 标准的预处理宏........... 303调试方法 ............................... 304 10.7.1 集成调试器 .................. 304 10.7.2 调试中的预处理指令 ... 305 10.7.3 使用 assert 宏 ............... 309 10.7.4 关闭断言机制 .............. 310 10.8 静态断言 ............................... 310 10.9 本章小结 ............................... 312 10.10 练习...................................... 313 10.7 第 11 章 定义自己的数据类型 .......... 315 11.1 类和面向对象编程 ............... 315 11.1.1 封装 ............................ 316 11.1.2 继承 ............................ 3189.5 9.6捕获子句 ................................. 277 在模板中使用 lambda 表达式 ..................................... 279 9.7 lambda 表达式中的递归 ....... 281 9.8 本章小结 ................................. 283 ■ 目 多态性 ........................ 318 术语 ............................ 319录XI11.1.3 11.1.412.1.3 12.1.4 12.1.5 12.1.6实现重载运算符........... 366 全局运算符函数........... 369 提供对运算符的 全部支持 ....................369 在类中实现所有的 比较运算符 ................. 37111.2 11.3定义类 ................................... 320 构造函数 ............................... 32211.3.1 11.3.2 11.3.3 在类的外部定义 构造函数 ..................... 324 默认构造函数的 参数值 ........................ 326 在构造函数中使用 初始化列表 ................. 326 11.3.4 使用 explicit 关键字 ..... 327 11.3.5 委托构造函数 .............. 329 11.3.6 默认的副本构造函数 ... 33112.2 12.3运算符函数术语 ................... 373 默认的类成员 ....................... 374 12.3.1 定义析构函数 .............. 37512.3.2 12.3.3 12.3.4 何时定义副本 构造函数 ..................... 377 实现赋值运算符........... 377 实现移动操作 .............. 379访问私有类成员................... 332 友元 ....................................... 333 11.5.1 类的友元函数 .............. 334 11.5.2 友元类 ........................ 336 11.6 this 指针 ................................ 337 11.7 const 对象和 const 函数成员 ............................... 338 11.8 类的对象数组 ....................... 340 11.9 类对象的大小 ....................... 342 11.10 类的静态成员 ..................... 342 11.10.1 静态数据成员 ............ 342 11.10.2 类的静态函数成员 ..... 347 11.11 析构函数 ............................. 347 11.12 类对象的指针和引用 ........ 350 11.13 将指针作为类的成员 ........ 351 11.13.1 定义 Package 类 ......... 353 11.13.2 定义 TruckLoad 类 ..... 354 11.13.3 实现 TruckLoad 类 ..... 355 11.4 11.5 11.14 11.15 11.16 第 12 章 12.1 嵌套类 ................................. 360 本章小结 ............................. 363 练习 ..................................... 363 运算符重载 ........................... 365 为类实现运算符 .................. 365 12.1.1 运算符重载 ................. 366 12.1.2 可以重载的运算符 ...... 36612.4重载算术运算符 ................... 380 12.4.1 改进输出操作 .............. 38412.4.2 根据一个运算符实现 另一个运算符.............. 38612.5 12.6 12.7 12.8 12.9 12.10重载下标运算符 ................... 387 重载类型转换 ....................... 394 重载递增和递减运算符 ...... 395 函数对象 ............................... 396 本章小结 ............................... 397 练习...................................... 398第 13 章 继承 ....................................... 399 13.1 类和面向对象编程 ............... 399 13.2 类的继承 ............................... 401 13.2.1 继承和聚合 .................. 401 13.2.2 派生类 ......................... 402 13.3 13.4 把类的成员声明为 protected ................................ 405 派生类成员的访问级别 ...... 40513.4.1 13.4.2 在类层次结构中 使用访问指定符........... 406 改变继承成员的 访问指定符 .................. 40813.5派生类中的构造 函数操作 ............................... 408 XIIC++入门经典(第 4 版)13.5.1 13.5.2 13.5.3派生类中的副本 构造函数 ..................... 412 派生类中的默认 构造函数 ..................... 414 继承构造函数 .............. 41414.4 14.5 14.6通过指针释放对象 ............... 457 本章小结 ............................... 458 练习 ........................................ 45913.6 13.7 13.8 13.9继承中的析构函数 .............. 415 重复的成员名....................... 417 重复的函数成员名 .............. 418 多重继承 ............................... 419 13.9.1 多个基类 ..................... 419 13.9.2 继承成员的模糊性 ...... 420 13.9.3 重复的继承 ................. 424 13.9.4 虚基类 ........................ 425第 15 章 运行时错误和异常 .............. 461 15.1 处理错误 ............................... 461 15.2 理解异常 ............................... 462 15.2.1 抛出异常 ..................... 463 15.2.2 异常处理过程 .............. 465 15.2.3 未处理的异常 .............. 466 15.2.4 导致抛出异常的代码 .... 467 15.2.5 嵌套的 try 块................ 468 15.3 用类对象作为异常 ............... 47215.3.1 15.3.2 15.3.3 15.3.4 匹配 Catch 处理程序和 异常 ............................ 473 用基类处理程序捕获 派生类异常 ................. 476 重新抛出异常 .............. 478 捕获所有的异常........... 48113.10在相关的类类型之间 转换 ..................................... 425 13.11 本章小结 ............................. 426 13.12 练习 ..................................... 426 15.4第 14 章 多态性 .................................... 429 14.1 理解多态性 ........................... 429 14.1.1 使用基类指针 .............. 429 14.1.2 调用继承的函数 .......... 431 14.1.3 虚函数 ........................ 43414.1.4 14.1.5 14.1.6 14.1.7 14.1.8 14.1.9 14.1.10 14.1.11 虚函数中的默认参 数值 ............................ 442 通过智能指针调用 虚函数........................ 443 通过引用调用虚函数.... 444 调用虚函数的基类 版本 ............................ 445 在指针和类对象之间 转换 ........................... 446 动态强制转换 .............. 447 转换引用 ................... 449 确定多态类型 ............ 44915.5抛出异常的函数 ................... 482 15.4.1 函数 try 块 ................... 483 15.4.2 不抛出异常的函数 ....... 483 15.4.3 构造函数 try 块 ............ 484 15.4.4 异常和析构函数........... 484 标准库异常 ........................... 485 15.5.1 异常类的定义 .............. 486 15.5.2 使用标准异常 .............. 487 本章小结 ............................... 490 练习 ........................................ 49115.6 15.7第 16 章 类模板 ................................... 493 16.1 理解类模板 ........................... 493 16.2 定义类模板 ........................... 494 16.2.1 模板参数 ..................... 495 16.2.2 简单的类模板 .............. 49516.2.3 定义类模板的函数 成员 ............................ 49714.2 14.3多态性的成本....................... 450 纯虚函数 ............................... 451 14.3.1 抽象类 ........................ 452 14.3.2 间接的抽象基类 .......... 45416.3 16.4创建类模板的实例 ............... 501 类模板的静态成员 ............... 506 ■ 目录XIII16.5非类型的类模板参数 .......... 50716.5.1 16.5.2 16.5.3 带有非类型参数的 函数成员的模板 ......... 510 非类型参数的变元 ...... 514 把指针和数组用作 非类型参数................. 51417.3文件流.................................... 54017.3.1 17.3.2 在文本模式下写入 文件 ............................ 540 在文本模式下读取 文件 ............................ 54316.6 16.7 16.8模板参数的默认值 .............. 515 模板的显式实例化 .............. 516 特殊情形 ............................... 51616.8.1 16.8.2 16.8.3 16.8.4 在类模板中使用 static_assert() .............. 517 定义类模板特化 .......... 518 部分模板特化 .............. 519 从多个部分特化中 选择 ............................ 51917.4 17.5设置流打开模式 ................... 546 未格式化的流操作 ............... 55417.5.1 17.5.2 未格式化的流输入 函数 ............................ 554 未格式化的流输出 函数 ............................ 55716.9 类模板的友元....................... 520 16.10 带有嵌套类的类模板 ........ 521 16.11 本章小结 ............................. 528 16.12 练习 ..................................... 528 第 17 章 文件输入与输出 ................... 531 17.1 C++中的输入输出 ............... 531 17.1.1 理解流 ........................ 531 17.1.2 使用流的优点 .............. 533 17.2 流类 ....................................... 534 17.2.1 标准流对象 ................. 535 17.2.2 流的插入和提取操作.... 535 17.2.3 流操纵程序 ................. 53717.6 17.7 17.8 17.9 17.10流输入输出中的错误........... 557 二进制模式中的流操作 ...... 559 文件的读写操作 ................... 570 字符串流 ............................... 577 对象和流 ............................. 57817.10.1 17.10.2 17.10.3 17.10.4 给对象使用插入 运算符 ....................... 579 给对象使用提取 运算符 ....................... 579 二进制模式中的 对象 I/O ..................... 582 流中更复杂的对象 ..... 58517.11 本章小结 ............................. 590 17.12 练习...................................... 590 第1章基 本 概 念有时必须在详细解释示例之前使用示例中的元素。本章将概述 C++的主要元素及其 组合方式, 以帮助理解这些元素, 还要探讨与计算机中表示数字和字符相关的几个概念。 本章主要内容 ● 现代 C++的含义 ● C++程序的元素 ● 如何注释程序代码 ● C++代码如何变成可执行程序 ● 面向对象的编程方式与过程编程方式的区别 ● 二进制、十六进制和八进制数字系统 ● Unicode1.1 现代 C++现代 C++使用最新、最好的 C++元素编程。这是 C++ 11 标准定义的 C++语言,最 新标准 C++ 14 对它进行了谨慎的扩展和改进。本书介绍 C++ 14 定义的 C++。 毫无疑问,C++是目前世界上使用最广泛、最强大的编程语言。如果打算学习一门 编程语言,C++就是一个理想的选择。它能在极大范围内的计算设备和环境中高效地开 发应用程序:个人电脑、工作站、大型计算机、平板电脑和移动电话。几乎任何程序都 可以用 C++编写:设备驱动程序、操作系统、薪水管理程序、游戏等。C++编译器也是 唾手可得的。最新的编译器运行在 PC、工作站和大型机上,常常具备跨编译功能,即可 以在一个环境下开发代码,在另一个环境下编译并执行。 C++带有一个非常大的标准库,其中包含大量例程和定义,提供了许多程序需要的 功能。例如,数值计算、字符串处理、排序和搜索、数据的组织和管理、输入输出等。 标准库非常大,本书仅涉及其皮毛。详细描述标准库提供的所有功能,需要好几本书的 篇幅。 Beginning STL 是使用标准模板库的一个指南, 而标准模板库是 C++标准库中以各 种方式管理和处理数据的一个子集。 就 C++语言的范围和库的广度而言,初学者常常觉得 C++令人生畏。将 C++的全部 内容放在一本书里是不可能的,但其实不需要学会 C++的所有内容,就可以编写实用的 2C++入门经典(第 4 版)程序。该语言可以循序渐进地学习,这并不是很难。例如学习开车,没有相关的专业知 识和经验,也可以成为非常有竞争力的司机,在印第安纳波利斯安全地开车。通过本书, 可以学到使用 C++高效编程所需的所有知识。读完本书后,你肯定可以编写自己的应用 程序,还可以开始研究 C++及其标准库的所有内容。1.2C++程序概念本书后面将详细论述本节介绍的所有内容。图 1-1 是一个完全可以工作的完整 C++ 程序,后面将解释该程序的各个部分。这个示例将用作讨论 C++一般内容的基础。这是一个语句,语句用分号结束。 这一行还有一个注释。 这两行是注释,注释以 //开头。 这一行添加了输入/输出功能。 这是 main 函数的第一行。函数的所有代码 包含在花括号中。这个语句结束了 main 函数。这个语句横跨 3 行。图 1-1一个完整的 C++程序1.2.1注释和空白图 1-1 中的前两行是注释。添加注释来解释程序代码,可以使他人更容易理解程序 的工作方式。编译器会忽略一行代码中双斜杠后面的所有内容,所以这种注释可以跟在 一行代码的后面。第一行注释指出包含代码的文件名。这个文件在本书的下载代码中。 本书每个可工作示例的文件都以这种方式给出。文件扩展名.cpp 表示,这是一个 C++源 文件。其他扩展名,例如,.cc 也用于表示 C++源文件。程序的所有可执行代码放在一个 或多个源文件中。 需要把注释放在多行上时,可以使用另一种注释形式。例如:/* This comment is over two lines. */编译器会忽略/*和*/之间的所有内容。可以修饰这种注释,使其更加突出。例如: 第1章 ■ 基 本 概 念3/************************ * This comment is * * over two lines. * ************************/空白是空格、制表符、换行符、换页符和注释的任意序列。编译器一般会忽略空白, 除非由于语法原因需要使用空白把元素区分开来。1.2.2预处理指令和头文件图 1-1 的第三行是一个预处理指令。预处理指令会以某种方式修改源代码,之后把 它们编译为可执行的形式。这个预处理指令把标准库头文件 iostream 的内容添加到这个 源文件 Ex1_01.cpp 中。头文件的内容插入到#include 指令的位置。 头文件包含源文件中使用的定义。iostream 包含使用标准库例程从键盘输入和将文 本输出到屏幕上所需的定义。具体而言,它定义了 std::cout 和 std::endl。每个程序都可 以包含一个或多个标准库头文件的内容,也可以创建和使用自己的头文件,以包含本书 后面构建的定义。如果在 Ex1_01.cpp 中省略了包含 iostream 头文件的预处理指令,源文 件就不会编译,因为编译器不知道 std::cout 和 std::endl 是什么。在编译之前,把头文件 的内容包含到源文件中。 提示: 尖括号和标准头文件名之间没有空格。对于一些编译器而言,尖括号&和&之间的空 格很重要;如果在这里插入了空格,程序就不会编译。1.2.3函数每个 C++程序都至少包含一个函数, 通常包含许多函数。 函数是一个命名的代码块, 这些是定义好的操作,例如读取输入的数据,计算平均值,或者输出结果。在程序中使 用函数的名称执行或调用函数。程序中的所有可执行代码都放在函数中。程序中必须有 一个名为 main 的函数,执行总是自动从这个函数开始。main()函数总是调用其他函数, 这些函数又可以调用其他函数,以此类推。函数提供了几个重要的优点: ● 程序分解为离散的函数,更容易开发和测试。 ● 函数可以在程序的几个不同的地方重用,与在每个需要的地方编写操作代码相 比,这会使程序更小。 ● 函数常常可以在许多不同的程序中重用,节省了时间和精力。 ● 大程序常常由一组程序员共同开发。 每个组员负责编写一系列函数, 这些函数是 整个程序中已定义好的一个子集。没有函数结构,这就是不可能的。 图 1-1 中的程序只包含 main()函数。该函数的第一行是:int main()这称为函数头,标识了函数。其中 int 是一个类型名称,它定义了 main()函数执行完 4C++入门经典(第 4 版)毕时返回的值的类型整数。一般情况下,函数定义中名称后面的圆括号,包含了调用函 数时要传递给函数的信息的说明。本实例中的圆括号是空的,但其中可以有内容。第 5 章将学习如何指定执行函数时传递给函数的信息的类型。文本中总是在函数名的后面加 上圆括号,以区分函数与其他代码。函数的可执行代码总是放在花括号中,左花括号跟 在函数头的后面。1.2.4语句语句是 C++程序的基本单元。语句总是用分号结束。分号表示语句的结束,而不是 代码行的结束。语句可以定义某个元素,例如计算或者要执行的操作。程序执行的所有 操作都是语句指定的。语句按顺序执行,除非有某个语句改变了这个顺序。第 4 章将学 习可以改变执行顺序的语句。图 1-1 的 main()中有 3 个语句。第一个语句定义了一个变 量,变量是一个命名的内存块,用于存储某种数据。在本例中变量的名称是 answer,可 以存储整数值:int answer {42}; // Defines answer with the value 42类型 int 放在名称的前面, 这指定了可以存储的数据类型――整数。 注意 int 和 answer 之间的空格。这里的一个或多个空白字符是必需的,用于分隔类型名称和变量名称。如 果没有空格,编译器会把名称看作 intanswer,这是编译器无法理解的。answer 的初始值 放在变量名后面的花括号中,所以它最初存储了 42。answer 和{42}之间也有一个空格, 但这个空格不是必需的。花括号不是名称的一部分,所以无论如何,编译器都可以区分 名称和初始值的指定。但是,应以统一的风格使用空白,提高代码的可读性。在第一个 语句的末尾有一个多余的注释,解释了上述内容,这还说明,可以在语句中添加注释。 //前面的空白也不是强制的,但最好保留这个空白。 可以把几个语句放在一对花括号{}中,此时这些语句就称为语句块。函数体就是一 个语句块,如图 1-1 所示,main()函数体中的语句就放在花括号中。语句块也称为复合语 句,因为在许多情况下,语句块可以看作是一个语句,详见第 4 章中的决策功能。在可 以放置一个语句的任何地方,都可以放置一个包含在花括号对中的语句块。因此,语句 块可以放在其他语句块内部,这个概念称为嵌套。事实上,语句块可以嵌套任意级。1.2.5数据输入输出在 C++中,输入和输出是使用流来执行的。如果要输出消息,可以把该消息放在输 出流中,如果要输入数据,则把它放在输入流中。因此,流是数据源或数据池的一种抽 象表示。在程序执行时,每个流都关联着某个设备,关联着数据源的流就是输入流,关 联着数据目的地的流就是输出流。对数据源或数据池使用抽象表示的优点是,无论流代 表什么设备,编程都是相同的。例如,从磁盘文件中读取数据的方式与从键盘上读取完 全相同。在 C++中,标准的输出流和输入流称为 cout 和 cin,在默认情况下,它们分别 对应计算机的屏幕和键盘。第 2 章将从 cin 中读取输入。 第1章 ■ 基 本 概 念5在图 1-1 中,main()的下一个语句把文本输出到屏幕:std::cout&& &The answer to life, the universe, and everything is & && answer &&std::该语句放在 3 行上,只是为了说明这么做是可行的。名称 cout 和 endl 在 iostream 头 文件中定义。本章后面将解释 std::前缀。&&是插入操作符,用于把数据传递到流中。第 2 章会遇到提取操作符&&,它用于从流中读取数据。每个&&右边的所有内容都会传递到 cout 中。把 endl 写入 std::cout,会在流中写入一个换行符,并刷新输出缓存。刷新输出 缓存可确保输出立即显示出来。该语句的结果如下:The answer to life, the universe, and everything is 42可以给每行语句添加注释。例如:std::cout && &The answer to life, the universe, and everything is & // This statement && answer // occupies && std:: // three lines双斜杠不必对齐,但我们常常对齐双斜杠,使之看起来更整齐,代码更容易阅读。1.2.6return 语句main()中的最后一个语句是 return。return 语句会结束函数,把控制权返回给调用函 数的地方。在本例中它会结束函数,把控制权返回给操作系统。return 语句可能返回一 个值或没有返回值。本例的 return 语句给操作系统返回 0,表示程序正常结束。程序可 以返回非 0 值,例如 1、2 等,表示不同的异常结束条件。Ex1_01.cpp 中的 return 语句是 可选的,可以忽略它。这是因为如果程序执行超过了 main()的最后一个语句,就等价于 执行 return 0。1.2.7名称空间大项目会同时涉及几个程序员。这可能会带来名称问题。不同的程序员可能给不同 的元素使用相同的名称,这可能会带来一些混乱,使程序出错。标准库定义了许多名称, 很难全部记住。不小心使用了标准库名称也会出问题。名称空间就是用于解决这个问 题的。 名称空间类似于姓氏,它置于该名称空间中声明的所有名称前面。标准库中的名称 都在 std 名称空间中定义,cout 和 endl 是标准库中的名称,所以其全名是 std::cout 和 std::endl。 其中的两个冒号有一个非常奇特的名称: 作用域解析操作符, 详见后面的说明。 这里它用于分隔名称空间的名称 std 和标准库中的名称,例如 cout 和 endl。标准库中的 几乎所有名称都有前缀 std。 名称空间的代码如下所示: 6C++入门经典(第 4 版)namespaceih_space { // // // // } All names declared in here need to be prefixed with ih_space when they are reference from outside. For example, a min() function defined in here would be referred to outside this namespace as ih_space::min()花括号对中的所有内容都位于 ih_space 名称空间中。 警告: main()函数不能定义在名称空间中,未在名称空间中定义的内容都存在于全局名称空 间中,全局名称空间没有名称。1.2.8名称和关键字Ex1_01.cpp 包含变量 answer 的定义, 并使用在 iostream 标准库头文件中定义的名称 cout 和 endl。程序中的许多元素都需要名称,定义名称的规则如下: ● 名称可以是包含大小写拉丁字母 A~Z 和 a~z、 数字 0~9 和下划线_的任意序列。 ● 名称必须以字母或下划线开头。 ● 名称是区分大小写的。 名称可以用下划线开头,但最好不要使用这样的名称,它们可能与 C++标准库中的 名称冲突,因为 C++标准库以这种方式定义了大量的名称。C++标准还允许名称有任意 长度,但有的编译器对此有某种长度限制,这个限制常常比较宽,不是一个严格的限制。 大多数情况下,不需要使用长度超过 12~15 个字符的名称。 下面是一些有效的 C++名称:toe_count shoeSize Box democrat Democrat number1 x2 y2 pValue out_of_range大小写字母是有区别的,所以 democrat 和 Democrat 是不同的名称。编写由两个或 多个单词组成的名称时,有几个约定;可以把第二个及以后各个单词的首字母大写,或 者用下划线分隔它们。 关键字是 C++ 中有特殊含义的保留字,不能把它们用于其他目的。例如, class、 double、throw 和 catch 都是保留字。1.3 类和对象类是定义数据类型的代码块。 类的名称就是类型的名称。 类类型的数据项称为对象。 创建变量,以存储自定义数据类型的对象时,就要使用类类型名称。定义自己的数据类 型,就可以根据具体的问题提出解决方案。例如,如果编写一个处理学生信息的程序, 就可以定义 Student 类型。Student 类型可以包含学生的所有特征,例如年龄、性别或学 第1章 ■ 基 本 概 念7校记录――这些都是程序需要的。1.4 模板有时程序需要几个类似的类或函数,其代码中只有所处理的数据类型有区别。编译 器可以使用模板给特定的自定义类型自动生成类或函数的代码。编译器使用类模板会生 成一个或多个类系列,使用函数模板会生成函数。每个模板都有名称,希望编译器创建 模板的实例时,就会使用该名称。标准库大量使用了模板。1.5 程序文件C++代码存储在两种文件中。源文件包含函数,因此包含程序中的所有可执行代码。 源文件的名称通常使用扩展名.cpp,但也使用其他扩展名,例如.cc。头文件包含元素的 定义,例如.cpp 文件中的可执行代码使用的类和模板。头文件的名称通常使用.h 扩展名, 但也使用其他扩展名,例如.hpp。当然,实际的程序一般包含其他类型的文件,这些文 件包含的内容与 C++无关,例如定义图形用户界面(GUI)的资源。1.6 标准库如果每次编写程序时,都从头开始创建所有内容,就是一件非常枯燥的工作。许多 程序都需要相同的功能,例如从键盘上读取数据,计算平方根,将数据记录按特定的顺 序排列。C++附带大量预先编写好的代码,提供了所有这些功能,因此不需要自己编写 它们。这些标准代码都在标准库中定义。标准库的一个子集是标准模板库(STL),它包含 大量的类模板,用于创建类型,以组织和管理数据。标准库还包含许多函数模板,用于 执行各种操作,例如排序和搜索数据集合,进行数值处理等。本书将学习 STL 的几个功 能,但全面讨论 STL 需要一整本书的篇幅。Beginning STL 是本书的后续产品,专门介 绍 STL。1.7 代码的表示样式代码排列的方式对代码的可读性有非常重要的影响。这有两种基本的方式。首先, 可以使用制表符和/或空格缩进程序语句,显示出这些语句的逻辑;再以一致的方式使用 定义程序块的匹配花括号,使块之间的关系更清晰。其次,可以把一个语句放在两行或 多行上,提高程序的可读性。安排匹配花括号和缩进语句的约定称为表示样式。 代码有许多不同的表示样式。表 1-1 显示了三种常用的代码表示样式。 8C++入门经典(第 4 版)表 1-1 三种常用的代码表示样式 样 式 1 namespace mine { bool has_factor(int x, int y) { int f{ hcf(x, y) }; if (f & 1) { } else { } } } 样 式 2 namespace mine{ bool has_factor(int x, int y) { int f{ hcf(x, y) }; if (f & 1) { } else { } } } 样 式 3 namespace mine{ bool has_factor(int x, int y) { int f{ hcf(x, y) }; if (f & 1){ } else{ } } }本书的示例使用样式 1。1.8 创建可执行文件从 C++源代码中创建可执行的模块需要两步骤。第一步是编译器把每个.cpp 文件转 换为对象文件,其中包含了与源文件内容对应的机器码。第二步是链接程序把程序的对 象文件合并到包含完整可执行程序的文件中。在这个过程中,链接程序会集成程序使用 的标准库函数。 图 1-2 表明,3 个源文件经过编译后,生成 3 个对应的对象文件。用于标识对象文 件的文件扩展名在不同的机器环境上是不同的,这里没有显示。组成程序的源文件可以 在不同的编译器运行期间单独编译,但大多数编译器都允许在一次运行期间编译它们。 无论采用哪种方式,编译器都把每个源文件看作一个独立的实体,为每个.cpp 文件生成 一个对象文件。然后在链接步骤中,把程序的对象文件和必要的库函数组合到一个可执 行文件中。 实际上,编译是一个迭代的过程,因为在源代码中总是会有输入错误或其他错误。 更正了每个源文件中的这些错误后,就可以进入链接步骤,但在这一步可能会发现有更 多的错误!即使链接步骤生成了可执行模块,程序仍有可能包含逻辑错误,即程序没有 生成希望的结果。为了更正这些错误,必须回过头来修改源代码,再编译。这个过程会 继续下去,直到程序按照希望的那样执行为止。如果程序的执行结果不像我们宣称的那 样,其他人就有可能找到我们本应发现的许多错误,这是毋庸置疑的。一般说来,如果 程序非常大,就总是包含错误。 第1章 ■ 基 本 概 念9在编译前把头文件 的内容包含进来源文件 (.cpp 文件)源文件 (.cpp 文件)源文件 (.cpp 文件)每个.cpp 文件都生 成一个对象文件编译器编译器编译器对象文件 (机器码)对象文件 (机器码)对象文件 (机器码)链接程序把所有的 对象文件和必要的 库例程组合起来, 生成可执行文件库函数链接程序可执行文件 (.exe 文件)图 1-2编译和链接过程1.9 表示数字在 C++程序中,数字的表示有许多方式,所以必须理解表示数字的各种可能性。如 果很熟悉二进制数、十六进制数和浮点数的表示,就可以跳过本节。1.9.1二进制数首先考虑一下常见的十进制数(如 324 或 911)表示什么。显然,324 是表示三百二十 四,911 表示九百一十一。这是“三百”加上“二十”再加上“四”的简写形式,或者 是“九百”加上“一十”再加上“一”的简写形式。更明确地说,这两个数表示:324 是:3×102+2×101+4×100,也就是 3×10×10+2×10+4 911 是:9×102+1×101+1×100,也就是 9×10×10+1×10+1这称为十进制表示法,因为这是建立在 10 的幂的基础之上。也可以说,这里的数字 以 10 为基底来表示,因为每个数位都是 10 的幂。以这种方式表示数值非常方便,因为 人有 10 根手指或 10 根脚趾或者 10 个任何类型的附属物。 但是, 这对 PC 就不太方便了, 因为 PC 主要以开关为基础,即开和关,加起来只有 2,而不是 10。这就是计算机用基 数 2 而不是用基数 10 来表示数值的主要原因。 用基数 2 来表示数值称为二进制计数系统。 用基数 10 表示数字,数字可以是 0~9。一般情况下,以任意 n 为基底来表示的数,每 10C++入门经典(第 4 版)个数位的数字是从 0 到 n-1。 二进制数字只能是 0 或 1,二进制数 1101 就可以分解为:1×23+1×22+0×21+1×20,也就是 1×2×2×2+1×2×2+0×2+1计算得 13(十进制系统)。在表 1-2 中,列出了用 8 个二进制数字表示的对应的十进 制值(二进制数字常常称为位)。表 1-2 二 进 制 0 1 2 … 16 17 … 124 125 126 127 十 进 与 8 位二进制值对应的十进制值 制 二 进 制 128 129 130 … 144 145 … 252 253 254 255 十 进 制00 10 … 01 0001 … 11 10 00 10 … 01 0001 … 11 10 使用前 7 位可以表示从 0~127 的数, 一共 128 个不同的数, 使用全部 8 位可以表示 8 n 256(即 2 )个数。一般情况下,如果有 n 位,就可以表示 2 个整数,其正值从 0 到 2n-1。 在计算机中,二进制数相加是非常容易的,因为对应数字加起来的进位只能是 0 或 1,所以处理过程会非常简单。图 1-3 中的例子演示了两个 8 位二进制数相加的过程。二进制 十进制进位图 1-3 二进制数的相加相加操作从最右边开始,将操作数中对应的位相加。图 1-3 指出,前 6 位都向左边 的下一位进 1,这是因为每个数字只能是 0 或 1。计算 1+1 时,结果不能存储在当前的位 中,而需要在左边的下一位中加 1。 第1章 ■ 基 本 概 念111.9.2十六进制数在处理很大的二进制数时,就会有一个小问题。如:11 01在实际应用中, 二进制表示法显得比较烦琐, 如果把这个二进制数表示为十进制数, 结果为 16,103,905,这个 8 位的十进制数没有什么价值。还可以用更长的二进制位数表 示更大的十进制数。显然,我们需要一种更高效的方式来表示这个数,但十进制并不总 是合适的。有时需要能够指定从右开始算起的第 10 位和第 24 位的数字为 1。用十进制 整数来完成这个任务是非常麻烦的,而且很容易出现计算错误。比较简单的解决方案是 使用十六进制表示法,即数字以 16 为基数表示。 基数为 16 的算术就方便得多, 它与二进制也相得益彰。 每个十六进制的数字可以是 从 0~15 的值(从 10 到 15 的数字用 A 到 F 或 a 到 f 表示, 如表 1-3 所示), 从 0~15 的数 值就分别对应于用 4 个二进制数字表示的值。表 1-3 十六进制数表示为十进制数和二进制数 十 六 进 制 0 1 2 3 4 5 6 7 8 9 A或a B或b C或c D或d E或e F或f 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 十 进 制 10 01 00 11 10 1111 二 进 制因为一个十六进制数对应于 4 个二进制数,所以可以把较大的二进制数表示为一个 十六进制数,方法是从右开始,把每 4 个二进制数字组成一组,再用对应的十六进制数 表示每个组。例如,二进制数: 12C++入门经典(第 4 版)11 01如果依次提取每 4 个二进制数字,用对应的十六进制数表示每个组,这个数字用十 六进制表示,就得到:F 5 B 9 E 1所得的 6 个十六进制数字分别对应于 6 组 4 个二进制数字。为了证明这适用于所有 的情况,下面用十进制表示法把这个数值直接从十六进制转换为十进制。 这个十六进制数的计算如下。F5B9E1 转换为十进制值:15 × 165 + 5 × 164 + 11 × 163 + 9 × 162 + 14 × 161 + 1 × 160计算得:15,728,640 + 327,680 + 45,056 + 2,304 + 224 + 1最后相加的结果与把二进制数转换为十进制数的结果相同:16,103,905。在 C++中, 十六进制数要加上前缀 0x 或 0X, 所以在代码中, 这个值写作 0xF5B9E1。 显然, 这表示, 99 与 0x99 不相等。 十六进制数的另一个非常方便的特点是, 现代计算机把整数存储在偶数字节的字中, 一般是 2、4、8 或 16 字节,一个字节是 8 位,正好是两个十六进制数字,所以内存中的 任意二进制整数总是精确对应于若干个十六进制数字。1.9.3负的二进制数二进制算术需要理解的另一个方面是负数。前面一直假定所有的数字都是正的。从 乐观的角度来看是这样,所以我们目前已对二进制数有了一半的认识。但在实际中还会 遇到负数,从悲观的角度来看,我们对二进制数的认识仅仅是一半。在计算机中,是如 何表示负数的?我们只能按照自己的意愿来处理二进制数字,所以解决方案必须是使用 其中的一个二进制数字。 对于允许是负数的数值(称为带符号的数值),必须先确定一个固定的长度(换言之, 就是二进制数字的位数),再把最左边的二进制数字设置为符号位。必须固定位数,这样 才能避免符号位与其他位的混淆。 因为计算机的内存由 8 位字节组成,所以二进制数字要存储在多个 8 位中(通常是 2 的幂),即有些数字是 8 位,有些数字是 16 位,有些数字是 32 位等。只要知道每个数值 的位数,就可以找到符号位,它应是最左边的那一位。如果符号位是 0,该数值就是正 的,如果它是 1,该数值就是负的。 似乎这就解决了问题,但实际上并非如此。每个数字都是由一个符号位和给定的位 数组成,其中符号位为 0 表示正数,符号位为 1 表示负数,给定的位数指定数字的绝对 值,换言之,就是无符号的数字。把+6 改为C6 只需要把符号位从 0 改为 1。但是,这个 表示方法需要大量的系统开销,而且系统开销的大小取决于对这个数字表示方法执行的 算术运算的复杂程度。因此,大多数计算机都采用了另一种方法。 第1章 ■ 基 本 概 念13理想情况下,当两个整数相加时,计算机不应不检查两个数字是否为负。我们希望 使用常规的“加”来生成相应的结果,而不考虑操作数的符号。相加操作把相应的二进 制数字合并在一起,得到相应的位,在必要时把 1 进到下一位上。如果把二进制的C8 加 到+12 上,答案就应是+4。如果把+3 加到+8 上,也采用相同的方式操作。 如果用简化的解决方案来执行这一操作,也就是把正数的符号位设置为 1,使它变 成负数,再执行算术运算,并进行常规的进位,答案就是错误的: 12 转换为二进制:
C8 转换为二进制:
如果把它们加起来,结果是: 。 答案是C20,这可不是我们希望的结果+4,它的二进制应是 。此时读者会 认为, “没有把符号作为另一个位” 。但这里就是这么做的。 下面看看计算机如何表示C8,即从+4 中减去+12,得到正确的结果: +4 转换为二进制:
+12 转换为二进制:
从+4 中减去+12,结果是: 。 对于右边的 4 位数字,必须借 1,才能进行减法,这正是我们在执行十进制算术时 所进行的操作。结果就是C8,即使它看起来不是C8,但其值的确是C8。再用二进制把该 值与+12 或+15 相加,就会得到正确的结果。当然,如果想得到C8,总是可以从 0 中减 去+8。 在从 4 中减去 12 或从 0 中减去+8 时,究竟进行了什么操作?实际上是对负二进制 数值采用了 2 的补码形式。通过一个可以在头脑中进行的简单过程就可以从任何正的二 进制数中获得这种形式。这里需要做一个约定,以避免解释它为什么有效。下面看看如 何从正数中构建负数的 2 的补码形式,读者也可以自己证明这是有效的。现在回到前面 的例子,给C8 构建 2 的补码形式。 首先把+8 转换为二进制:现在反转每个二进制数字,即把 0 变成 1,把 1 变成 0:这称为 1 的补码形式。如果给这个数加上 1,就得到了 2 的补码形式:这就是从+4 中减去+12,得到的C8 的二进制表示。为了确保正确,下面对C8 和+12 进行正常的相加操作: +12 转换为二进制
C8 转换为二进制
把这两个数加在一起,得到:
答案就是 4。这是正确的。左边所有的 1 都向前进位,这样该位的数字就是 0。最左 14C++入门经典(第 4 版)边的数字应进位到第 9 位,即第 9 位应是 1,但这里不必担心这个第 9 位,因为在前面 计算-8 时从前面借了一位,在此正好抵消。实际上,这里做了一个假定,符号位 1 或 0 永远放在最左边。读者可以自己试验几个例子,就会发现这种方法总是有效的。最妙的 是,使用负数的 2 的补码形式使计算机上的算术计算非常简单快速。1.9.4八进制数八进制整数是用以 8 为基底来表示的数。八进制的数字是 0 到 7。目前八进制数很 少使用。计算机内存用 36 位字来衡量时,八进制数很有用,因为可以把 36 位的二进制 值指定为 12 个八进制数字。 这是很久以前的情形了, 为什么还要介绍八进制?因为它可 能会引起潜在的冲突。在 C++中仍可以编写八进制的常量。八进制值有一个前导 0,所 以 76 是十进制值,076 是八进制值,它对应十进制的 64。下面是一条黄金规则: 注意: 不要在十进制整数的前面加上前导 0,否则会得到另一个值,或者得到编译器的一 个错误消息。1.9.5 Big-Endian 和 Little-Endian 系统整数在内存的一系列连续字节中存储为二进制值, 通常存储为 2、 4、 8 或 16 个字节。 字节采用什么顺序是非常重要的。 把十进制数 262657 存储为 4 字节二进制值。选择这个值是因为它的二进制值是00 10 每个字节的位模式都很容易与其他字节区分开来。如果使用 Inter 处理器的 PC,该 数字就存储为: 字节地址: 00 01 02 03 数据位: 00 00
可以看出, 值中最重要的 8 位是都为 0 的那些位, 它们都存储在地址最高的字节中, 换言之,就是最右边的字节。最不重要的 8 位存储在地址最低的字节中,即最左边的字 节中。这种安排形式称为 Little-Endian。 如果使用基于 Motorola 处理器的机器,该数值在内存中存储为: 字节地址: 00 01 02 03 数据位: 00 10
字节现在的顺序是反的,最重要的 8 位存储在最左边的字节中,即地址最低的字节 中。这种安排形式称为 Big-Endian。最新的一些处理器,例如 SPARC 和 Power-PC 处理 器是双向优先的,这表示数据的字节顺序可以在 Big-Endian 和 Little-Endian 之间切换。 注意: 无论字节顺序是 Big-Endian 还是 Little-Endian,在每个字节中,最重要的位都放在 第1章 ■ 基 本 概 念15左边,最不重要的位都放在右边。 这非常有趣,但为什么这很重要?在大多数情况下,这并不重要。即使不知道执行 代码的计算机是采用 Big-Endian 还是 Little-Endian,都可以编写出有效的 C++程序。但 是,在处理来自另一台机器的二进制数据值,这就很重要了。二进制数据会写入文件或 通过网络传送为一系列字节,此时必须解释它们。如果数据源所在的机器使用的 Endian 形式与运行代码的机器不同,就必须反转每个二进制值的字节顺序,否则就会出错。 对 于 那 些 了 解 一 些 有 趣 背 景 信 息 的 读 者 来 说 , 大 概 知 道 术 语 Big-Endian 和 Little-Endian 取自 Jonathan Swift 撰著的《格利佛游记》 。Lilliput 的国王命令所有的国民 必须在鸡蛋的小端磕破鸡蛋。这是因为国王的儿子按照在鸡蛋的大端磕破鸡蛋的传统方 式玩耍时,划破了自己的手指。守法的普通 Lilliput 国民称为 Little-Endian,Lilliputian 王国中一些坚持在鸡蛋的大端磕破鸡蛋的反传统主义者就是 Big-Endian。许多人因此被 判死刑。1.9.6浮点数我们常常要处理非常大的数――例如,宇宙中的质子数,它需要 79 位十进制数字。 显然在许多情况下,要处理的数都不仅仅是 4 字节二进制数能表示的 10 位十进制数字。 同样,还有许多非常小的数,例如,汽车销售人员接受你对 2001 本田汽车(它仅行驶了 480 000 英里)的报价所需的分钟数。处理这两种数值的机制就称为浮点数。 在十进制记数法中,数值的浮点表示是带有两个部分的小数值。其中一部分称为尾 数,它大于等于 0.9,小于 1.0,且有固定的位数。另一部分称为指数,浮点数的值是尾 数乘以 10 的指数幂。演示这种表示法要比描述它更容易,所以下面看一些例子。在普通 的十进制计数法中,365 写为浮点数的形式为:0.E 表示“指数” ,尾数部分 0.3650000 乘以 10 的幂,就得到需要的值。即:0.3650000 × 10 × 10 × 10这显然是 365。 这里的尾数有 7 位小数。浮点数中的位数精度取决于给它分配的内存。4 字节的单 精度浮点数一般能提供约 7 位小数的精度。这里说“大约” ,是因为在计算机中,这些数 使用二进制的浮点数形式,23 位的二进制小数不会精确地对应于有 7 位小数位数的十进 制小数。双精度浮点数一般对应于约 15 位小数的精度。 下面看一个小数值:0.它计算为.365×10-4,即.0000365。 假定有一个数 2 134 311 179,表示为单精度浮点数: 16C++入门经典(第 4 版)0.它们不完全相同,因为丢弃了 3 个低位数字,初始值就表示为 2 134 311 000。这是 处理如此大范围的数所付出的代价,这个范围是 10-38 到 10+38(正数和负数)。它们称为浮 点数,其原因非常明显:小数点是浮动的,其位置取决于指数值。 除了由于保证精度而带来的固定精度限制之外,还有一个方面要注意。对不同量级 的数执行相加或相减操作时要特别小心。这个问题可以用一个简单的例子来说明。 把.365E-3 和.365E+7 加起来,写成十进制的形式:0.000365 + 3,650,000.0结果如下:3,650,000.000365转换为带 7 位小数的浮点数时,得到:0.把.365E-3 和.365E+7 加起来,似乎没有什么效果,所以不必担心它。但问题在于结 果只有 6 位或 7 位小数精度。大数的所有位数都不会受到小数的影响,因为大数的所有 有效位数都离小数的有效位数太远了。可笑的是,当两个数几乎相等时,也必须特别小 心。如果计算这两个数之差,结果可能只有一两位小数精度。在这种情况下,很容易计 算两个完全是垃圾的值。浮点数可以执行没有它们就不可能执行的计算,但要确保结果 有效,就必须记住它们的限制。这意味着考虑要处理的值域及其相对值。1.10 表示字符计算机中的数据没有内在的含义。机器码指令只是数字,当然数值就是数值,字符 也是数值。每个字符都获得了一个独特的整数值,称为代码或代码点。值 42 可以是 6 周的天数、生活、宇宙和其他事务的答案,也可以是星号字符。这取决于如何解释它。 在 C++中, 单个字符可以放在单引号中, 例如'a' 、 ‘?’ 、 ‘*’ , 编译器会给它们生成代码值。1.10.1ASCII 码20 世纪 60 年代,人们定义了美国信息交换标准码(ASCII),来表示字符。这是一个 7 位代码,所以共有 128 个不同的代码值。ASCII 值 0 到 31 表示各种非打印控制符,例 如回车符(代码 15)和换页符(代码 12)。代码值 65 到 90 是大写字母 A 到 Z,代码值 141 到 172 对应小写字母 a 到 z。如果查看字母的代码值的对应二进制值,就会发现大小写 字母的代码仅在第 6 位上有区别:小写字母的第 6 位是 0,大写字母的第 6 位是 1。其他 代码表示数字 0 到 9、标点符号和其他字符。这适合于美国人或英国人,但法国人或德 国人在文本中需要重音和元音变音,但它们没有包含在 7 位 ASCII 中。 第1章 ■ 基 本 概 念17为了克服 7 位代码的局限,人们定义了 ASCII 的扩展版本,它有 8 位代码。代码值 0 到 127 与 7 位 ASCII 版本表示相同的字符,代码值 128 到 255 是可变的。8 位 ASCII 的一个变体称为 Latin-1,它提供了大多数欧洲语言的字符,以及俄语等语言中的字符。 当然,对于韩国人、日本人或阿拉伯人而言,8 位 ASCII 肯定是不够的。为了克服扩展 ASCII 的局限,20 世纪 90 年代出现了通用字符集(Universal Character Set,UCS),UCS 由标准 ISO 10646 定义,其代码有 32 位,提供了超过 20 亿个不同的代码值。1.10.2 UCS 和 UnicodeUCS 定义了字符和整数代码值(称为代码点)之间的映射。代码点与编码是不同的, 认识到这一点很重要:代码点是一个整数,编码指定了把给定的代码点表示为一系列字 节或字的方式。小于 256 的代码值非常常见,可以用 1 个字节表示。使用 4 字节存储只 需要 1 字节的代码值,仅是因为有其他代码需要多个字节,是非常低效的。编码是表示 代码点、允许更高效地存储它们的方式。 Unicode 是一个标准,定义了一组字符及其代码点(与 UCS 相同),代码点值从 0 到 0x10ffff。Unicode 还为这些代码点定义了几个不同的编码,包括其他机制,例如处理从 右向左阅读的语言(如阿拉伯语),代码点的范围足以包含世界上所有语言的字符集,还 包含许多其他的图形化字符,例如数学符号。代码分为 17 个代码段,每段包含 65 536 个代码值。0 段包含 0 到 0xffff 的十六进制代码值,1 段包含 0x10000 到 0x1ffff 的代码, 2 段包含 0x20000 到 0x2ffff 的代码, 以此类推, 17 段包含 0x100000 到 0x10ffff 的代码。 大多数国家语言的字符代码包含在 0 段中,其代码值为 0 到 0xffff。因此,大多数语言的 字符串可以表示为 16 位代码的一个序列。 Unicode 中一个可能混淆的方面是:它提供了多个字符编码方法。最常用的编码是 UTF-8 和 UTF-16,它们不能表示 Unicode 集合中的所有字符。UTF-8 和 UTF-16 之间的 区别是如何表示给定字符的代码点,给定字符的代码数值在这两种表示法中是相同的。 下面是这些编码表示字符的方式: ● UTF-8 把字符表示为长度在 1 字节和 4 字节之间变化的序列。ASCII 字符集在 UTF-8 中表示为单字节代码, 其代码值与 ASCII 相同。 大多数网页都使用 UTF-8 编码文本,0 代码段包含了 UTF-8 中的单字节和双字节代码。 ● UTF-16 把字符表示为一个或两个 16 位值。UTF-16 包括了 UTF-8。因为一个 16 位值包含 0 代码段中的所有代码, 所以 UTF-16 覆盖了多语言编程环境中的大多 数情形。 存储 Unicode 字符有 3 个整数类型:wchar_t、char16_t 和 char32_t。详见第 2 章。1.11C++源字符编写 C++语句要使用基本源字符集, 这些是在 C++源文件中可以显式使用的字符集。 用于定义名称的字符集是上述字符集合的一个子集。当然,基本源字符集并没有限制代 18C++入门经典(第 4 版)码中使用的字符数据。程序可以用各种方式创建没有包含在该字符集中的字符串,基本 源字符集包括下述字符: ● 大小写字母 A~Z 和 a~z ● 数字 0~9 ● 控制字符,如换行符、水平和垂直制表符、换页符 ● 字符_{}[]#()&&%:;.?*+-/^&|~!=,\&' 这很简单、直观。一共可以使用 96 个字符,这些字符可以满足大多数要求。大多数 情况下,基本源字符集足够用了,但偶尔需要使用不包含在基本源集合中的字符。可以 在名称中包含 Unicode 字符。 Unicode 字符指定为码点的十六进制表示 \udddd 或 \Udddddddd(其中 d 是一个十六进制数)。注意第一种形式使用小写 u,第二种形式使用大 写 U,两者都是可接受的。但是,不能以这种方式指定基本源字符集中的字符。另外, 名称中的字符不能是控制字符。字符和字符串数据都可以包含 Unicode 字符。1.11.1三字符序列三字符序列不太常见,但 C++标准允许把某些字符指定为三字符序列。三字符序列 就是用于标识另一个字符的三个字符序列。以前为了表示 C 语言需要的、7 位 ASCII 没 有的字符,这是必不可少的一种方法。表 1-4 列出了以这种方式在 C++中指定的字符。表 1-4 三字符序列的字符 字 符 # [ ] \ { } ^ | ~ 三字符序列 ??= ??( ??) ??/ ??& ??& ??' ??! ??-为了兼容,C++仍支持这些字符。编译器会用对应的字符替代所有三字符序列,再 对源代码进行其他处理。 有几个实例需要注意, 尤其是使用正则表达式功能(本书未介绍) 时。 这是因为出现在正则表达式中的序列可能对应于三字符序列, 但这不是我们希望的。 在大多数情况下,可以忽略三字符序列,尽管有可能不小心指定它们。1.11.2转义序列在程序中使用字符常量时,例如单个字符或字符串,某些字符是会出问题的。显然, 第1章 ■ 基 本 概 念19不能直接把 newline 或 tab 这样的字符输入为字符常量, 因为它们只完成自己该做的工作: 转到新的一行或移动到源代码文件的下一个制表符位置。通过转义序列可以把控制字符 输入为字符常量。转义序列是指定字符的一种间接方式,通常以一个反斜杠\开头。表示 控制字符的转义序列如表 1-5 所示。表 1-5 表示控制字符的转义序列 转 义 序 列 \n \t \v \b \r \f \a 控 制 字 符 换行符 水平制表符 垂直制表符 退格字符 回车字符 换页字符 警告字符还有其他一些字符在直接表示时会出问题。 显然, 表示反斜杠字符本身是很困难的, 因为它表示转义序列的开头。 用作界定符的单引号和双引号, 例如常量‘A’或字符串“text” 也有问题。表 1-6 列出了这些转义序列。表 1-6 用转义序列指定的“问题”字符 转 义 序 列 \\ \' \& \? 字 反斜杠 单引号 双引号 问号 符由于反斜杠表示转义序列的开始,因此把反斜杠字符输入为字符常量的唯一方式是 使用两个连续的反斜杠(\\)。 下面的程序示例使用转义序列输出要显示在屏幕上的消息。要查看该程序的结果, 需要输入、编译、链接和执行下面的程序。// Ex1_02.cpp // Using escape sequences #include &iostream& int main() { std::cout && &\&Least \'said\' \\\n\t\tsoonest \'mended\'.\&& && std:: } 20C++入门经典(第 4 版)在编译、链接和运行这个程序时,会显示如下结果:&Least 'said' \ soonest 'mended'.&所得的输出由语句中双引号之间的内容确定:std::cout && &\&Least \'said\' \\\n\t\tsoonest \'mended\'.\&& && std::原则上,上述语句中外部双引号之间的所有内容都会发送给 cout。双引号之间的字 符串称为字符串字面量。双引号字符表示该字符串字面量的开始和结束;它们不是字符 串的一部分。字符串字面量中的转义序列会被编译器转换为它表示的字符,所以该字符 会发送给 cout,而不是发送转义序列。字符串字面量中的反斜杠总是表示转义序列的开 始,所以发送给 cout 的第一个字符是双引号。 接着输出的是 Least 后跟一个空格,接着是一个单引号、said 和另一个单引号。然后 是一个空格和由\\指定的反斜杠。接着把对应于\n 的换行符写入流,使光标移动到下一 行的开头。然后用\t\t 给 cout 发送两个制表符,让光标向右移动两个制表位置。之后显 示字符串 soonest、一个空格和单引号中的 mended,最后是句号和一个双引号。1.12 过程化编程方法和面向对象编程方法在历史上,过程化编程方法曾是编写几乎所有程序的方式。要创建问题的过程化编 程解决方案,必须考虑程序实现的过程,才能解决问题。一旦需求明确地确定下来,就 可以写出完成任务的大致提纲,如下所示: ● 为程序要实现的整个过程创建一个清晰的高级定义。 ● 将整个过程分为可工作的计算单元, 这些计算单元应尽可能是自包含的。 它们常 常对应于函数。 ● 把每个计算单元的逻辑和工作分解为更详细的动作序列。 这就下降到对应于编程 语言语句的一级。 ● 根据正处理的数据的基本类型:数值数据、单个字符和字符串,来编写函数。 在解决相同问题时,除了开始时对问题进行清晰的说明这一点相同之外,面向对象 的编程方法在其他地方都完全不同: ● 根据问题的详细说明确定该问题所涉及的对象类型。 例如, 如果程序处理的是篮 球运动员,就应把 BaseballPlayer 标识为程序要处理的一个数据类型。如果程序 是一个账户打包程序,就应定义 Account 类型和 Transaction 类型的对象。还要 确定程序需要对每种对象执行的操作集。 这将产生一组与应用程序相关的数据类 型,用于编写程序。 ● 为问题需要的每种新数据类型生成一个详细的设计方案, 包括可以对每种对象类 型执行的操作。 第1章 ■ 基 本 概 念21● 根据已定义的新数据类型及其允许的操作,编写程序的逻辑。 面向对象解决方案的程序代码完全不同于过程化解决方案,理解起来也比较容易, 维护也方便得多。 面向对象解决方案所需的设计时间要比过程化解决方案长一些。 但是, 面向对象程序的编码和测试阶段比较短,问题也比较少,所以这两种方式的整个开发时 间大致相同。 下面简要论述面向对象方式。假定要实现一个处理各种盒子的程序。这个程序的一 个合理要求是把几个小盒子装到另一个大一些的盒子中。在过程化程序中,需要在一组 变量中存储每个盒子的长度、宽度和高度。包含几个盒子的新盒子尺寸必须根据每个被 包含盒子的尺寸,按照为打包一组盒子而定义的规则进行计算。 面向对象的解决方案首先需要定义一个 Box 数据类型,这样就可以创建变量,引用 Box 类型的对象,并创建 Box 对象。然后定义一个操作,把两个 Box 对象加在一起,生 成包含前两个 Box 对象的第三个 Box 对象。使用这个操作,就可以编写如下语句:bigBox = box1 + box2 + box3;在这个语句中,+操作的含义远远超出了简单的相加。+运算符应用于数值,会像以 前那样工作,但应用于 Box 对象时,就有一个特殊的含义。这个语句中每个变量的类型 都是 Box,上述代码会创建一个 Box 对象,其尺寸足够包含 box1、box2 和 box3。 编写像这样的语句要比分别处理所有的尺寸容易得多,计算过程越复杂,面向对象 编程方式的优点就越明显。但这只是一个很粗略的说明,对象的功能要比这里所描述的 强大得多。介绍这个例子的目的是让读者对使用面向对象方法来解决问题有一个大致的 了解。面向对象的编程方法基本上是根据问题涉及的实体来解决问题,而不是根据计算 机喜欢使用的实体,即数字和字符,来解决问题。1.13 本章小结本章简要介绍了 C++的一些基本概念。后面将详细讨论本章提及的内容。本章介绍 的基本概念如下: ● C++程序包含一个或多个函数,其中一个是 main()函数。执行总是从 main()函数 开始。 ● 函数的可执行部分由包含在一对花括号中的语句组成。 ● 一对花括号定义了一个语句块。 ● 语句用分号结束。 ● 关键字是 C++中有特殊含义的一组保留字。程序中的实体不能与 C++语言中的 任何关键字同名。 ● C++程序包含在一个或多个文件中。源文件包含可执行代码,头文件包含可执行 代码使用的定义。 ● 定义函数的代码通常存储在扩展名为.cpp 的源文件中。 ● 源文件使用的定义通常存储在扩展名为.h 的头文件中。 22C++入门经典(第 4 版)● 预处理器指令指定了要对文件中的代码执行的操作。 所有的预处理器指令都在编 译文件中的代码之前执行。 ● 头文件中的代码通过#include 预处理器指令添加到源文件中。 ● 标准库提供了支持和扩展 C++语言的大量功能。 ● 要访问标准库函数和定义,可以把标准库头文件包含到源文件中。 ● 输入和输出是利用流来执行的, 需要使用插入和提取运算符, 即&&和&&。 std::cin 是对应于键盘的标准输入流,std::cout 是把文本写入屏幕的标准输出流。它们都 在标准库头文件 iostream 中定义。 ● 面向对象的编程方式需要定义专用于某问题的新数据类型。 一旦定义好需要的数 据类型,就可以根据这些新数据类型来编写程序。 ● Unicode 定义的独特整数代码值表示世界上几乎所有语言的字符,以及许多专用 的字符集。 代码值也称为代码点。 Unicode 还定义了代码点如何编码为字节序列。1.14 练习1. 创建、编译、链接、执行一个程序,在屏幕上输出文本&Hello World&。 2. 创建并执行一个程序,在一行上输出自己的姓名,在下一行输出年龄。 3. 下面的程序有几处编译错误。 请指出这些错误并更正, 使程序能正确编译并运行。include &iostream& Int main() { std:cout && &Hello World& && std:endl )注释: 本书所有练习的答案都可在 Apress 网站 /source-code/上找到。 第2章基本数据类型本章将介绍每个程序都需要的、C++内置的基本数据类型。C++的面向对象功能全 部建立在这些基本数据类型的基础之上,因为用户创建的所有数据类型最终都是根据计 算机操作的基本数值数据定义的。学习完本章后,读者将能编写传统格式(输入-处理-输 出)的简单 C++程序。 本章主要内容 ● C++中的数据类型 ● 变量的声明和初始化 ● 字面量的含义和定义方式 ● 整数的二进制和十六进制表示 ● 计算的过程 ● 如何固定变量的值 ● 如何创建存储字符的变量 ● auto 关键字的作用 ● lvalue 和 rvalue2.1 变量、数据和数据类型变量是用户定义的一个命名的内存段。每个变量都只存储特定类型的数据。每个变 量都定义了可以存储的数据类型。每个基本类型都用唯一的类型名称(即关键字)来标识。 关键字是 C++中的保留字,不能用于其他目的。 编译器会进行大量的检查,确保在给定的情况下使用正确的数据类型,它还确保在 操作中合并不同的类型(例如将两个值相加)时,它们要么有相同的类型,要么可以把一 个值转换为另一个值的类型,使它们相互兼容。编译器检测并报告尝试把不兼容的数据 类型组合在一起而产生的错误。 数值分为两大类:整型数和浮点数(可以是分数)。在每个大类中,都有几种基本的 C++类型,每种类型都可以存储特定的数值范围。首先介绍整数类型。 24C++入门经典(第 4 版)2.1.1定义整型变量下面的语句定义了一个整型变量:int apple_这个语句定义了一个 int 类型的变量 apple_count,该变量包含某个随机的垃圾值。 在定义变量时,可以而且应该指定初始值,如下所示:int apple_count {15}; // Number of applesapple_count 的初始值放在变量名后面的花括号中,所以其值为 15。包含初始值的花 括号称为初始化列表。在本书后面,初始化列表常常包含几个值。定义变量时不必初始 化它们,但最好进行初始化。确保变量一开始就有已知的值,在代码不像预期的那样工 作时,将便于确定出错的位置。 类型 int 一般有 4 个字节,可以存储从-2,147,483,648 到+2,147,483,647 的整数。 这覆 盖了大多数情形,所以 int 是最常用的整数类型。 下面是 3 个 int 类型的变量定义:int apple_count {15}; int orange_count {5}; int total_fruit {apple_count + orange_count}; // Number of apples // Number of oranges // Total number of fruittotal_fruit 的初始值是前面定义的两个变量值之和。这说明,变量的初始值可以是表 达式。 在源文件中, 定义前述两个变量

我要回帖

更多关于 二进制打印 的文章

 

随机推荐