工程哲学:Bjarne Stroustrup,没有额外开销的抽象

核心要点
- 没有额外开销的抽象。 Stroustrup构建C++,就是要终结”清晰代码”与”贴近底层的速度”之间的虚假取舍——表达力强的写法和手工调优的写法,应当编译成同样的机器码。
- 零开销原则。 “用不到的,你不必为之付费;用得到的,你再怎么手写也不可能更好”——这是每一项C++特性都必须通过的规则。
- RAII让它落到实处。 把资源的生命周期绑定到栈上对象,便能在没有垃圾回收器的情况下,获得确定性、异常安全的清理——后来Rust几乎原封不动地采纳了这一模式。
- 下方不留余地。 C++必须始终是最高层的语言,在它与硅片之间除了汇编器再无他物,否则一门更底层的语言就会取而代之。
原则
“用不到的,你不必为之付费。更进一步:用得到的,你再怎么手写也不可能更好。” —— Bjarne Stroustrup,零开销原则1
这句话是系统编程中最重要的一句,它承载着两重承诺。前半句说的是:抽象在不使用时必须是免费的——动用某项特性,它就要花费你一些代价;忽略它,它就分文不取——既不占一字节内存,也不耗一个运行周期。后半句则是更难兑现的承诺:当你确实使用这项抽象时,编译器产出的机器码至少要和一位称职程序员针对这个具体任务手写的一样好。一个在发布模式下不做任何边界检查的vector。一个编译成裸指针的智能指针。一个消融进原始循环里的迭代器。清晰是免费的。
大多数语言都强迫你做取舍。你要么选择高层、表达力强但缓慢的写法,要么退到底层、快速却难以阅读的写法——于是整门手艺就变成了在哪一天接受哪一种痛苦。Stroustrup的整个职业生涯都是对这种取舍的拒绝。他曾亲身体会过这种痛:读博时,他热爱Simula的抽象,却嫌它在实际工作中太慢;他热爱BCPL的速度,却嫌它太底层、无法用来构建任何大型程序。2C++之所以存在,正是因为他认定:你本不该被迫选择。效率与抽象不是两个需要权衡的对立旋钮;它们本就该一同到来,出现在同一行代码里。
这份信念,与另一个论点底下的信念是同一个——性能不是你添加上去的功能,而是你设法不去丢失的属性。零开销的抽象,不是在一门慢语言之上搭起来的一个快东西。它是一个清晰的东西,编译器会把它重新碾回到与你手写完全一致的样子,于是你永远不必为清晰付费。”我喜欢自己的代码既优雅又高效,”他说过——而要紧的词是那个又字。他厌恶在二者之间做选择。3
背景
Bjarne Stroustrup于1950年12月30日出生在丹麦奥胡斯。4他在奥胡斯大学攻读数学与计算机科学,之后赴英国剑桥大学计算机实验室读博,并于1979年完成学业。他的论文——《分布式计算机系统中的通信与控制》——研究的是如何把操作系统的工作分散到一个计算机网络上。4
为了研究这个问题,他需要构建一个模拟器,而第一版是用Simula写的。Simula是挪威人(由Ole-Johan Dahl和Kristen Nygaard设计)开发的语言,它向世界引入了类与对象的概念。Stroustrup很喜欢它。Simula的类抽象让他能够按照问题本身的组织方式、而非机器的组织方式,去编排一个庞大而错综的程序——写起来、改起来都是一种享受。然而当他试图在大规模场景下运行时,这些抽象在自身的开销下崩溃了。它的实现太慢,不切实际。他改用BCPL——一门精简的系统语言——重写了模拟器,速度回来了,但BCPL太底层,用它构建任何大型程序都是一场煎熬。2这一记急刹般的反差,是塑造他的那道创伤。他用过表达力强的语言,代价是性能;又用过快速的语言,代价是其余的一切。两者都不可接受。这两半本该能够同时成立。
1979年,他带着这份怨念来到新泽西的贝尔实验室,在那里再次面对一个分布式系统的问题:分析UNIX内核在网络上的流量。想起在剑桥的经历,他着手把Simula的组织能力加进C语言——一门本就快速、贴近硬件、作为UNIX系统语言的语言——而且不愿牺牲C的一丝速度。45他把成果称为“带类的C”(C with Classes)。1983年,他的同事Rick Mascitti建议取名为C++,也就是把自增运算符用在C自己身上:下一个C,更进一步,底下还是同一台机器。5

工作
从”带类的C”到C++:一门不花费任何代价的更好的C
最初那一步(1979—1983)是刻意保守的,也恰到好处。拿来C——本就快速、本就直接映射到硬件、本就是人们信任的系统语言——再加上Simula教会他想要的那些东西:类、更好的类型检查、数据抽象。5关键在于,他把这些东西作为一个C程序员无需为之付费的层加了进去。一个C程序仍然是合法的C++程序。一个不使用任何新特性的struct,编译出来还是它一贯的那些字节。新的能力是可选的,使用它所花的代价,仅仅等同于手写等价C代码所要花的。这就是零开销原则的雏形,在他还没给它起名字之前就已存在:不要向那些不使用该特性的人征税,也不要让该特性生成比他们自己手写更糟的代码。
他在《C++程序设计语言》(1985)中记录并稳定了这门语言——正是这本书让它得以普及——随后又用数十年时间,通过对此后每一项特性的设计去守护它的内核。4
零开销原则
零开销原则是那个承重的核心理念,是衡量每一项C++特性的尺子。Stroustrup把它分作两部分陈述:“用不到的,你不必为之付费(无论时间还是空间);更进一步:用得到的,你再怎么手写也不可能更好。”1第一句是一项税收政策——任何特性都不得向不使用它的代码强加成本。第二句是一道质量标准——你确实使用的特性,其编译结果不得比一位细致的程序员针对这件具体工作手写出来的更差。模板、constexpr、移动语义,以及标准库的容器和算法,全都是为遵守它而设计的;而那两个著名的例外——运行时类型信息(RTTI)与异常——恰恰就是编译器提供开关供你关闭的那两项特性,正因为它们违背了这条规则。6
回报就是那件你不亲眼看到汇编代码就会觉得不可能的事:表达力强的写法和手工调优的写法,产出同样的机器码。

正是这种同一性——表达力强与手工调优坍缩成同一份产出——使得这条原则不仅是技术性的,也是道德性的。它剥夺了工程师惯用的那个借口。倘若清晰当真昂贵,你就可以理直气壮地写出那个丑陋而快速的版本,”因为它必须快”。Stroustrup的赌注是:这个借口通常是个谎言——一个设计良好的抽象,能让你同时拥有易读的版本和快速的版本,因为它们就是同一个版本。这份代价只被付过一次,由语言设计者付出,从此程序员便再也不必付了。
RAII与确定性资源管理
这条原则需要一套机制,来应对系统编程中最棘手的部分:那些必须先获取、然后再可靠释放的资源——内存、文件、锁、套接字。垃圾回收对此的回答是放弃确定性:清理终将发生,按回收器的时间表来,而这对一把锁或一个文件句柄是无法容忍的。Stroustrup的回答——他将其命名为RAII——资源获取即初始化(Resource Acquisition Is Initialization)——是把资源的生命周期绑定到栈上某个对象的生命周期。7你在构造函数里获取资源;在析构函数里释放它;而由于C++保证栈上对象的析构函数会在它离开作用域的一瞬间运行——无论是正常离开还是经由异常,且以构造的逆序进行——资源便会恰在它应当被释放之时确定性地释放,不需要回收器,也不存在可能被遗忘的手动free。7
RAII就是把零开销落到了实处。那次析构函数调用,本就是你无论如何都得手写的代码;语言只是保证你不可能忘记写它。你得到的是自动、异常安全、确定性的清理,而你所付出的,分毫不超过你自己手写清理代码本就要付的。这正是那个被后来的语言——其中最著名的是Rust——几乎原封不动采纳的C++模式。
标准化,以及”下方不为更底层的语言留余地”
Stroustrup没有把C++当作个人项目攥在手里。他把它交给了标准委员会——1989年起进入ANSI,1991年起进入ISO——并在其中服务了数十年,担任演进工作组(Evolution Working Group)的主席,好让C++成为一个稳定、中立于厂商的标准,而非某一家公司的方言。4他还给委员会立下一条根本性规则,源自他在《C++的设计与演化》中的设计原则:“不要在C++之下为任何更底层的语言留余地(汇编器除外)。”8其推理是精确的——如果你能用某门介于C++与裸机之间的语言写出更高效的代码,那么那门语言就会成为系统编程的首选,C++也就失去了存在的理由。8为保持生命力,C++必须保留C对硬件的直接访问、对数据布局的控制,以及那些与机器一一对应的基本类型。8在抽象与硅片之间,除了汇编器,不该有任何东西插足。零开销原则,换作一条竞争边界来陈述就是:做那门仍然不在桌上留下任何东西的、最高层的语言。
方法
这套方法只押一个赌注——只要你把抽象设计对了,清晰与速度就是同一回事——并把它作为一组硬性规则来强制执行。
用零开销来衡量每一项特性。 任何特性都不得向不使用它的代码征税,也不得生成比手写等价物更糟的代码。这道关口,每一项C++的新增内容都得通过,四十年来从未例外。16
信任程序员,并保持多范式。 C++支持过程式、面向对象、泛型和函数式风格,因为Stroustrup拒绝替你决定该如何组织你的问题。语言把工具交给你,并假定你比语言设计者更懂你自己的领域。6
直接映射到硬件。 保留C在基本类型、数据布局与机器之间的一一对应。抽象坐落在金属之上,而非架在一层把金属藏起来的运行时之上。8
让清理是确定的,而非”终将发生”的。 RAII把资源生命周期绑定到对象生命周期,于是释放发生在一个已知的瞬间,异常安全,既无垃圾回收器,也没有什么需要记着去释放。7
在公开场合把它稳定下来。 把这门语言交给一个国际标准委员会,而非自己占有它,这样C++就是一个经久的公共标准,任何单一厂商都无法将它分叉。4
影响链
谁塑造了他
Ole-Johan Dahl与Kristen Nygaard,经由Simula。 Simula教会了Stroustrup抽象是为了什么——让一个程序得以按照问题的结构来构建——而它的缓慢则教会了他这堂课的另一半:如果你用不起一项抽象,那它就一文不值。C++正是这样一次尝试:既保住Simula的馈赠,又不付出它的任何代价。(直接的、塑造性的影响)
BCPL,以及围绕C与UNIX的系统编程文化。 BCPL给了他相反的那种痛——纯粹的速度,却无从组织任何大型的东西——而C,作为贝尔实验室里UNIX的语言,给了他那个快速、映射硬件的根基,让他得以在其之上构建C++,而非取而代之。25(直接影响)
贝尔实验室。 正是这个孕育了C与UNIX的同一所机构,把那个催生”带类的C”的分布式系统问题交给了Stroustrup,也给了他那种容许他花数年时间把语言打磨对的研究文化。(塑造性的影响)
他塑造了谁
世界的基础设施跑在他的语言之上。 操作系统、浏览器、数据库、游戏引擎、编译器、高频交易系统、火星车的飞行软件——现代计算中那些性能攸关的核心,C++占了不成比例的一大块。鲜有工程师的工作能承重至此。
零成本抽象运动。 Stroustrup的原则,被改名为”零成本抽象”,成为Rust一个明确的立项目标——Rust采纳了RAII(以所有权和Drop的形式)、确定性析构,以及”高层构造必须编译成最优机器码”的规则。像D和Carbon这样的语言,都出自同一条血脉。”你不该为清晰付费”这一理念如今已成主流,而它出自他。6
贯穿线
John Carmack希望内层循环只做物理允许范围内最少的工作——“光速糟透了”,机器受制于硬性极限,每一个浪费的周期都是一次选择。Stroustrup则把工具交到Carmack手里,让他能以可读的方式写出那段循环:零开销的抽象,正是这样一个承诺——清晰的版本和最贴近机器的版本是同一份机器码,于是你永远不必二选一。如果说Barbara Liskov为抽象给出了理论——数据抽象、一个类型欠其使用者的契约、那条从Simula到CLU的”你应当针对一个东西做什么来编程,而非它如何工作”的血脉——那么Stroustrup就为那套理论赋予了一具运行起来不花费任何代价的躯体。而如果说Alan Kay把对象设想为彼此传递消息的小计算机,一场后期绑定、运行时的对话,那么Stroustrup则沿着相反的道路走向了同一个词:他的对象在编译期就被解析,并消融为裸机器码,OOP在他这里是一件零开销的组织工具,而非一条活的消息总线。同样的词汇,两种哲学——一种为灵活性优化,另一种为运行时的那张账单优化。(系列桥接)
我从中汲取了什么
我一直记着的那一课是:在”表达力强还是快”这道选择题里,几乎总是糟糕的设计强加给你的一道伪命题。当我发现自己正在写那个丑陋的版本、”因为它必须快”时,Stroustrup的原则就是一句指控:那并非计算的某条定律,而是你没能找到那个能自行编译成快速结果的抽象。要做的功课,是把清晰的版本设计得足够好,好到机器能从中产出最优的代码——把代价在设计阶段付一次,从此使用它的每一行里都不必再付。这与质量是唯一的变量是同一道标准:问题从来不是”我承担得起把它写清晰吗?”,而是”我的抽象怎么会漏到让清晰要花费任何代价的地步?”
在我如今所构建的世界里——智能体、工具循环、AI系统——诱惑恰恰是零开销的反面:在一层包装上再叠一层包装,吞下一次模型调用来掩盖一条慢路径,任由每一个图省事的抽象悄悄增加无人测量的延迟。Stroustrup式的做法,是要求那个方便的接口编译成那个高效的接口——让干净的API与快速的路径成为同一条路径,代价付在框架的设计里,而非记到流经它的每一次调用头上。那份信念——即品味是一套技术系统,你能拿一条硬性规则去对照它,而不是靠嘴硬主张的一种感觉——正是那条从1979年一个跑得太慢的模拟器,贯穿到2026年一个不该跑得太慢的智能体框架的线。
常见问题
Bjarne Stroustrup的工程哲学是什么?
Stroustrup坚信:你永远不该被迫在表达力强的高层代码与贴近底层的性能之间做选择。他以零开销原则构建了C++:一项语言特性在你不使用它时必须分文不取,而当你使用它时,它的编译结果不得比你针对那件任务手写的更差。1与此搭配的,还有信任程序员(C++刻意做成多范式)、直接映射到硬件、通过RAII实现确定性的资源管理,以及把这门语言作为一个开放的ISO标准稳定下来而非自己占有它。467
零开销原则是什么?
这是Stroustrup分作两部分陈述的规则:“用不到的,你不必为之付费(无论时间还是空间);更进一步:用得到的,你再怎么手写也不可能更好。”1前半句意味着一项未被使用的特性不强加任何运行时或内存成本;后半句意味着一项你确实使用的特性,所生成的代码至少和一位称职程序员手写出来的一样好。这是每一项C++特性都必须通过的关口。那两项违背它的特性——运行时类型信息与异常——恰恰就是多数编译器允许你关掉的那两项。6正是这条原则,使得智能指针或迭代器这样的C++抽象,能够编译成与原始、手工调优版本相同的机器码。
什么是RAII,Stroustrup为何发明它?
RAII——资源获取即初始化(Resource Acquisition Is Initialization)——是Stroustrup命名的C++模式,它把资源的生命周期绑定到一个栈上对象的生命周期:你在对象的构造函数里获取资源,在它的析构函数里释放资源。7由于C++保证栈上对象的析构函数会在它离开作用域的那一刻运行——无论函数是正常返回,还是异常在展开栈——内存、文件、锁、套接字这类资源都会被确定性地、在一个已知的瞬间释放,不需要垃圾回收器,也没有什么需要手动去释放。它为你提供了自动、异常安全且零开销的清理,后来Rust几乎原封不动地以所有权和Drop的形式采纳了它。7
Stroustrup为何创造C++,它从何而来?
在撰写他那篇关于分布式系统的剑桥博士论文期间(1979年完成),Stroustrup用Simula构建了一个模拟器,爱上了它的类抽象,却发现它在大规模下太慢;他改用BCPL重写,后者很快,却太底层、无法用来构建任何大型程序。2两种取舍都不可接受,于是在贝尔实验室——面对一个分析UNIX内核跨网络流量的问题时——他着手把Simula的组织能力加进C,同时不损失C的速度。他把它称作“带类的C”(C with Classes)(1979);他的同事Rick Mascitti在1983年建议取名为C++。452018年,美国国家工程院授予他查尔斯·斯塔克·德雷珀奖(Charles Stark Draper Prize)——美国工程界的最高荣誉,一项常被称为工程界诺贝尔奖的50万美元大奖——以表彰他在设计与实现C++编程语言方面的工作。9
来源
-
“What is the zero-overhead principle?” Standard C++ Foundation FAQ (isocpp.org), maintained with Stroustrup. “What you don’t use, you don’t pay for (in time or space) and further: What you do use, you couldn’t hand code any better.” See also Bjarne Stroustrup, “C++ – an Invisible Foundation of Everything,” Overload (ACCU), 2021. ↩↩↩↩↩
-
“C++,” Wikipedia. While writing his PhD at Cambridge, Stroustrup used Simula but found it too slow at scale and rewrote his simulator in BCPL; he concluded Simula had features helpful for large software development but was too slow for practical use, while BCPL was fast but too low-level. Primary account: Bjarne Stroustrup, “A History of C++: 1979–1991,” HOPL-II (his own paper). ↩↩↩↩
-
Bjarne Stroustrup, “Quotes,” stroustrup.com (his own site). “I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance…” and “Code should [be] elegant and efficient; I hate to have to choose between those.” ↩
-
“Bjarne Stroustrup,” Wikipedia. Born 30 December 1950, Aarhus, Denmark; PhD University of Cambridge 1979, thesis “Communication and control in distributed computer systems”; led the Large-scale Programming Research department at Bell Labs; The C++ Programming Language (1985); founding member of the C++ standards committee (ANSI from 1989, ISO from 1991), chaired the Evolution Working Group; later Texas A&M (2002–2014), Morgan Stanley (2014–2022), full professor at Columbia (since 2022). See also Stroustrup’s Bio/FAQ on stroustrup.com. ↩↩↩↩↩↩↩↩
-
Bjarne Stroustrup, “Bjarne Stroustrup’s FAQ,” stroustrup.com. “I wanted to write efficient systems programs in the styles encouraged by Simula67… The specific tasks that caused me to start designing and implementing C++ (initially called ‘C with Classes’) had to do with distributing operating system facilities across a network.” On the 1983 rename by Rick Mascitti: “C++,” Wikipedia. ↩↩↩↩↩
-
“Zero-overhead principle,” cppreference.com. States the principle as defined by Stroustrup and notes the two features that do not follow it – run-time type identification and exceptions – and that compilers commonly include switches to turn them off. On multi-paradigm design and the influence on Rust’s “zero-cost abstractions”: “Zero-overhead principle” and Rust documentation on zero-cost abstractions. ↩↩↩↩↩↩
-
“Resource acquisition is initialization,” Wikipedia. The term RAII was coined by Stroustrup; the technique was developed for exception-safe resource management in C++ during 1984–1989, primarily by Stroustrup and Andrew Koenig, and introduced in his 1994 book The Design and Evolution of C++. Resource release happens in the destructor; C++ guarantees objects with automatic storage duration are destroyed at the end of the enclosing scope in reverse order of construction, giving deterministic, exception-safe cleanup. ↩↩↩↩↩↩
-
Bjarne Stroustrup, The Design and Evolution of C++ (1994), design rules, as discussed in “(Re)affirm design principles for future C++ evolution,” ISO C++ committee paper P3466R0. “Leave no room for a lower-level language below C++ (except assembler)” – the reasoning being that if more efficient code could be written in some lower-level language, that language would become the systems language of choice; to remain viable C++ must keep C’s direct hardware access, control over data layout, and primitive types that map one-to-one onto hardware. ↩↩↩↩
-
“Bjarne Stroustrup receives Draper Prize, engineering’s top U.S. honor,” Standard C++, 2018: the 2018 Charles Stark Draper Prize, “a $500,000 biannual award… considered the Nobel Prize of engineering,” awarded to Stroustrup “for his work designing and implementing the C++ programming language.” The National Academy of Engineering’s own citation phrases it “for conceptualizing and developing the C++ programming language” (NAE, “Computer Science Pioneer Bjarne Stroustrup to Receive the 2018 Charles Stark Draper Prize for Engineering,” via EurekAlert, 2018). He also received the IET Faraday Medal in 2017 for pioneering C++. ↩