“如果所有数值计算软件都可以用 C++ 编写而不影响效率,那就太好了,但除非能找到实 现上述目标而又不损害 C++ 类型 系统的方法,否则最好依赖FORTRAN、汇编程序或特定体系 结构的扩展。”
—— 本贾尼·斯特劳斯特卢普(BjarneStroustrup)C++ 之父

用 C 语言编写的程序可以获得良好的性能,并且大量的软件都是用C 语言编写的。C 语言 相对较小且易于学习。挑战在于使用简单而危险的语言特性编写大型且无缺陷(Bug)的软件, 特别是指针和宏。C 语言最新的标准是在 2017 年发布的,因此将其命 名为 C17。大多数 C 语言的特性(但并不是所有特性),迟早会被引入 C++ 中。

当应用程序的主要组件是网页或图形界面,并且需要执行的计算相对较少时,Java、C#和PHP 等语言可能是不错的选择。

C++ 在开发大型、高质量的软件时表现尤其出色,而且开发过程不一定是缓慢而痛苦的。 只要我们能正确地进行抽象,就可以迅速编写出 C++ 程序。我们乐观地认为,在未来的 C++ 标 准中,将出现更多的科学计算库。

显然,我们了解的语言越多,我们的选择也就越多。此外,对这些语言了解得越多,我们 的选择就越明智。大型项目通常包含用多种语言编写的组件,而在大多数情况下,关键性能的 内核是用 C 或 C++ 实现的。总之,学习C++ 是一段令人着迷的旅程,对其有深入的理解将使你成为一名在任何情况下都表现出色的程序员。

学习 C++ 的理由

与其他语言不同,C++ 应用涵盖了从足够接近硬件的编程到高级抽象编程的所有范围。较 低级别的编程(比如用户自定义的内存管理)使程序员能够理解执行过程中具体发生了哪些事 情,这反过来又有助于程序员理解其他编程语言的行为。用 C++ 可以编写出非常高效的程序, 以至于用机器语言编写的代码要付出巨大的努力才能略微超过它。但是,还是应该先将精力放 在编写清晰且具有表现力的软件代码上,然后再考虑进行核心性能调优。

C++ 语言的高级特性包括以下几方面。该语言直接支持多种编程范式:面向对象编程、泛型编程、元编程、并发编程等。像 RAII和表达式模板这样的编程技术都是用 C++ 实现的,同时也是为 C++ 发明的。由于该 语言的表现力非常强,通常可以在不改变语言的情况下构建这些新技术。也许将来你也会为它 发明一种新技术。

C++新书

C++现代编程技术:写给工程师的C++教程

学好C++,让自己成为更出色的程序员_初始化

适读人群 :本书适合想全面学习C++编程的读者以及对C++新特性感兴趣的读者阅读参考。尤其适合软件工程师以及对C++在科学软件、工程软件领域的应用感兴趣的读者。

本书是你掌握 C++ 编程精髓的得力助手。无论你是硬件底层的编程高手,还是追求高级抽象思维的软件工程师,本书都将引领你深入C++的每一个层面。
本书紧跟 C++17 和 C++20 的新标准,通过丰富多样的技术案例,为你呈现 C++ 编程的强大魅力。
本书由资深C++教育专家彼得·哥特史林(Peter Gottschling)编写,结合其在物理、数学、工程领域的教学经验,为你细致解读C++的高级特性,带你领略从基础到高级的编程艺术。
无论你的编程经验如何,你都将快速掌握 lambda 表达式、可变参数模板等日益强大的C++特性。

阅读本书的理由

书中内容已经过实践检验。作者教授“C++ for Scientists”课程已超过 3 年,学习这门课程 的学生大多来自数学系,还有一些来自物理和工程系,他们在参加此课程之前通常不了解 C++, 但在课程结束时能够掌握表达式模板等高级技术。读者可以按照自己的节奏阅读本书, 按部就班地推进,或通过阅读附录 A 进行深入探索。

本书目录

第 1章 C++基础1

1.1 我们的第 一个程序1

1.2 变量3

1.2.1 内置类型4

1.2.2 字符和字符串5

1.2.3 声明变量6

1.2.4 常量6

1.2.5 字面量7

1.2.6 非窄化初始化9

1.2.7 作用域10

1.3 操作符12

1.3.1 算术操作符13

1.3.2 布尔操作符15

1.3.3 位操作符17

1.3.4 赋值操作符17

1.3.5 程序控制流18

1.3.6 内存管理19

1.3.7 访问操作符19

1.3.8 类型处理19

1.3.9 异常处理20

1.3.10 操作符重载20

1.3.11 操作符优先级20

1.3.12 避免歧义21

1.4 表达式和语句23

1.4.1 表达式23

1.4.2 语句24

1.4.3 分支24

1.4.4 循环27

1.4.5 goto30

1.5 函数30

1.5.1 参数30

1.5.2 返回值32

1.5.3 内联33

1.5.4 重载34

1.5.5 main函数35

1.6 异常处理36

1.6.1 断言36

1.6.2 异常38

1.6.3 静态断言42

1.7 I O42

1.7.1 标准输出42

1.7.2 标准输入43

1.7.3 文件的输入和输出43

1.7.4 通用流的概念44

1.7.5 格式化45

1.7.6 新型格式化46

1.7.7 处理I O异常49

1.7.8 文件系统52

1.8 数组、指针和引用53

1.8.1 数组53

1.8.2 指针55

1.8.3 智能指针57

1.8.4 引用60

1.8.5 指针和引用的区别61

1.8.6 不要引用过期数据61

1.8.7 数组的容器62

1.9 结构化的软件项目64

1.9.1 注释65

1.9.2 预处理器指令66

1.10 练习70

1.10.1 窄化70

1.10.2 字面量70

1.10.3 操作符70

1.10.4 分支71

1.10.5 循环71

1.10.6 I O71

1.10.7 数组和指针71

1.10.8 函数71

第 2章 类72

2.1 为通用性编程而不局限于技术细节72

2.2 成员74

2.2.1 成员变量74

2.2.2 可访问性75

2.2.3 访问操作符77

2.2.4 类的静态声明77

2.2.5 成员函数78

2.3 设置值:构造函数与赋值79

2.3.1 构造函数79

2.3.2 赋值88

2.3.3 初始化器列表89

2.3.4 统一初始化91

2.3.5 移动语义93

2.3.6 通过字面量构造对象102

2.4 析构函数104

2.4.1 实现规则104

2.4.2 妥善处理资源104

2.5 方法生成总结110

2.6 访问成员变量111

2.6.1 访问函数111

2.6.2 下标操作符112

2.6.3 常量成员函数113

2.6.4 引用限定的成员114

2.7 操作符重载的设计116

2.7.1 保持一致性116

2.7.2 优先级117

2.7.3 成员函数和自由函数117

2.7.4 重载等式119

2.7.5 重载“飞船”运算符121

2.7.6 重载中的类型系统123

2.8 练习124

2.8.1 多项式124

2.8.2 有理数124

2.8.3 移动赋值125

2.8.4 初始化器列表125

2.8.5 资源管理125

第3章 泛型编程126

3.1 函数模板126

3.1.1 实例化127

3.1.2 参数类型推导128

3.1.3 处理模板中的异常132

3.1.4 混合类型133

3.1.5 统一初始化134

3.1.6 自动返回的类型134

3.1.7 模板参数简化135

3.2 命名空间和函数查找135

3.2.1 命名空间135

3.2.2 参数依赖查找138

3.2.3 命名空间限定和ADL142

3.3 类模板144

3.3.1 容器示例144

3.3.2 设计统一的类和函数接口146

3.4 类型推导和定义151

3.4.1 自动变量类型152

3.4.2 表达式的类型152

3.4.3 decltype(auto)153

3.4.4 类模板参数推导154

3.4.5 推导多种类型156

3.4.6 定义类型157

3.5 模板特例化159

3.5.1 为某种类型特例化一个类159

3.5.2 函数特例化和重载162

3.5.3 类的偏特化163

3.5.4 偏特化函数164

3.5.5 用户自定义类型的结构化绑定166

3.5.6 用户自定义格式化169

3.6 模板的非类型参数171

3.6.1 固定大小的容器171

3.6.2 推导非类型参数173

3.7 函子174

3.7.1 类函数参数176

3.7.2 组合函子177

3.7.3 递归178

3.7.4 泛型规约181

3.8 lambda表达式182

3.8.1 捕获183

3.8.2 泛型lambda186

3.9 变量模板188

3.10 概念编程190

3.10.1 定义概念191

3.10.2 通过概念分发194

3.10.3 类中的概念195

3.10.4 概念设计197

3.11 可变参数模板197

3.11.1 递归函数197

3.11.2 直接展开199

3.11.3 索引序列200

3.11.4 折叠表达式202

3.11.5 类型生成器202

3.11.6 增长测试203

3.12 练习205

3.12.1 字符串表达205

3.12.2 元组的字符串表达205

3.12.3 泛型堆栈205

3.12.4 带类型参数的有理数205

3.12.5 向量的迭代器206

3.12.6 奇数迭代器206

3.12.7 奇数范围206

3.12.8 bool堆栈206

3.12.9 自定义大小的堆栈206

3.12.10 梯形法则207

3.12.11 带静态函数的部分特例化207

3.12.12 Functor函子207

3.12.13 Lambda207

3.12.14 实现 make_unique207

第4章 程序库208

4.1 标准模板库208

4.1.1 入门示例209

4.1.2 迭代器209

4.1.3 容器214

4.1.4 算法223

4.1.5 范围(Range)228

4.1.6 并行计算234

4.2 数值计算236

4.2.1 复数236

4.2.2 随机数生成器239

4.2.3 数学专用函数247

4.2.4 数学常量248

4.3 元编程249

4.3.1 极限(limits)250

4.3.2 类型特征251

4.4 实用程序253

4.4.1 optional253

4.4.2 元组(tuple)254

4.4.3 variant256

4.4.4 any258

4.4.5 string_view259

4.4.6 span260

4.4.7 function261

4.4.8 引用包装器263

4.5 关于时间264

4.6 并发编程267

4.6.1 专用术语267

4.6.2 概述267

4.6.3 thread268

4.6.4 关于调用者269

4.6.5 异步调用271

4.6.6 异步解析器272

4.6.7 可变互斥锁277

4.6.8 协程278

4.6.9 其他新的并发特性280

4.7 高级科学软件库280

4.7.1 替代算法280

4.7.2 区间运算281

4.7.3 线性代数281

4.7.4 常微分方程281

4.7.5 偏微分方程282

4.7.6 图形算法282

4.8 练习282

4.8.1 根据大小排序282

4.8.2 将lambda表达式作为谓词进行

查找282

4.8.3 STL 容器283

4.8.4 复数283

4.8.5 并行的向量加法284

4.8.6 重构并行加法284

第5章 元编程285

5.1 让编译器计算285

5.1.1 编译期函数285

5.1.2 扩展编译期函数287

5.1.3 素数289

5.1.4 常量的恒定性291

5.1.5 编译期lambda表达式292

5.2 提供和使用类型信息293

5.2.1 类型特征294

5.2.2 条件异常处理297

5.2.3 const简洁视图用例298

5.2.4 参数化有理数304

5.2.5 特定领域的类型属性306

5.2.6 enable_if307

5.2.7 可变参数模板的优化311

5.3 表达式模板314

5.3.1 简单的操作符314

5.3.2 表达式模板类317

5.3.3 泛型表达式模板320

5.3.4 在数据过期之前复制321

5.4 元调优:编写自定义编译器优化323

5.4.1 经典的固定尺寸展开325

5.4.2 嵌套展开327

5.4.3 动态展开332

5.4.4 展开向量表达式334

5.4.5 优化表达式模板335

5.4.6 调优简化操作338

5.4.7 嵌套循环调优345

5.4.8 调优小结348

5.5 语义概念优化349

5.5.1 语义调优的需求350

5.5.2 语义概念层次353

5.6 图灵完备性355

5.7 练习357

5.7.1 类型特征357

5.7.2 斐波那契数列357

5.7.3 最大公约数元程序358

5.7.4 混合类型的有理数358

5.7.5 向量表达式模板358

5.7.6 元列表359

第6章 面向对象编程360

6.1 基本原则360

6.1.1 基类和派生类361

6.1.2 继承构造函数364

6.1.3 虚函数和多态类365

6.1.4 通过继承实现函子371

6.1.5 派生Exception类372

6.2 去除冗余374

6.3 多重继承375

6.3.1 多个父类375

6.3.2 普通的“祖父母”376

6.4 子类型的动态选择381

6.5 转型384

6.5.1 基类和派生类之间的转换384

6.5.2 常量转型388

6.5.3 重新解析的转型388

6.5.4 函数式转型388

6.5.5 隐式转换390

6.6 高级技术391

6.6.1 CRTP391

6.6.2 包含重载的类型特征395

6.7 练习399

6.7.1 非冗余菱形399

6.7.2 继承向量类399

6.7.3 重构向量中的异常399

6.7.4 抛出异常测试399

6.7.5 Clone 函数400

第7章 科学项目401

7.1 ODE解析器的实现401

7.1.1 常微分方程401

7.1.2 Runge-Kutta算法403

7.1.3 泛型实现404

7.1.4 展望411

7.2 创建项目412

7.2.1 构建过程412

7.2.2 构建工具416

7.2.3 单独编译420

7.3 模块423

7.4 结语427

附录A 程序库428

A.1 科学软件的优劣428

A.2 基本的细节434

A.2.1 静态变量434

A.2.2 关于if语句435

A.2.3 达夫设备436

A.2.4 程序调用436

A.2.5 断言和异常437

A.2.6 二进制I O438

A.2.7 C风格的 I O439

A.2.8 垃圾回收机制440

A.2.9 宏的问题440

A.3 实际用例:矩阵转置442

A.4 类的详细信息451

A.4.1 指向成员的指针451

A.4.2 更多初始化示例451

A.4.3 访问多维数据结构452

A.5 方法生成455

A.5.1 自动生成455

A.5.2 控制生成458

A.5.3 生成规则458

A.5.4 设计指南和不足462

A.6 模板465

A.6.1 统一初始化465

A.6.2 函数调用466

A.6.3 为特定硬件特例化469

A.6.4 可变参数二进制I O470

A.7 关于软件库的更多信息471

A.7.1 在C++03中使用std::vector471

A.7.2 可变参数471

A.8 旧式的动态选择472

A.9 元编程473

A.9.1 历史上的第 一个元编程473

A.9.2 元函数475

A.9.3 向后兼容的静态断言477

A.9.4 匿名类型参数477

A.10 链接到C代码480

附录B 编程工具483

B.1 g++483

B.2 调试484

B.2.1 基于文本的调试器484

B.2.2 图形化界面调试工具:DDD486

B.3 内存分析488

B.4 gnuplot489

B.5 UNIX、Linux和macOS490

附录C 语言规范492

C.1 值类别492

C.2 操作符概要493

C.3 转换规则496

C.3.1 提升496

C.3.2 其他转换496

C.3.3 常用的算术转换497

C.3.4 窄化498

参考资料499