"C++ 是被设计成语言特性不断相互作用,形成远超各个部分之和的整体。"
——比雅尼·斯特劳斯特鲁普(Bjarne Stroustrup)
这句话有时被 C++ 的批评者反过来引用:正是因为特性相互作用,C++ 变成了一种几乎没有人能完全掌握的语言,一种"危险的"语言,一种被新生代语言反复批判的靶子。但即便如此,C++ 在 2026 年仍是性能关键系统的主流语言之一——游戏引擎、操作系统、数据库、浏览器内核,很可能有 C++ 的代码在运行。这正是斯特劳斯特鲁普意图实现的:不为小型学习型项目设计,为真实的大型软件设计。
破除误解:C++ 不是"加了类的 C"
这是流传最广的误解。斯特劳斯特鲁普最初的项目名称"带类的 C"(C with Classes,1979 年)助长了这个误解,但 C++ 很快就超出了这个定义。
C++ 引入了:异常处理、模板(强大的泛型编程机制)、RAII(资源获取即初始化,Resource Acquisition Is Initialization)、运算符重载、多重继承、命名空间……这些特性组合在一起,形成的语言与 C 的关系类似于人与黑猩猩——基因序列高度相似,但行为方式和思维模型几乎完全不同。
现场:贝尔实验室与 Simula 的启发
1950 年 12 月 30 日,斯特劳斯特鲁普生于丹麦奥胡斯(Aarhus)。他在奥胡斯大学完成本科,1979 年于剑桥大学获计算机科学博士学位,论文研究了类(class)在分布式系统中的应用。
博士期间,他用 Simula(第一门面向对象语言,1967 年由挪威人 Dahl 和 Nygaard 设计)做实验。Simula 的类和对象机制令他印象深刻,但其性能不如 C——他的博士模拟程序在 Simula 上运行太慢,不得不改用 BCPL(C 的祖先之一)重写。
博士毕业后,斯特劳斯特鲁普加入贝尔实验室计算机科学研究中心,与 C 的发明者 Dennis Ritchie 在同一栋楼工作。他开始把 Simula 的思想移植到 C 中,1979 年实现了"带类的 C"预处理器(Cfront),1983 年正式命名为 C++(C 的自增,暗示"比 C 更进一步")。
C++ 的核心设计哲学
零开销抽象(Zero-Overhead Abstraction):这是斯特劳斯特鲁普给每条语言特性定下的准入门槛。他自己的表述是:"你不用的东西,你不必为它付费;而你用到的东西,你手写也不可能写得更好。"前半句的直接后果是:C++ 没有内置垃圾回收(它对不使用的程序同样是成本),多态必须显式声明 virtual 才产生虚表开销,异常处理被设计为不抛出时几乎零代价。后半句解释了模板为什么在编译期展开——泛型代码实例化之后,应当与手写特化版本一样快。这条原则把 C++ 与一切"带虚拟机的高级语言"划清了界限:抽象可以是免费的午餐,前提是成本必须在编译期结清。
RAII(资源获取即初始化):这是 C++ 最被低估的设计之一。RAII 把资源(内存、文件、锁)的获取绑定到对象的构造函数,释放绑定到析构函数,使得"对象存在期间资源有效,对象销毁时资源自动释放"。现代 C++ 智能指针(unique_ptr、shared_ptr)正是 RAII 的实现。RAII 把内存安全问题从运行时(GC 暂停)转移到了编译期约束,启发了后来 Rust 的所有权系统。
与 C 兼容:这是 C++ 最深思熟虑的一场赌注。斯特劳斯特鲁普明知 C 带着指针、宏和未定义行为的全部包袱,仍坚持让 C++ 能编译绝大多数 C 代码、能与 C 链接——因为只有这样,既有的数百万 C 程序员才能渐进迁移,而不必重写世界。这场赌注赢了 adoption,也埋下了后患:C++ 继承了 C 的危险面,批评者指责它"坏的部分来自 C",并非全无道理。
模板与泛型编程:C++ 模板(1990 年代加入)是图灵完备的编译时计算机制,可以在编译期生成高度特化的代码。Erwin Unruh 在 1994 年的一个演示程序证明,C++ 模板系统可以在编译错误信息中打印素数——模板元编程(TMP)由此成为一种奇特的子文化。标准模板库(STL)由 Alexander Stepanov 设计,是模板泛型编程的实用典范,1994 年并入 C++ 标准。
从 C with Classes 到模板:一条演化线
回看 C++ 的成形过程,几乎每个特性都是对上一阶段具体痛点的回应。
1979 年 5 月,"带类的 C"开工,同年 10 月第一个实现投入使用。它的编译器 Cfront 是一个把 C++ 翻译成 C 的前端——这个看似迂回的选择其实深思熟虑:凡是能跑 C 编译器的机器就能跑 C++,可移植性几乎免费获得。类的引入解决的是 C 程序的结构问题,而构造函数与析构函数的引入解决的是资源问题:对象的生死从此有了确定的时刻,初始化与清理不再依赖程序员的自觉。
但容器类很快撞上了墙。想写一个"整数列表、字符串列表都能复用"的 List,当时只有两条路:Smalltalk 式的动态类型(灵活,但有运行时开销,且接口错误要到运行时才暴露),或者为每种类型把代码复制一遍。斯特劳斯特鲁普选择了第三条路:参数化类型。1988 年 10 月,他在丹佛的 USENIX C++ 大会上首次公开模板设计(论文《Parameterized Types for C++》)——让编译器为每种具体类型生成特化代码,类型安全留在编译期,运行期不多花一分钱。
异常处理则是另一场更漫长的战役。设计从 1984 年拉锯到 1989 年,安德鲁·柯尼希(Andrew Koenig)与斯特劳斯特鲁普联合署名发表了最终方案,这是 C++ 第一个在完全公开审视下完成的特性。而异常的到来意外点亮了 RAII 的全部潜力:既然异常会沿调用栈"展开"并沿途调用每个局部对象的析构函数,那么只要把资源绑在对象上,无论函数正常返回还是中途抛出,资源都必然被释放。RAII 由此从一个编程好习惯,升格为 C++ 错误处理与资源管理的统一骨架。
标准化之路
1989 年,ANSI 开始 C++ 标准化工作;1998 年发布第一个国际标准(C++98)。此后 C++ 按固定周期更新标准:C++03、C++11、C++14、C++17、C++20、C++23——自 C++11 起,基本保持了三年一版的节奏。
C++11 被许多人视为现代 C++ 的起点——引入了移动语义(Move Semantics)、Lambda 表达式、auto 类型推导、nullptr、std::thread 等,大幅提升了语言的表达力和安全性。斯特劳斯特鲁普本人在 C++11 标准委员会中发挥了核心作用。
学术与工业职位
斯特劳斯特鲁普在贝尔实验室工作到 2002 年。此后在得克萨斯 A&M 大学任教,2014 年转至摩根士丹利(Morgan Stanley)担任技术部门董事总经理(2019 年成为摩根士丹利首位 Technical Fellow),2022 年 4 月退休。自 2022 年 7 月起,他在哥伦比亚大学担任计算机科学正教授(full professor)。
主要奖项:Grace Murray Hopper Award(1993)、IEEE Fellow(1994)、ACM Fellow(1994)、Charles Stark Draper Prize(2018,表彰其对 C++ 的奠基性贡献)、John Scott Medal(2018)、Dahl-Nygaard Prize(2015)。
代价与争议
"C++ 是一种危险的语言":C++ 允许程序员做几乎任何事情——包括产生未定义行为(Undefined Behavior)、缓冲区溢出、悬空指针。大量历史 CVE(通用漏洞披露)可以追溯到 C/C++ 的内存不安全。微软 2019 年报告称,过去 12 年 70% 的安全漏洞是内存安全问题。美国 NSA 2022 年建议组织迁移离开 C/C++。
斯特劳斯特鲁普的回应是:现代 C++(C++11 及以后)配合 Core Guidelines(他与 Herb Sutter 共同维护的编程规范)可以极大减少内存安全问题,问题在于旧代码和不规范使用,而非语言本身。
Rust 的崛起:Rust(2015 年稳定版)被设计为"内存安全的 C++ 替代",通过所有权类型系统在编译期消除数据竞争和悬空指针,已被 Linux 内核(2022)和 Android(2021)接受,是 C++ 遭遇最有力的竞争者。Stroustrup 认为,Rust 解决的问题 C++ 也可以解决,且 Rust 自身有复杂性代价,两者将长期共存。
委员会设计的局限:C++ 标准委员会由 200 余名来自各大公司和学术机构的代表组成,决策需要共识,导致语言特性的引入速度极慢,有时为向后兼容牺牲优雅性。批评者认为 C++ 的复杂性部分是委员会政治的结果,而非技术必要性。Stroustrup 承认委员会过程并不完美,但认为多方参与保证了语言适合真实工业需求,而非学术偏好。
跨域连接
- 制度经济学:每个新版本都必须让既有代码继续编译,于是特性只能叠加不能替换,复杂度单调上升。这与制度演化同构:既有承诺是沉没的协调资产,推翻它的代价由所有人分担,收益却归少数人。推论因此是结构性的——真正简化语言的改动只会出现在新语言里,不会出现在同一语言的下一版里。
- 流行病学:内存安全漏洞在漏洞总量中长期占稳定的大比例,这说明它不是个别程序员的失误,而是暴露面的结构性属性。对策的形状随之改变:与临床式的个体培训相比,改变默认(安全抽象、默认边界检查、迁移到有所有权约束的语言)收益要大得多,正如公共卫生优先改水而不是逐个劝人洗手。
- 社会选择理论:需要共识的委员会天然偏向"加"而不是"减"。删除会明确伤害某个既有用户群,添加只是让不用的人忽略它——在接近一致同意的规则下,减法几乎不可能通过。这条推论与技术必要性无关:这类过程产出的语言必然单调膨胀,规模是决策规则的产物。
- 公共池塘资源治理:资源管理的核心问题从来是"谁负责归还"。RAII 的答案是给每份资源指定唯一负责人,并让负责期与作用域重合;垃圾回收走的是相反路线——取消个体责任,改由中央定期清理。两条路的失效模式因此不同:前者卡在所有权难以表达的场景,后者卡在停顿与延迟。
- 可计算性:模板实例化本身是一个可以不终止的求值过程,图灵完备这句话的另一面就是编译可能停不下来,错误也无法在语法层面被局部定位。编译期计算的表达力与它那些著名的诊断信息,来自同一个事实。
人物小记:坚持与孤独
斯特劳斯特鲁普是 C++ 标准委员会的持续参与者——他从 1989 年委员会成立之日起就参与标准化工作,超过 35 年。这在编程语言历史上是罕见的:语言的发明者持续参与语言的演化,跨越了超过十个标准版本。
他在一次采访中说:"如果你认为 C++ 很复杂,那是因为你在解决的问题本身就很复杂。如果语言看起来很简单,那通常是因为它的复杂性被转移到了别的地方——运行时、框架,或者程序员的脑子里。"
他的个人网站 stroustrup.com 至今仍由他本人维护,包含了他对 C++ 常见批评的逐一回应——这种持续的公开辩护姿态在软件工程界是独特的。
参考文献
- Stroustrup, B. The C++ Programming Language. 4th ed., Addison-Wesley, 2013.(C++ 的权威参考书,由语言发明者撰写)
- Stroustrup, B. The Design and Evolution of C++. Addison-Wesley, 1994.(设计决策的历史背景,理解 C++ 为什么是现在这个样子的最佳读物)
- Stroustrup, B. A History of C++: 1979–1991. ACM SIGPLAN HOPL-II, 1993.(C++ 早期演化的第一手记录)
- Stroustrup, B. Parameterized Types for C++. Proc. USENIX C++ Conference, 1988.(模板设计的原始论文)
- Koenig, A. & Stroustrup, B. Exception Handling for C++. Journal of Object-Oriented Programming 3(2), 1990.(异常机制的最终方案)
- Dahl, O.-J. & Nygaard, K. SIMULA: An ALGOL-based Simulation Language. CACM 9(9), 1966.(Simula 原始论文,C++ 的思想来源)
- Stepanov, A. & Lee, M. The Standard Template Library. HP Labs Technical Report HPL-94-61, 1994.(STL 设计原始文档)
延伸阅读
- Stroustrup, B. A Tour of C++. 3rd ed., Addison-Wesley, 2022.(简洁的 C++ 现代特性导览)
- Meyers, S. Effective Modern C++. O'Reilly, 2014.(C++11/14 最佳实践)
- Stroustrup, B. & Sutter, H. C++ Core Guidelines. github.com/isocpp/CppCoreGuidelines.(现代 C++ 编程规范,持续维护)