C++编程思想(两卷合订本)(第1卷 标准C++导引(原书第2版)&第2卷 实用编程技术)
作  者:(美)Bruce Eckel;Chuck Allison
出版日期:2011-07-01
出 版 社:机械工业出版社
ISBN:9787111350217
定 价:116
购买地址:互动网
图书简介 图书目录 阅读 前言 译序
  • 图书简介
    《C++编程思想(两卷合订本)》曾荣获美国《软件开发》杂志评选的1996年Jolt生产力大奖,中文版自2000年推出以来,经久不衰,获得了读者的充分肯定和高度评价。

    《C++编程思想(两卷合订本)》的第1卷是在第1版的基础上进行了更加深入的分析和修改后得到的第2版,其内容更加集中,可以供不同程度的读者选择阅读。本书第2卷介绍了C++实用的编程技术和最佳的实践方法,深入探究了异常处理方法和异常安全设计;介绍C++的字符串、输入输出流的现代用法;解释多重继承问题的难点,描述了典型的设计模式及其实现,特别介绍了多线程处理编程技术。

    《C++编程思想(两卷合订本)》是C++领域内一本权威的著作,书中的内容、讲授方法、练习既适合课堂教学,又适合读者自学。本书适合作为高等院校计算机及相关专业的本科生、研究生的教材,也可供从事软件开发的研究人员和科技工作者参考。
  • 图书目录
    《C++编程思想(两卷合订本)》
    出版者的话
    出版说明
    第1卷 标准C++导引
    译者序 3
    前言 5
    第1章 对象导言 11
    1.1 抽象的过程 11
    1.2 对象有一个接口 12
    1.3 实现的隐藏 14
    1.4 实现的重用 15
    1.5 继承:重用接口 15
    1.5.1 is-a 关系和is-like-a 关系 18
    1.6 具有多态性的可互换对象 18
    1.7 创建和销毁对象 21
    1.8 异常处理:应对错误 22
    1.9 分析和设计 22
    1.9.1 第0阶段:制定计划 24
    1.9.2 第1阶段:我们在做什么 24
    1.9.3 第2阶段:我们将如何建立对象 26
    1.9.4 第3阶段:创建核心 28
    1.9.5 第4阶段:迭代用例 29
    1.9.6 第5阶段:进化 29
    1.9.7 计划的回报 30
    1.10 极限编程 30
    1.10.1 先写测试 31
    1.10.2 结对编程 32
    1.11 为什么C++会成功 32
    1.11.1 一个较好的C 32
    1.11.2 延续式的学习过程 33
    1.11.3 效率 33
    1.11.4 系统更容易表达和理解 33
    1.11.5 尽量使用库 33
    1.11.6 利用模板的源代码重用 34
    1.11.7 错误处理 34
    1.11.8 大型程序设计 34
    1.12 为向OOP转变而采取的策略 34
    1.12.1 指导方针 35
    1.12.2 管理的障碍 35
    1.13 小结 37
    第2章 对象的创建与使用 38
    2.1 语言的翻译过程 38
    2.1.1 解释器 38
    2.1.2 编译器 39
    2.1.3 编译过程 39
    2.2 分段编译工具 40
    2.2.1 声明与定义 40
    2.2.2 连接 44
    2.2.3 使用库文件 44
    2.3 编写第一个C++程序 45
    2.3.1 使用iostream类 45
    2.3.2 名字空间 46
    2.3.3 程序的基本结构 47
    2.3.4 “Hello, World!” 47
    2.3.5 运行编译器 48
    2.4 关于输入输出流 48
    2.4.1 字符数组的拼接 49
    2.4.2 读取输入数据 49
    2.4.3 调用其他程序 50
    2.5 字符串简介 50
    2.6 文件的读写 51
    2.7 vector简介 52
    2.8 小结 55
    2.9 练习 56
    第3章 C++中的C 57
    3.1 创建函数 57
    3.1.1 函数的返回值 58
    3.1.2 使用C的函数库 59
    3.1.3 通过库管理器创建自己的库 59
    3.2 执行控制语句 60
    3.2.1 真和假 60
    3.2.2 if-else语句 60
    3.2.3 while语句 61
    3.2.4 do-while语句 61
    3.2.5 for语句 62
    3.2.6 关键字break 和 continue 63
    3.2.7 switch语句 64
    3.2.8 使用和滥用goto 65
    3.2.9 递归 65
    3.3 运算符简介 66
    3.3.1 优先级 66
    3.3.2 自增和自减 67
    3.4 数据类型简介 67
    3.4.1 基本内建类型 67
    3.4.2 bool类型与true和false 68
    3.4.3 说明符 69
    3.4.4 指针简介 70
    3.4.5 修改外部对象 72
    3.4.6 C++引用简介 74
    3.4.7 用指针和引用作为修饰符 75
    3.5 作用域 76
    3.5.1 实时定义变量 77
    3.6 指定存储空间分配 78
    3.6.1 全局变量 78
    3.6.2 局部变量 79
    3.6.3 静态变量 80
    3.6.4 外部变量 81
    3.6.5 常量 82
    3.6.6 volatile变量 83
    3.7 运算符及其使用 83
    3.7.1 赋值 83
    3.7.2 数学运算符 83
    3.7.3 关系运算符 85
    3.7.4 逻辑运算符 85
    3.7.5 位运算符 85
    3.7.6 移位运算符 86
    3.7.7 一元运算符 88
    3.7.8 三元运算符 88
    3.7.9 逗号运算符 89
    3.7.10 使用运算符时的常见问题 89
    3.7.11 转换运算符 90
    3.7.12 C++的显式转换 90
    3.7.13 sizeof―独立运算符 93
    3.7.14 asm 关键字 94
    3.7.15 显式运算符 94
    3.8 创建复合类型 94
    3.8.1 用typedef命名别名 95
    3.8.2 用struct把变量结合在一起 95
    3.8.3 用enum提高程度清晰度 97
    3.8.4 用union节省内存 98
    3.8.5 数组 99
    3.9 调试技巧 106
    3.9.1 调试标记 106
    3.9.2 把变量和表达式转换成字符串 108
    3.9.3 C语言assert( )宏 108
    3.10 函数地址 109
    3.10.1 定义函数指针 109
    3.10.2 复杂的声明和定义 109
    3.10.3 使用函数指针 110
    3.10.4 指向函数的指针数组 111
    3.11 make:管理分段编译 111
    3.11.1 make的行为 112
    3.11.2 本书中的makefile 114
    3.11.3 makefile的一个例子 114
    3.12 小结 116
    3.13 练习 116
    第4章 数据抽象 119
    4.1 一个袖珍C库 119
    4.1.1 动态存储分配 122
    4.1.2 有害的猜测 124
    4.2 哪儿出问题 125
    4.3 基本对象 126
    4.4 什么是对象 130
    4.5 抽象数据类型 131
    4.6 对象细节 131
    4.7 头文件形式 132
    4.7.1 头文件的重要性 132
    4.7.2 多次声明问题 133
    4.7.3 预处理器指示#define、#ifdef
    和#endif 134
    4.7.4 头文件的标准 134
    4.7.5 头文件中的名字空间 135
    4.7.6 在项目中使用头文件 135
    4.8 嵌套结构 136
    4.8.1 全局作用域解析 138
    4.9 小结 139
    4.10 练习 139
    第5章 隐藏实现 142
    5.1 设置限制 142
    5.2 C++的访问控制 142
    5.2.1 protected说明符 144
    5.3 友元 144
    5.3.1 嵌套友元 146
    5.3.2 它是纯面向对象的吗 148
    5.4 对象布局 148
    5.5 类 149
    5.5.1 用访问控制来修改Stash 151
    5.5.2 用访问控制来修改Stack 151
    5.6 句柄类 152
    5.6.1 隐藏实现 152
    5.6.2 减少重复编译 152
    5.7 小结 154
    5.8 练习 154
    第6章 初始化与清除 156
    6.1 用构造函数确保初始化 156
    6.2 用析构函数确保清除 157
    6.3 清除定义块 159
    6.3.1 for循环 160
    6.3.2 内存分配 161
    6.4 带有构造函数和析构函数的Stash 162
    6.5 带有构造函数和析构函数的Stack 164
    6.6 聚合初始化 166
    6.7 默认构造函数 168
    6.8 小结 169
    6.9 练习 169
    第7章 函数重载与默认参数 171
    7.1 名字修饰 172
    7.1.1 用返回值重载 172
    7.1.2 类型安全连接 172
    7.2 重载的例子 173
    7.3 联合 176
    7.4 默认参数 178
    7.4.1 占位符参数 179
    7.5 选择重载还是默认参数 180
    7.6 小结 183
    7.7 练习 183
    第8章 常量 185
    8.1 值替代 185
    8.1.1 头文件里的const 186
    8.1.2 const的安全性 186
    8.1.3 聚合 187
    8.1.4 与C语言的区别 187
    8.2 指针 188
    8.2.1 指向const的指针 189
    8.2.2 const指针 189
    8.2.3 赋值和类型检查 190
    8.3 函数参数和返回值 191
    8.3.1 传递const值 191
    8.3.2 返回const值 191
    8.3.3 传递和返回地址 193
    8.4 类 195
    8.4.1 类里的const 196
    8.4.2 编译期间类里的常量 198
    8.4.3 const对象和成员函数 200
    8.5 volatile 204
    8.6 小结 205
    8.7 练习 205
    第9章 内联函数 207
    9.1 预处理器的缺陷 207
    9.1.1 宏和访问 209
    9.2 内联函数 210
    9.2.1 类内部的内联函数 210
    9.2.2 访问函数 211
    9.3 带内联函数的Stash和Stack 215
    9.4 内联函数和编译器 218
    9.4.1 限制 219
    9.4.2 向前引用 219
    9.4.3 在构造函数和析构函数里隐藏行为 220
    9.5 减少混乱 220
    9.6 预处理器的更多特征 221
    9.6.1 标志粘贴 222
    9.7 改进的错误检查 222
    9.8 小结 225
    9.9 练习 225
    第10章 名字控制 227
    10.1 来自C语言中的静态元素 227
    10.1.1 函数内部的静态变量 227
    10.1.2 控制连接 230
    10.1.3 其他存储类型说明符 232
    10.2 名字空间 232
    10.2.1 创建一个名字空间 232
    10.2.2 使用名字空间 234
    10.2.3 名字空间的使用 237
    10.3 C++中的静态成员 238
    10.3.1 定义静态数据成员的存储 238
    10.3.2 嵌套类和局部类 241
    10.3.3 静态成员函数 242
    10.4 静态初始化的相依性 244
    10.4.1 怎么办 245
    10.5 替代连接说明 250
    10.6 小结 250
    10.7 练习 251
    第11章 引用和拷贝构造函数 254
    11.1 C++中的指针 254
    11.2 C++中的引用 254
    11.2.1 函数中的引用 255
    11.2.2 参数传递准则 257
    11.3 拷贝构造函数 257
    11.3.1 按值传递和返回 257
    11.3.2 拷贝构造函数 261
    11.3.3 默认拷贝构造函数 265
    11.3.4 替代拷贝构造函数的方法 266
    11.4 指向成员的指针 267
    11.4.1 函数 269
    11.5 小结 271
    11.6 练习 271
    第12章 运算符重载 274
    12.1 两个极端 274
    12.2 语法 274
    12.3 可重载的运算符 275
    12.3.1 一元运算符 276
    12.3.2 二元运算符 279
    12.3.3 参数和返回值 288
    12.3.4 不常用的运算符 290
    12.3.5 不能重载的运算符 295
    12.4 非成员运算符 296
    12.4.1 基本方针 297
    12.5 重载赋值符 297
    12.5.1 operator=的行为 298
    12.6 自动类型转换 306
    12.6.1 构造函数转换 306
    12.6.2 运算符转换 307
    12.6.3 类型转换例子 309
    12.6.4 自动类型转换的缺陷 310
    12.7 小结 312
    12.8 练习 312
    第13章 动态对象创建 315
    13.1 对象创建 315
    13.1.1 C从堆中获取存储单元的方法 316
    13.1.2 operator new 317
    13.1.3 operator delete 317
    13.1.4 一个简单的例子 318
    13.1.5 内存管理的开销 318
    13.2 重新设计前面的例子 319
    13.2.1 使用delete void*可能会出错 319
    13.2.2 对指针的清除责任 320
    13.2.3 指针的Stash 320
    13.3 用于数组的new和delete 324
    13.3.1 使指针更像数组 325
    13.4 耗尽内存 325
    13.5 重载new和delete 326
    13.5.1 重载全局new和delete 327
    13.5.2 对于一个类重载new和delete 328
    13.5.3 为数组重载new和delete 330
    13.5.4 构造函数调用 332
    13.5.5 定位new和delete 333
    13.6 小结 334
    13.7 练习 334
    第14章 继承和组合 336
    14.1 组合语法 336
    14.2 继承语法 337
    14.3 构造函数的初始化表达式表 339
    14.3.1 成员对象初始化 339
    14.3.2 在初始化表达式表中的内建类型 339
    14.4 组合和继承的联合 340
    14.4.1 构造函数和析构函数调用的次序 341
    14.5 名字隐藏 343
    14.6 非自动继承的函数 346
    14.6.1 继承和静态成员函数 349
    14.7 组合与继承的选择 349
    14.7.1 子类型设置 350
    14.7.2 私有继承 352
    14.8 protected 353
    14.8.1 protected继承 353
    14.9 运算符的重载与继承 353
    14.10 多重继承 355
    14.11 渐增式开发 355
    14.12 向上类型转换 356
    14.12.1 为什么要“向上类型转换” 357
    14.12.2 向上类型转换和拷贝构造函数 357
    14.12.3 组合与继承(再论) 359
    14.12.4 指针和引用的向上类型转换 360
    14.12.5 危机 360
    14.13 小结 361
    14.14 练习 361
    第15章 多态性和虚函数 364
    15.1 C++程序员的演变 364
    15.2 向上类型转换 365
    15.3 问题 366
    15.3.1 函数调用捆绑 366
    15.4 虚函数 366
    15.4.1 扩展性 367
    15.5 C++如何实现晚捆绑 369
    15.5.1 存放类型信息 370
    15.5.2 虚函数功能图示 371
    15.5.3 撩开面纱 372
    15.5.4 安装vpointer 373
    15.5.5 对象是不同的 373
    15.6 为什么需要虚函数 374
    15.7 抽象基类和纯虚函数 375
    15.7.1 纯虚定义 378
    15.8 继承和VTABLE 378
    15.8.1 对象切片 380
    15.9 重载和重新定义 382
    15.9.1 变量返回类型 383
    15.10 虚函数和构造函数 385
    15.10.1 构造函数调用次序 385
    15.10.2 虚函数在构造函数中的行为 386
    15.11 析构函数和虚拟析构函数 386
    15.11.1 纯虚析构函数 388
    15.11.2 析构函数中的虚机制 389
    15.11.3 创建基于对象的继承 390
    15.12 运算符重载 392
    15.13 向下类型转换 394
    15.14 小结 396
    15.15 练习 397
    第16章 模板介绍 400
    16.1 容器 400
    16.1.1 容器的需求 401
    16.2 模板综述 402
    16.2.1 模板方法 403
    16.3 模板语法 404
    16.3.1 非内联函数定义 405
    16.3.2 作为模板的IntStack 406
    16.3.3 模板中的常量 408
    16.4 作为模板的Stash和Stack 409
    16.4.1 模板化的指针Stash 411
    16.5 打开和关闭所有权 415
    16.6 以值存放对象 417
    16.7 迭代器简介 418
    16.7.1 带有迭代器的栈 425
    16.7.2 带有迭代器的PStash 427
    16.8 为什么使用迭代器 432
    16.8.1 函数模板 434
    16.9 小结 435
    16.10 练习 435
    附录A 编码风格
    附录B 编程准则
    附录C 推荐读物
    第2卷 实用编程技术
    译者序 441
    前言 442
    第一部分 建立稳定的系统
    第1章 异常处理 448
    1.1 传统的错误处理 448
    1.2 抛出异常 450
    1.3 捕获异常 451
    1.3.1 try块 451
    1.3.2 异常处理器 451
    1.3.3 终止和恢复 452
    1.4 异常匹配 453
    1.4.1 捕获所有异常 454
    1.4.2 重新抛出异常 454
    1.4.3 不捕获异常 455
    1.5 清理 456
    1.5.1 资源管理 457
    1.5.2 使所有事物都成为对象 458
    1.5.3 auto_ptr 460
    1.5.4 函数级的try块 461
    1.6 标准异常 462
    1.7 异常规格说明 464
    1.7.1 更好的异常规格说明 467
    1.7.2 异常规格说明和继承 467
    1.7.3 什么时候不使用异常规格说明 468
    1.8 异常安全 468
    1.9 在编程中使用异常 471
    1.9.1 什么时候避免异常 471
    1.9.2 异常的典型应用 472
    1.10 使用异常造成的开销 474
    1.11 小结 476
    1.12 练习 476
    第2章 防御性编程 478
    2.1 断言 480
    2.2 一个简单的单元测试框架 482
    2.2.1 自动测试 483
    2.2.2 TestSuite框架 485
    2.2.3 测试套件 488
    2.2.4 测试框架的源代码 489
    2.3 调试技术 493
    2.3.1 用于代码跟踪的宏 494
    2.3.2 跟踪文件 494
    2.3.3 发现内存泄漏 495
    2.4 小结 499
    2.5 练习 500
    第二部分 标准C++库
    第3章 深入理解字符串 504
    3.1 字符串的内部是什么 504
    3.2 创建并初始化C++字符串 505
    3.3 对字符串进行操作 508
    3.3.1 追加、插入和连接字符串 508
    3.3.2 替换字符串中的字符 509
    3.3.3 使用非成员重载运算符连接 512
    3.4 字符串的查找 513
    3.4.1 反向查找 516
    3.4.2 查找一组字符第1次或最后一次出现的位置 517
    3.4.3 从字符串中删除字符 519
    3.4.4 字符串的比较 520
    3.4.5 字符串和字符的特性 523
    3.5 字符串的应用 527
    3.6 小结 531
    3.7 练习 531
    第4章 输入输出流 534
    4.1 为什么引入输入输出流 534
    4.2 救助输入输出流 537
    4.2.1 插入符和提取符 537
    4.2.2 通常用法 540
    4.2.3 按行输入 541
    4.3 处理流错误 542
    4.4 文件输入输出流 544
    4.4.1 一个文件处理的例子 544
    4.4.2 打开模式 546
    4.5 输入输出流缓冲 546
    4.6 在输入输出流中定位 548
    4.7 字符串输入输出流 550
    4.7.1 输入字符串流 551
    4.7.2 输出字符串流 552
    4.8 输出流的格式化 555
    4.8.1 格式化标志 555
    4.8.2 格式化域 556
    4.8.3 宽度、填充和精度设置 557
    4.8.4 一个完整的例子 557
    4.9 操纵算子 560
    4.9.1 带参数的操纵算子 560
    4.9.2 创建操纵算子 562
    4.9.3 效用算子 563
    4.10 输入输出流程序举例 565
    4.10.1 维护类库的源代码 565
    4.10.2 检测编译器错误 568
    4.10.3 一个简单的数据记录器 570
    4.11 国际化 573
    4.11.1 宽字符流 574
    4.11.2 区域性字符流 575
    4.12 小结 577
    4.13 练习 577
    第5章 深入理解模板 580
    5.1 模板参数 580
    5.1.1 无类型模板参数 580
    5.1.2 默认模板参数 582
    5.1.3 模板类型的模板参数 583
    5.1.4 typename关键字 587
    5.1.5 以template关键字作为提示 588
    5.1.6 成员模板 589
    5.2 有关函数模板的几个问题 591
    5.2.1 函数模板参数的类型推断 591
    5.2.2 函数模板重载 594
    5.2.3 以一个已生成的函数模板地址作为参数 595
    5.2.4 将函数应用到STL序列容器中 598
    5.2.5 函数模板的半有序 600
    5.3 模板特化 601
    5.3.1 显式特化 601
    5.3.2 半特化 602
    5.3.3 一个实例 604
    5.3.4 防止模板代码膨胀 606
    5.4 名称查找问题 609
    5.4.1 模板中的名称 609
    5.4.2 模板和友元 613
    5.5 模板编程中的习语 617
    5.5.1 特征 617
    5.5.2 策略 621
    5.5.3 奇特的递归模板模式 623
    5.6 模板元编程 624
    5.6.1 编译时编程 625
    5.6.2 表达式模板 631
    5.7 模板编译模型 636
    5.7.1 包含模型 636
    5.7.2 显式实例化 637
    5.7.3 分离模型 638
    5.8 小结 639
    5.9 练习 640
    第6章 通用算法 642
    6.1 概述 642
    6.1.1 判定函数 644
    6.1.2 流迭代器 646
    6.1.3 算法复杂性 647
    6.2 函数对象 648
    6.2.1 函数对象的分类 649
    6.2.2 自动创建函数对象 649
    6.2.3 可调整的函数对象 652
    6.2.4 更多的函数对象例子 653
    6.2.5 函数指针适配器 658
    6.2.6 编写自己的函数对象适配器 662
    6.3 STL算法目录 665
    6.3.1 实例创建的支持工具 666
    6.3.2 填充和生成 669
    6.3.3 计数 670
    6.3.4 操作序列 671
    6.3.5 查找和替换 674
    6.3.6 比较范围 679
    6.3.7 删除元素 681
    6.3.8 对已排序的序列进行排序和运算 684
    6.3.9 堆运算 691
    6.3.10 对某一范围内的所有元素进行运算 691
    6.3.11 数值算法 697
    6.3.12 通用实用程序 699
    6.4 创建自己的STL风格算法 700
    6.5 小结 701
    6.6 练习 702
    第7章 通用容器 706
    7.1 容器和迭代器 706
    7.2 概述 707
    7.2.1 字符串容器 711
    7.2.2 从STL容器继承 712
    7.3 更多迭代器 714
    7.3.1 可逆容器中的迭代器 715
    7.3.2 迭代器的种类 716
    7.3.3 预定义迭代器 717
    7.4 基本序列容器:vector、list和deque 721
    7.4.1 基本序列容器的操作 721
    7.4.2 向量 723
    7.4.3 双端队列 728
    7.4.4 序列容器间的转换 730
    7.4.5 被检查的随机访问 731
    7.4.6 链表 732
    7.4.7 交换序列 736
    7.5 集合 737
    7.6 堆栈 743
    7.7 队列 745
    7.8 优先队列 748
    7.9 持有二进制位 755
    7.9.1 bitset 756
    7.9.2 vector 758
    7.10 关联式容器 760
    7.10.1 用于关联式容器的发生器和填充器 763
    7.10.2 不可思议的映像 765
    7.10.3 多重映像和重复的关键字 766
    7.10.4 多重集合 768
    7.11 将STL容器联合使用 771
    7.12 清除容器的指针 773
    7.13 创建自己的容器 774
    7.14 对STL的扩充 776
    7.15 非STL容器 777
    7.16 小结 781
    7.17 练习 781
    第三部分 专 题
    第8章 运行时类型识别 785
    8.1 运行时类型转换 785
    8.2 typeid 操作符 789
    8.2.1 类型转换到中间层次类型 790
    8.2.2 void型指针 791
    8.2.3 运用带模板的RTTI 792
    8.3 多重继承 793
    8.4 合理使用RTTI 793
    8.5 RTTI的机制和开销 797
    8.6 小结 797
    8.7 练习 798
    第9章 多重继承 800
    9.1 概论 800
    9.2 接口继承 801
    9.3 实现继承 803
    9.4 重复子对象 807
    9.5 虚基类 810
    9.6 名字查找问题 817
    9.7 避免使用多重继承 819
    9.8 扩充一个接口 820
    9.9 小结 823
    9.10 练习 823
    第10章 设计模式 825
    10.1 模式的概念 825
    10.2 模式分类 826
    10.3 简化习语 827
    10.3.1 信使 827
    10.3.2 收集参数 828
    10.4 单件 829
    10.5 命令:选择操作 833
    10.6 消除对象耦合 836
    10.6.1 代理模式:作为其他对象的前端 837
    10.6.2 状态模式:改变对象的行为 838
    10.7 适配器模式 840
    10.8 模板方法模式 841
    10.9 策略模式:运行时选择算法 842
    10.10 职责链模式:尝试采用一系列
    策略模式 843
    10.11 工厂模式:封装对象的创建 845
    10.11.1 多态工厂 847
    10.11.2 抽象工厂 849
    10.11.3 虚构造函数 851
    10.12 构建器模式:创建复杂对象 855
    10.13 观察者模式 860
    10.13.1 “内部类”方法 862
    10.13.2 观察者模式举例 864
    10.14 多重派遣 867
    10.15 小结 873
    10.16 练习 873
    第11章 并发 875
    11.1 动机 875
    11.2 C++中的并发 876
    11.3 定义任务 878
    11.4 使用线程 879
    11.4.1 创建有响应的用户界面 880
    11.4.2 使用执行器简化工作 882
    11.4.3 让步 884
    11.4.4 休眠 885
    11.4.5 优先权 886
    11.5 共享有限资源 887
    11.5.1 保证对象的存在 887
    11.5.2 不恰当地访问资源 890
    11.5.3 访问控制 892
    11.5.4 使用保护简化编码 893
    11.5.5 线程本地存储 896
    11.6 终止任务 897
    11.6.1 防止输入/输出流冲突 897
    11.6.2 举例观赏植物园 898
    11.6.3 阻塞时终止 901
    11.6.4 中断 902
    11.7 线程间协作 906
    11.7.1 等待和信号 906
    11.7.2 生产者-消费者关系 909
    11.7.3 用队列解决线程处理的问题 912
    11.7.4 广播 916
    11.8 死锁 921
    11.9 小结 925
    11.10 练习 926
    附 录
    附录A 推荐读物
    附录B 其他
  • 阅读
  • 前言
    像任何人类语言一样,C++提供了一种表达思想的方法。如果这种表达方法是成功的,那么当问题变得更大和更复杂时,该方法将会明显地表现出比其他方法更容易和更灵活的优点。
    不能只把C++看做是语言要素的一个集合,因为有些要素单独使用是没有意义的。如果我们不只是用C++语言编写代码,而是用它思考“设计”问题,那么必须综合使用这些要素。而且,为了以这种方法理解C++,我们必须了解使用C的问题和一般的编程问题。本书讨论的是编程问题、为什么这些编程问题会成为要解决的问题以及用C++解决编程问题所采用的方法。因此,在每一章中所解释的一组语言要素,都建立在C++语言解决某一类特殊问题所用方法的基础之上。以这种方式,我希望一点一点地引导读者,从掌握C开始,直到读者使用C++变成自己的母语思维方式。
    我将始终坚持一种观点:读者应当在头脑中建立一个模型,以便从各个方面深入理解这门语言的精髓。如果读者遇到难题,他可以将问题纳入这个模型,推导出答案。我将努力把已经印在我脑海中的见解传授给读者,正是这些见解,使得我能开始“用C++进行思考”。
    第1卷第2版中的新内容
    本书是第1版的彻底重写,反映了C++标准最终完成所带来的C++的所有改变,也反映了自从第1版写完后我又学习到的内容。我已经检查并重写了第1版中的全部文字,在这个过程中,我删去了一些过时的例子,修改了一些现有的例子,并增加了一些新的例子和新的练习。我对第1版的内容进行了大规模的重新整理和重新编排,以便反映新出现的更好的工具和我对人们如何学习C++的进一步理解。为方便没有C背景知识的读者能阅读本书后面的章节,在第2版增加了一章,简要地介绍C概念和基本的C++特征。
    因而,对于“第2版与第1版相比有何不同”这个问题的简要回答是:不同之处不在于版本号是新的,而是进行了重写,有的地方读者甚至无法认出原来的例子和材料。
    第2卷的内容是什么
    C++标准增加了一些重要的新库,例如String、在标准C++库中的容器和算法,以及模板中的新的复杂性。这些新增的内容和其他更高级的主题被放进本书的第2卷,包括多重继承、异常处理、设计模式和建立和调试稳定系统等内容。
    如何得到第2卷
    就像当前你手上的这本书一样,《C++编程思想》第2卷完全可以从我的网站www.BruceEckel.com上下载。
    预备知识
    在本书第1版中,我假定读者已经学习了C,并至少具有自如阅读的水平。我的重点放在简化我认为比较困难的部分:C++语言。第2版增加了一章,快速地介绍C,并在光盘上提供“Thinking in C”的课堂讨论材料,但是即使如此,我仍然假设读者具有一定的程序设计经验。另外,正如读者可以通过读小说而直接地学会许多新词一样,读者也可以从在本书后面的文字中学习有关于C的大量知识。
    学习C++
    我希望本书的读者有和我进入C++时相同的情况:作为一个C程序员,对于编程持有实在而执着的态度。但糟糕的是,我的背景和经验是在硬件层的嵌入式编程方面。在那里,C常常被看做高层语言,它对于位操作是低效率的。后来我发现,自己甚至不是一个好的C程序员,平时总是掩盖了对malloc( )和free( )、setjmp( )和longjmp( )结构以及其他“复杂”概念的无知,当开始触及这些主题时就竭力回避,而不是努力去获取新的知识。
    在我开始致力于学习C++时,当时惟一像样的书是Stroustrup夫子自道式的“专家指南”,因此我只好靠自己弄清基本概念。这引出了我的第一本关于C++的书,这本书基本上就是直接把我头脑中的经验倒出来而写成的。它的构思是作为读者的指南,引导程序员同时进入C和C++。这本书的两个版本 都收到了读者的热情反响。
    几乎就在《Using C++》出版的同时,我开始讲授这门语言。讲授C++已经变成了我的职业。自1989年以来,在授课时我看到了世界各地听众昏昏欲睡的样子、茫然不知的面容和困惑不解的表情。当我对一些人数不多的人群进行内部培训时,在练习过程中又发现了某些问题。即便那些面带微笑和会心点头的学生,实际上对许多问题也还是糊涂的。通过开创和多年主持“软件开发会议”的C++和Java系列专题,我发现,我和其他讲演者都有一种倾向,即过快地向听众灌输了过多的主题。后来,我做了一些努力,通过区别对待不同层次的听众和提供相关资料的方法,尽量吸引听众。也许这是过分的要求,但是因为我是一个抵触传统教学的人(对于大部分人而言,我相信这种抵触源于厌倦),所以希望我通过努力,使每一个人都能跟得上教学进度。
    有一段时间,我编写了大量的教学演示。这样,我结束了通过实验和重复方式进行学习(在设计C++程序的过程中,这也是一项很有用的技术)的阶段。最后,从我多年的教学经验中总结出来的所有内容,形成了一门课程。在课程中,我用一系列分离的、易于理解的步骤并采用实地课堂讨论的形式解决学习中的问题(理想的学习情况),并在每次课后面跟随着练习。
    本书的第1版是作为两学年制课程编写的,并且书中的内容已经在许多不同的课堂讨论上通过了多种形式的检验。我从每次课堂讨论上收集反馈意见,不断地修改和调整内容,直到我感觉到它已经成为一本很好的教材为止。但这本书不仅仅是课堂讨论的分发教材,而且我在其中放入了尽可能多的信息,在结构上使得它能引导读者顺利地通过当前主题和进入下一个主题。另外,这本书也适合于自学读者,能帮助他们尽快地掌握这门新的编程语言。
    目标
    在这本书中,我的目标是:
    1) 以适当的进度介绍内容。每次将学习向前推进一小步,因此读者能很容易地在继续下一步学习之前消化每个已学过的概念。
    2) 尽可能使用简短的例子。当然,这有时会妨碍我解决“现实世界”的问题。但是,我发现,当初学者能够掌握例子的每个细节,而不受问题的领域所影响时,他们通常会更有兴趣进行学习。另外,在课堂情况下能达到的接受能力,对代码的长短也有严格的限制。为此,我有时会受到使用“玩具例子”的批评,但是我甘愿承受这一批评,因为这样更有利于取得某些教学法上的效果。
    3) 仔细安排描述内容的顺序,不让读者看到还没有揭示的内容。当然,这不是总能做到的;如果出现了这种情况,我将会给出简明的介绍性的描述。
    4) 只把对于理解这门语言比较重要的东西介绍给读者,而不是介绍我知道的所有内容。我相信,不同信息的重要性是不同的。有些内容是95%的程序员不需要知道的,这些东西只会迷惑人们,增加他们对该语言复杂性的感觉。举一个C语言的例子,如果我们记住运算符优先表(我是记不住的),我们就可以写更漂亮的代码。但是,如果一定要这样做,反而会使代码的读者或维护者糊涂。所以可以忘掉优先级,当不清楚时使用括号。我们对于C++中的某些内容也可以采取同样的态度,因为我认为这些内容对于写编译器的人更重要,而对于程序员就不是那么重要。
    5) 保持每一节的内容充分集中,使得授课时间以及两个练习之间的间隔时间不长。这不仅能使听众保持活跃的思想和在课堂讨论中精力集中,而且会使他们有更大的成就感。
    6) 帮助读者打下坚实的基础,使得他们能充分地理解面对的问题,从而可以顺利地转向学习更困难的课程和书籍(特别是这本书的第2卷)。
    7) 我尽力不用任何特定厂商的C++版本,因为对于学习编程语言,我不认为特定实现的细节像语言本身一样重要。大部分厂商的文档只适合于他们自己的特定实现。
    各章概要
    C++是一个在已有文法上面增加了新的不同特征的语言(因此,它被认为是混合的面向对象的编程语言)。由于很多人走了学习弯路,因此我们已经开始探索C程序员转移到C++语言特征的方法。因为这是过程型训练思想的自然延伸,所以我决定去理解和重复相同的道路,并通过引出和回答一些问题来加速这一进程,这些问题是当我学习该语言时遇到的和听众在听我的课时提出来的。
    设计这门课时,我始终注意一件事:精练C++语言的学习过程。听众的反馈意见帮助我认识到哪些部分是很难学习的和需要额外解释的。在这个领域中,我曾经雄心勃勃,一次讲解包括了太多的内容。通过讲解过程,我知道了,如果包括大量新特征,就必须对它们全部作出解释,而且学生也特别容易混淆这些新特征。因此,我努力一次只介绍尽可能少的特征,理想的情况是每章一次只介绍一个主要概念。
    本书的目标是只在每一章中讲授一个概念,或只讲授一小组相关的概念,用这种方法,不会依赖于其他的特征。这样,在进入下一章的学习之前,学生可以对自己的当前知识融会贯通。为了实现这个目标,我把一些C特征留到后面的章节去介绍,甚至放在比我希望的还要往后的地方介绍。这样做的好处是读者不会因为看到了许多未解释的C++特征被使用而困惑,因此,对该语言的介绍将是和缓的,并且将反映出读者自己消化这些特征时将会采用的方式。
    下面是本书各章内容的简要说明。
    第1章 对象导言。当项目对于维护而言变得太大和太复杂时,就产生了“软件危机”。按程序员们的说法,“我们无法完成那些项目,即便能完成,它们也太昂贵了”。这引出了一些问题,在本章中我将讨论这些问题,并且讨论面向对象程序设计(OOP)的思想和如何运用这一思想解决软件危机问题。这一章引导读者了解OOP的基本概念和特征,介绍分析和设计过程。另外,在这一章中,我还将阐述采用这种语言的好处,提出关于如何转入C++语言领域的建议。
    第2章 对象的创建与使用。这一章解释用编译器和库建立程序的过程。它介绍了本书中的第一个C++程序,显示程序如何构造和编译,然后介绍标准C++中的可用对象的基本库。在结束这一章时,我们就对如何用流行的对象库编写C++程序有一个深刻的领会。
    第3章 C++中的C。这一章详细综述在C++中使用的C的特征和一些只在C++中使用的特征,还介绍在软件开发领域通用的“制作”工具,并且用它建立了本书中的所有例子(本书的源代码在www.BruceEckel.com中可找到,包含了对每章的makefile)。第3章假设读者已经具有某种过程型程序设计语言的坚实基础,例如Pascal和C语言或者甚至某种形式的Basic(只要读者已经用这种语言编写了大量的代码,特别是函数)。
    第4章 数据抽象。C++的大部分特征都围绕着创建新数据类型的能力。这不仅可以提供优质代码组织,而且可以为更强大的OOP能力奠定基础。读者将可以看到如何用将函数放入结构内部的简单过程来实现这一思想,并可以看到如何具体地完成这样的过程和创建什么样的代码。读者还能学会组织代码成为头文件和实现文件的最好方法。
    第5章 隐藏实现。通过说明结构中的一些数据和函数是private(私有的),可以把它们设置为对于这个新结构类型的用户是不可见的。这意味着能够把下层实现和客户程序员看到的接口隔离开来,这样就容易改变具体实现,而不影响客户代码。另外,C++还引入关键字class作为描述新数据类型的更具吸引力的方法,而单词“对象”的意思并不神秘,它是一种美妙的变量。
    第6章 初始化与清除。C语言的最通常的一类错误是由于变量未初始化而引起的。C++的构造函数使得程序员能保证他的新数据类型(即“他的类的对象”)的变量总是能被恰当地初始化。如果他的对象还需要某种方式的清除,他可以保证这个清除动作总是由C++的析构函数来完成。
    第7章 函数重载与默认参数。C++可以帮助程序员建立大而复杂的项目。这时,可能会引进使用相同函数名的多个库,还可能会在同一个库中选择具有不同含义的相同的名字。C++采用函数重载使这一问题容易解决。重载允许当参数表不同时重用相同的函数名。默认参数通过自动为某些参数提供默认值,使我们能用不同的方式调用同一个函数。
    第8章 常量。本章讨论了const和volatile关键字,它们在C++中有另外的含义,特别是在类的内部。我们将学习对指针定义使用const的含义。本章还说明const的含义在类的内部和外部有何不同,以及如何在类的内部创建编译时常量。
    第9章 内联函数。预处理宏省去了函数调用开销,但是也排除了有价值的C++类型检查。内联函数具有预处理宏和实际函数调用的所有好处。这一章深入地研究了内联函数的实现和使用。
    第10章 名字控制。在程序设计中,创建名字是基本的活动,而当项目变大时,名字的数目是无法限制的。C++允许在名字创建、可视性、存储代换和连接方面有大量的控制。这一章将说明如何在C++中用两种技术控制名字。第一,用关键字static控制可视性和连接,研究它对于类的特殊含义。另一种在全局范围内更有用的控制名字的技术是C++的namespace(名字空间)特征,它允许把全局名字空间划分为不同的区域。
    第11章 引用和拷贝构造函数。C++指针的作用和C指针一样,而且具有更强的C++类型检查的好处。C++还提供了另外的处理地址的方法:继Algol和Pascal之后,C++使用了“引用”,允许当程序员使用平常的符号时由编译器来处理地址操作。读者还会遇到拷贝构造函数,它通过按值传送控制将对象传送给函数或从函数中返回的方式。最后,本章还将解释C++指向成员的指针。
    第12章 运算符重载。这个特征有时被称为“语法糖(syntactic suger)”。由于运算符也可以像函数调用那样使用,这使得程序员在运用类型的语法时更加灵活。在这一章中,读者将了解到,运算符重载只是不同类型的函数调用,并且将学会如何写自己的运算符重载,学会处理参数、类型返回以及确定一个运算符是成员还是友元时的一些易混淆的情况。
    第13章 动态对象创建。一个空中交通系统将处理多少架飞机?一个CAD系统将需要多少种造形?在一般的程序设计问题中,我们不可能知道程序运行所需要的对象的数量、生命期和类型。在这一章中,我们将学习C++的new和delete如何漂亮地通过在堆上安全地创建对象而解决上述问题。我们还将看到,new和delete如何用不同的方法重载,从而使我们能控制如何分配和释放存储。
    第14章 继承和组合。数据抽象允许程序员从零开始创建新的类型。通过组合和继承,程序员可以用已存在的类型创建新的类型。用组合方法,程序员可以以老的类型作为零件组装成新的类型;而用继承方法,程序员可以创建已存在类型的一个更特殊的版本。在这一章中,读者将学习这一文法,学习如何重定义函数,以及理解构造和析构对于继承和组合的重要性。
    第15章 多态性和虚函数。单靠读者自己,可能要花九个月的时间才能发现和理解OOP的这一基石。通过一些小而简单的例子,读者可以看到如何通过继承创建一个类型族,看到在这个类型族中如何通过公共基类对这个族中的对象进行操作。关键字virtual使程序员能够按类属处理这个族中的所有对象,这意味着大块代码将不依赖于特殊类型的信息,因此,程序是可扩充的,构造程序和维护代码也变得更容易和更便宜。
    第16章 模板介绍。继承和组合允许程序员重用对象代码,但不能解决有关重用需要的所有问题。模板通过为编译器提供了一种在类或函数体中代换类型名的方法,来允许程序员重用源代码。这就支持了容器类库的使用,容器类库是使我们能快速而有效地开发面向对象程序的重要工具(标准C++库包含了一个重要的容器类库)。这一章给出了这个基本主题的详尽阐述。
    另一些主题(更高级的课题),可以在本书的第2卷中看到,本书的第2卷可以从网站www.BruceEckel.com下载。
    练习
    我已经发现,在课堂讨论期间,练习对同学们的完全理解是特别有用的,因此,本书的每章后面都有一组练习。练习题的数量在第1版的基础上有很大的增加。
    在这些练习中,很多是比较简单的,可以在课堂内或实验室内用合理的时间完成,老师可以通过观察证实所有的学生正在吸收这些材料。有些练习是为了激发优秀学生的学习兴趣。大量练习被设计成能在短期内求解,目的是为了测试和完善学生所掌握的知识,而不是提出主要的挑战(也许我们将自己找到那些挑战,更可能的是,那些挑战会自动出现在我们面前)。
    练习的答案
    部分练习题的答案可以在本书的电子文档“Thinking in C++ Annotated Solution Guide”中找到,只需支付很少的费用就可以从网站www.BruceEckel.com上获得这个电子文档。
    源代码
    本书中的源代码是免费软件版权,通过网站www.BruceEckel.com分发。该版权防止未经允许用印刷媒体重印这些代码,但是,在许多其他情况下可以使用这些代码。
    这些代码放在一个压缩文件中,可以从任何有zip工具的平台上提取(如果没有安装合适的平台,可以从Internet上找到适合你的平台的某个版本)。
    读者可以在自己的项目中和在课堂上使用这些代码,只要遵守代码中的版权声明。
    语言标准
    在本书中,当谈到遵循ISO C标准时,我一般只是说‘C’。只有当有必要区别标准C和老的、以前版本的C时,我才加以区分。
    在写这本书时,C++标准委员会完成了语言的标准化工作。这样,我将用术语“标准C++”来指代这个标准化的语言。如果我简单地谈到C++,读者就应该假设这意味着“标准C++”。
    在C++标准委员会的实际名字与标准本身的名字之间有些混淆。委员会的主席Steve Clamage就此作了如下澄清:
    有两个C++标准委员会:NCITS(以前的X3)J16委员会和ISO JTC1/SC22/WG14委员会。ANSI授权NCITS建立制订美国国家标准的技术委员会。
    1989年J16受委托制订C++美国标准。1991年WG14受委托制订国际标准。J16项目转变为“Type I”(国际)项目,并服从于ISO标准化计划。
    这两个委员会在同一时间、同一地点开会,J16的投票作为美国在WG14的票数。WG14委派J16做技术工作,并对J16的技术工作进行表决。
    最初,C++标准是作为ISO标准制订的。ANSI后来投票(在J16的建议下)决定采用ISO C++标准作为C++美国标准。
    因此,“ISO”是称呼C++标准的正确方式。
    语言支持
    读者的编译器可能不支持在本书中讨论的所有特征,如果还没有编译器的最新版本就更是如此了。实现像C++这样的语言是艰巨的任务,而且读者可能希望将这些特征划分成一些部分后分别出现,而不是一下子全出现。如果读者试用本书中的某个例子,从编译器中得到了大量的错误,这可能不是代码或编译错误,而可能是他的特定编译器还没有实现例子中的某个特征。
    错误
    无论一个作者具有多少发现错误的技巧,总会有一些错误漏网,它们常常能被新读者发现。如果读者发现了任何认为是错误的地方,请填写网站www.BruceEckel.com上关于本书的修改表格并在线提交,我将非常感激。
  • 译序
  • 本书评论
    用户名:密码: 验证码:点击我更换图片
    
    北京皓辰网域网络信息技术有限公司. 版权所有 京ICP证:060528号
    百度大联盟认证绿色会员