跳转到内容

前言

C++ 的发展始于 1982 年。丹麦计算机科学家 Bjarne Stroustrup 以 “C with Classes” 为基础,进一步发展出了这门语言。1985 年,The C++ Programming Language 一书的第一版问世。C++ 的首个标准化版本发布于 1998 年,即 C++98。2003 年发布的 C++03 只包含少量小幅更新。此后的一段时间里,语言演进相对沉寂,但势头随后逐渐积聚,最终在 2011 年迎来了一次重大升级——C++11。自那以后,C++ 标准委员会基本维持三年一个版本的节奏,先后带来了 C++14、C++17、C++20,而现在则是 C++23。总体来说,随着 2023 年 C++23 的发布,C++ 已接近 40 岁,但依然活力十足。2023 年大多数编程语言排行榜中,C++ 依旧稳居前四。它被广泛应用在各种硬件环境中,从带嵌入式微处理器的小型设备,到多机架超级计算机,几乎无所不包。除了硬件覆盖面极广之外,C++ 几乎可以胜任任何类型的编程工作:移动平台游戏、对性能要求极高的人工智能(AI)与机器学习(ML)软件、自动驾驶汽车组件、实时三维图形引擎、底层硬件驱动、完整操作系统、网络设备软件栈、Web 浏览器等等。C++ 程序的性能很难被其他编程语言匹敌,因此它事实上已成为构建快速、强大、企业级程序的首选语言。Microsoft、Facebook、Amazon、Google 等大型科技公司,都在用 C++ 编写的服务支撑其基础设施。尽管 C++ 非常流行,但要真正全面掌握这门语言并不容易。专业 C++ 程序员所使用的许多简单却强大的技巧,在传统教材中并不常见;即使是经验丰富的 C++ 开发者,也常常对某些实用能力知之甚少。

太多编程书籍只聚焦语言语法,而忽略其在真实世界中的使用方式。典型的 C++ 教材通常每章介绍语言中的一大块内容,讲清语法,再附上示例。Professional C++ 并不采用这种写法。它不会只给你一堆脱离实践语境的“零件”和“螺丝”,而是教你如何在现实工作中使用 C++。本书会介绍那些鲜为人知、但能显著提升开发效率的特性,也会带你掌握把初学者与专业程序员区分开来的编程技巧。

即使你已经使用 C++ 多年,你也可能仍不熟悉它的一些高级特性,或者尚未真正发挥这门语言的全部能力。也许你还不了解最新版 C++23 引入的新特性;也许你已经能写出合格的 C++ 代码,但希望进一步掌握 C++ 设计与良好编程风格;又或者你接触 C++ 时间还不长,但希望一开始就按“正确的方法”来学习。本书正是为这几类读者准备的,它会帮助你把 C++ 水平提升到专业开发者的层次。

由于本书关注的是如何从 C++ 的基础或中级水平进阶为专业的 C++ 程序员,因此它默认你已经具备一定的编程基础。第 1 章“C++ 与标准库速成导论”会对 C++ 基础做一次回顾,但它并不能替代系统性的编程训练。如果你刚开始学习 C++,但已经掌握了其他编程语言,例如 C、Java 或 C#,那么你应该能够从 第 1 章 中获得绝大多数所需的入门补充。

无论如何,你都应当具备扎实的编程基本功。你需要了解循环、函数与变量,知道如何组织一个程序,并熟悉递归等基础技巧。你还应该对常见数据结构(例如队列)以及排序、查找等常用算法有一定认识。你暂时还不必掌握面向对象编程——这部分内容会在 第 5 章“基于类的设计”中讲解。

你还需要熟悉自己用于编译代码的编译器。后文会介绍两种编译器:Microsoft Visual C++ 与 GCC。至于其他编译器,请参考各自随附的官方文档。

Professional C++ 所采用的 C++ 学习路径,既能提升代码质量,也能提高你的编程效率。在本书第六版中,你会在各处看到关于 C++23 新特性的讨论。这些新特性并不是零散地分布在少数几个章节里,而是尽可能融入了全书的示例之中:凡是适合使用新特性的地方,相关示例都已经更新。

Professional C++ 讲授的不只是 C++ 的语法与语言特性。本书同样强调编程方法论、可复用的设计模式,以及良好的编程风格。Professional C++ 的方法体系覆盖了完整的软件开发过程——从设计与编码,到调试,再到团队协作。通过这种方式,你不仅能掌握 C++ 语言及其细微特性,还能把它强大的能力真正运用到大规模软件开发之中。

想象一下:有些人学会了 C++ 的全部语法,却从未见过任何真实用法的示例。他们懂得恰好足以“把事情搞砸”的那一点知识!如果没有例子,他们可能会以为程序里的所有代码都该写进 main() 函数,或者所有变量都该是全局变量——而这些做法显然都不能算是良好编程的典范。

专业的 C++ 程序员除了懂语法之外,更懂如何正确使用这门语言。他们理解良好设计的重要性,理解面向对象编程的思想,也懂得如何恰当地利用现有库。他们还会逐步积累一整套实用代码与可复用思路。

通过阅读并真正理解本书,你将成长为一名专业的 C++ 程序员。你对 C++ 的认识将扩展到许多鲜少被提及、又常常被误解的语言特性;你会更深刻地理解面向对象设计,并掌握高水平的调试技能。也许最重要的是,读完本书后,你将带着一整套可以真正用于日常工作的可复用思想离开。

努力成为专业的 C++ 程序员,而不仅仅是“懂 C++ 的程序员”,其收益是多方面的。理解语言真实的运行机制,能提升代码质量;学习不同的编程方法与流程,能帮助你更好地与团队协作;掌握可复用库与常见设计模式,能提升你每天的开发效率,让你不再反复“造轮子”。这些经验都会让你成为更出色的开发者,也让你在职场中更具价值。虽然本书不能保证你因此获得晋升,但它肯定不会拖你的后腿。

本书共分为五个部分。

第一部分“走进 Professional C++”从一章 C++ 基础速成开始,帮助读者夯实 C++ 知识底座。在这次速成回顾之后,第一部分 还会进一步深入字符串处理,因为本书大多数示例都会频繁用到字符串。该部分的最后一章则探讨如何编写可读性强的 C++ 代码。

第二部分“Professional C++ 软件设计”讨论 C++ 设计方法论。你将读到设计的重要性、面向对象方法论,以及代码复用的重要意义。

第三部分“以专业方式编写 C++”从专业开发者的视角,对 C++ 做一次技术巡礼。你将学习如何以最佳方式管理 C++ 内存、如何创建可复用的类,以及如何利用继承等重要语言特性。你还会学到输入输出技巧、错误处理、字符串本地化、正则表达式,以及如何通过名为“模块”的可复用组件来组织代码。你还将了解如何实现运算符重载、如何编写模板、如何使用 concepts 对模板参数施加约束,以及如何释放 lambda 表达式与函数对象的威力。本部分还会讲解 C++ 标准库,包括容器、迭代器、范围与算法;同时也会介绍标准中提供的其他一些库,例如与时间、日期、时区、随机数以及文件系统相关的库。

第四部分“精通 C++ 高级特性”展示如何把 C++ 的能力真正发挥到极致。本部分会揭开 C++ 某些“神秘感”背后的机制,并说明如何使用其中较为高级的特性。你将学习如何按自身需求定制与扩展 C++ 标准库、模板编程中的高级细节(包括模板元编程),以及如何利用多线程来发挥多处理器与多核系统的能力。

第五部分“C++ 软件工程”聚焦如何编写企业级质量的软件。你将读到当代软件团队采用的工程实践;如何编写高效的 C++ 代码;软件测试相关概念,例如单元测试与回归测试;调试 C++ 程序常用的技术;如何把设计技巧、框架以及概念层面的面向对象设计模式融入自己的代码;以及跨语言、跨平台代码的解决方案。

全书最后还附有一章实用的 C++ 技术面试逐章指南、一份带注释的参考书目、标准中可用 C++ 头文件的汇总,以及对统一建模语言(UML)的简要介绍。

本书并不是一部罗列 C++ 中每个类、每个成员函数、每个函数的纯参考手册。Peter Van Weert 与 Marc Gregoire 合著的 C++17 Standard Library Quick Reference(Apress,2019,ISBN:978-1-4842-4923-9)则是一部更精炼的参考书,它汇总了截至 C++17 标准为止 C++ 标准库提供的所有核心数据结构、算法与函数。1 附录 B“带注释的参考书目”中还列出了更多参考资料。另有两个非常优秀的在线参考站点:

  • cppreference.com:你可以在线使用,也可以下载离线版本,以便在无法联网时查阅。
  • cplusplus.com/reference

本书中凡提到“标准库参考(Standard Library Reference)”,指的就是这类详尽的 C++ 参考资料。

以下还有一些同样非常出色的在线资源:

  • github.com/isocpp/CppCoreGuidelinesC++ Core Guidelines 由 C++ 语言的发明者 Bjarne Stroustrup 牵头,是跨多个组织、多年讨论与设计的协作成果。其目标是帮助开发者高效地使用现代 C++。这套指南更关注相对高层的问题,例如接口、资源管理、内存管理以及并发。
  • github.com/Microsoft/GSL:这是微软对 Guidelines Support Library(GSL)的实现,包含 C++ Core Guidelines 推荐使用的函数与类型。它是一个仅包含头文件的库。
  • isocpp.org/faq:这里收集了大量常见的 C++ 问题与解答。
  • stackoverflow.com:你既可以搜索常见编程问题的答案,也可以自己提问。

为了帮助你更高效地阅读本书并跟上讲解节奏,全书采用了若干统一约定。

像这样的方框用于承载重要且不应忽略的信息,并且它们与上下文内容直接相关。

正文中的格式约定如下:

  • 重要术语在首次出现时使用 斜体 表示。
  • 键盘按键写法示例如下:Ctrl+A。
  • 文件名以及正文中的代码会以这样的形式展示:monkey.cpp
  • URL 会以这样的形式展示:wiley.com

代码会以三种不同方式呈现:

// 代码中的注释显示如下。
在代码示例中,新的且重要的代码会以这种方式突出显示。
在当前语境下不那么重要,或前文已经出现过的代码,会以这种形式呈现。

带有 C++23 小图标的段落或章节,表示其中内容专门针对 C++23 标准,就像本段一样。C++11、C++14、C++17 与 C++20 的特性不会额外标注图标。

使用本书,你只需要一台装有 C++ 编译器的计算机即可。本书只关注已经标准化的 C++ 内容,不涉及各家厂商特有的编译器扩展。

你可以使用任何你喜欢的 C++ 编译器。如果你还没有 C++ 编译器,也可以免费下载一个。可选项很多。例如,在 Windows 上,你可以下载免费的 Microsoft Visual Studio Community Edition,其中包含 Visual C++;在 Linux 上,则可以使用同样免费的 GCC 或 Clang。

接下来的两个小节会简要说明如何使用 Visual C++ 与 GCC。更详细的信息,请参考你所使用编译器附带的官方文档。

本书讨论了 C++23 标准引入的新特性。在我写作本书时,还没有任何编译器能够完整符合 C++23 标准。有些新特性只被部分编译器支持,另一些则尚未被任何编译器支持。各家编译器厂商都在积极补齐这些新特性,我相信用不了太久,就会出现完整支持 C++23 的编译器。你可以在 en.cppreference.com/w/cpp/compiler_support 上持续查看不同编译器对各项特性的支持情况。

在我写作本书时,并非所有编译器都已经完整支持模块;不过至少主流编译器都已经在一定程度上支持了它。本书会始终使用模块。如果你的编译器尚未支持模块,也可以像 第 11 章“模块、头文件及其他主题”中简要说明的那样,把使用模块的代码改写为不使用模块的形式。

首先,你需要创建一个项目。启动 Visual C++ 2022,在欢迎界面点击 Create A New Project。如果欢迎界面没有显示,请选择 File ➪ New ➪ Project。在 Create A New Project 对话框中,搜索带有 C++、Windows 和 Console 标签的 Console App 项目模板,然后点击 Next。接着指定项目名称和保存位置,再点击 Create

新项目加载完成后,你会在 Solution Explorer 中看到项目文件列表。如果这个停靠窗口未显示,请选择 View ➪ Solution Explorer。新建项目默认会在 Source Files 节点下包含一个名为 <projectname>.cpp 的文件。你可以直接在该 .cpp 文件中编写自己的 C++ 代码;如果你想编译本书配套源码压缩包中的源文件,也可以先在 Solution Explorer 中选中 <projectname>.cpp 并删除它。你可以通过右键单击项目名,然后选择 Add ➪ New ItemAdd ➪ Existing Item 来添加新文件或已有文件。

在我写作本书时,Visual C++ 2022 还不会自动启用 C++23 特性。若要开启 C++23,请在 Solution Explorer 窗口中右键项目并选择 Properties。在 Properties 窗口中进入 Configuration Properties ➪ General,把 C++ Language Standard 选项设置为 ISO C++23 Standard,或设置为你当前 Visual C++ 版本中可用的 Preview - Features from the Latest C++ Working Draft,然后点击 OK

最后,选择 Build ➪ Build Solution 来编译你的代码。编译无误后,即可通过 Debug ➪ Start Debugging 来运行程序。

你可以使用任意喜欢的文本编辑器创建源代码文件,并将其保存到某个目录。要编译代码,打开终端并运行以下命令,显式列出你需要编译的所有 .cpp 文件:

g++ -std=c++2b -o <executable_name> <source1.cpp> [source2.cpp]

-std=c++2b 选项用于通知 GCC 启用 C++23 特性。待 GCC 完整支持 C++23 后,这个选项会变为 -std=C++23

在 GCC 中,可通过 -fmodules-ts 选项启用模块支持。

在我写作本书时,GCC 尚不支持 第 1 章 中提到的 C++23 标准命名模块 std。要让此类代码能够编译,你需要先把 import std; 替换为对具体标准库头文件的 import 声明。完成这一步后,对于如下这类标准库头文件的 import 声明,还需要先对其进行预编译:

import <iostream>;

以下是对 <iostream> 进行预编译的示例:

g++ -std=c++2b -fmodules-ts -xc++-system-header iostream

例如,第 1 章 中的 AirlineTicket 示例使用了模块。要让它在 GCC 下编译,首先需要把 std::println() 改写为 std::cout,因为在我写作本书时 GCC 还不支持 <print> 功能。接下来,把 import std; 替换成合适的 import 声明;在这个例子中需要的是 <string><iostream>。你可以在可下载源码归档中的 Examples\Ch00\AirlineTicket 目录下找到已经调整好的代码。

然后,先编译两个标准头文件 <iostream><string>

g++ -std=c++2b -fmodules-ts -xc++-system-header iostream
g++ -std=c++2b -fmodules-ts -xc++-system-header string

再编译模块接口文件:

g++ -std=c++2b -fmodules-ts -c -x c++ AirlineTicket.cppm

最后,编译应用程序本身:

g++ -std=c++2b -fmodules-ts -o AirlineTicket AirlineTicket.cpp AirlineTicketTest.cpp AirlineTicket.o

编译成功后,可以按如下方式运行:

./AirlineTicket

第 2 章“字符串与字符串视图”中解释过:你可以很方便地把标准库容器(例如 std::vector)中的全部内容打印到屏幕上。这是 C++23 的新特性,而在我写作本书时,并不是所有编译器都已支持它。

举例来说,第 2 章 说明了你可以像下面这样输出 std::vector 的内容。如果你现在还看不懂全部语法,也不用担心——读完 第 2 章 后你就会明白:

std::vector values { 11, 22, 33 };
std::print("{:n}", values);

它的输出结果为:

11, 22, 33

如果你的编译器尚不支持这种用 std::print() 打印容器内容的 C++23 特性,那么可以把第二行代码改写为:

for (const auto& value : values) { std::cout << value << ", "; }

其输出结果为:

11, 22, 33,

同样地,如果你现在还不理解这段语法,也不必担心。读完 第 2 章 之后,一切都会变得清晰。

以下小节说明了获取本书相关支持的几种方式。

在学习本书示例时,你既可以选择手动输入全部代码,也可以直接使用本书附带的源码文件。不过,我仍然建议你尽量自己动手录入代码,因为这会显著提升学习效果,也更有助于记忆。本书全部源码都可以从 www.wiley.com/go/proc++6e 下载,也可以从 GitHub 仓库 github.com/Professional-CPP/edition-6 获取。

下载完代码后,只需使用你喜欢的解压工具将其解压即可。

如果你认为自己在本书中发现了错误,请务必告知我们。John Wiley & Sons 深知为读者提供准确内容的重要性,但即便我们已经尽了最大努力,仍然可能出现疏漏。

如果你想提交勘误,请发送邮件至客户服务团队 wileysupport@wiley.com,邮件主题请写为 “Possible Book Errata Submission”。

如果你在阅读本书过程中有任何问题,可以通过 marc.gregoire@nuonsoft.com 联系作者。作者会尽量及时回复。

  1. 在我写作本书时,一本更新版的 C++23 Standard Library Quick Reference 正在编写中。它与前作一样,是一部精炼的参考书,但将纳入全部 C++20 与 C++23 特性。