C++之父 Bjarne 最新发声:21世纪代码该怎样写?

开发
本文根据 Bjarne Stroustrup 的最新文章“21st Century C++”内容进行归纳总结,总结了现代 C++(尤其是 C++20/23/30 版本)在语法特性、资源管理、模块化、泛型编程以及编程准则等方面的重要进展。

本文根据 Bjarne Stroustrup 的最新文章“21st Century C++”内容进行归纳总结,总结了现代 C++(尤其是 C++20/23/30 版本)在语法特性、资源管理、模块化、泛型编程以及编程准则等方面的重要进展。希望能帮助读者更好地理解当代 C++ 的核心理念,并在实际开发中落地应用。

前言:为何需要“21世纪的 C++”?

C++ 自 1979 年构思至今,已有 40 多年历史。在这漫长的发展过程中,C++ 保留了与早期版本的兼容性,却也不断进化,新增了许多语言特性和标准库功能。Bjarne Stroustrup 在其文章中强调:

  • 兼容性是 C++ 的重要特征,老代码几乎无需修改就能继续运行。
  • 但如果仅停留在上世纪末的使用方式,往往会降低开发效率,并产生许多安全与性能隐患。

因此,面向“21 世纪”的现代 C++(尤其是 C++20/23/30)在语言理念和应用实践上,都提出了 更简洁、更安全、更可维护、更高性能 的方案。例如 RAII(自动资源管理)、模块(module)、概念(concept)等。通过合理使用这些现代特性与指导原则,我们可以写出既简洁又安全、既通用又高性能的 C++ 代码。

一、C++ 理想与关键特性

1. C++ 理想

Stroustrup 总结了 C++ 设计与实践所追求的几个主要目标(从 1980 年代至今基本未变):

  • 直接表达抽象:让程序员能直接表达所思考的概念,不必屈从于底层实现细节。
  • 静态类型安全:在编译期捕获尽可能多的错误;类型错误越早发现越好。
  • 资源安全(RAII):通过对象的构造与析构自动管理资源,避免泄漏和手动释放的不确定性。
  • 零开销抽象:抽象不能带来额外的性能负担,“用不到就不付费,用到也只付最少的代价”。
  • 直接访问硬件:对底层系统的可控性和可移植性。
  • 可维护性:代码可读、易于修改;同时兼顾高性能和大规模项目需求。
  • 平台独立:跨平台特性优先。
  • 稳定性/兼容性:保证老代码能在新环境下继续运行。

基于这些理想,C++ 从早期的面向对象特性(class/继承/虚函数)一路演进到模板、泛型编程、lambda、概念(concepts)、模块(modules)等,不断扩展着语言应用边界。

2. 老特性与新特性

C++ 的一些“老”特性(class、构造/析构、异常、模板、std::vector 等)依旧是当代 C++ 程序的基石;而一些“新”特性(模块、概念、lambda、ranges、constexpr/consteval、并发库、协程等)正为应用开发提供更加灵活且更高层次的抽象支持。

注意:并非只有最新特性才是“好”的,关键在于恰当组合,并遵守现代编程准则。

二、资源管理:RAII 与异常处理

1. RAII 核心原则

RAII(Resource Acquisition Is Initialization)是现代 C++ 保证资源安全的重要基础。其本质是:

  • 构造函数中完成资源获取,
  • 析构函数中完成资源释放,
  • 通过作用域退出来自动释放资源,从而避免手动释放的麻烦与风险。

举个简单例子,一个自定义 Vector 若自己管理内存,就应在构造函数中分配,在析构函数中回收,以防止内存泄漏;而在函数调用栈退栈时,Vector 会自动析构,从而释放资源。

2. 避免裸指针与显式 new/delete

如果在函数中直接使用 new 返回裸指针,一旦在中途抛异常或函数提前返回,资源就容易泄漏。现代 C++ 强调:

  • 尽量使用容器(例如 std::vector),或
  • 使用智能指针(例如 std::unique_ptr、std::shared_ptr)。

从而减少甚至杜绝显式 delete。加之异常处理(exception)或错误码检测,可以有效防止资源泄漏和悬空指针(dangling pointer)的问题。

3. 错误处理:异常 vs. 错误码

C++ 的异常是一种自带栈展开(unwinding)并自动调用析构的机制,可以“保证 RAII 不失效”。它比较适用于无法在本地处理的“真正异常”场景。同时,对那些在函数局部就能处理好的小错误,可以使用返回值或错误码进行判断。

Stroustrup 强调,不必在所有情况下都只用返回值或只用异常,这两者各有适用场景。

三、模块化与 import

1. 头文件与宏的历史问题

C++ 继承了 C 的预处理器(#include 和宏),它给“模块化”带来很多隐患:

  • 包含次序敏感:同样的头文件顺序不同可能带来不同结果。
  • 包含过载:一个大型头文件往往被重复编译多次,浪费编译时间。

2. 模块(Modules)优势

现代 C++(C++20 起)引入了原生“模块”概念:

  • import 的顺序不影响编译效果,避免了头文件中宏定义与嵌套包含的混乱。
  • 一次编译后即可重复使用,大幅减少重复编译时间。

Stroustrup 举的例子表明,一个包含 40~50 万行的巨型库,如果由传统的 #include 方式引入,编译需要 1.5 秒×N;而换用 import 后或只需编译一次,就能在之后的多个源文件中快速复用,可能只耗时几十分之一甚至更少。

对于大型工程,采用模块化改造虽然需要投入,但能极大改善编译性能与可维护性。

四、泛型编程与概念(Concepts)

1. 泛型编程在标准库中的应用

C++ 标准库中大量使用模板来实现容器(std::vector、std::list……)、并发(std::thread、std::jthread……)等特性,其理念是:一次编写,适应多种类型。但传统模板检查时,错误信息往往冗长而晦涩。

2. 概念(Concept)的引入

C++20 带来的“概念”提供了一种“更清晰”的模板形参约束方式。

  • 概念本质上是一个在编译期执行的布尔函数,用来判断某个类型是否满足特定接口/用法要求。
  • 这让模板的可读性和错误提示大幅提升。
template<Sortable_range R>
void sort(R& r) { /* ... */ }

若传入一个不满足 Sortable_range 的类型,例如 std::list,编译期就能给出更准确的错误位置,而不再是一长串模版展开报错。

3. constexpr 与 consteval

C++20 还强化了编译期计算的能力:

  • constexpr:可在编译期求值,也可在运行期执行。
  • consteval:必须在编译期执行。
  • 概念(concept)也是类似的“编译期布尔函数”。

这些机制让我们可以在编译阶段就完成更多逻辑,从而减少运行期开销,或尽早捕获错误。

五、编程准则与“Profile”机制

1. 为什么需要“准则”?

C++ 虽然强大,但也常被诟病“太复杂”,且有些老式用法带来安全风险。例如:

  • 数组越界、裸指针悬空、重复释放……
  • 不恰当的类型转换、隐式转换导致的微妙 Bug……

而编译器本身又不能硬性禁止所有潜在不安全用法(因为要兼容遗留代码)。因此,“编程准则”就成了提升代码安全与质量的必经之路。

2. C++ Core Guidelines

Stroustrup 牵头的 “C++ Core Guidelines” 提供了一系列核心规则,涵盖资源管理、智能指针、容器与迭代器、异常安全等方方面面。主要目标是:

  • 默认禁止不安全用法(如裸指针遍历数组、显式 delete 等),
  • 在必要时可以使用更底层、更灵活的方式,
  • 通过静态检查(分析工具)+ 运行时检查(必要的边界检测)来确保规则得以贯彻。

3. Profile:强制化子集

有了准则,为了让它真正落地,社区和标准委员会提议了“Profile”方案,即:

  • Profile 是一组可被工具强制执行的规则集合。
  • 可以针对不同应用领域(比如嵌入式、安全关键系统等)选择适合的 Profile,编译器或静态分析工具会拒绝不符合该 Profile 的写法。

比如,[[profile::enforce(type)]] 告诉编译器“不允许任何隐式转换或类型不安全的操作”,并在检测到时进行报错。这种做法能最大化地利用现代 C++ 优势,同时仍能在需要时提供后门(可局部 suppress 规则)。

六、未来发展

Stroustrup 在文章末尾提到,目前社区正持续推动以下方向:

  • 并行与异步计算:让数据并行/任务并行更易用、更安全。
  • 静态反射:编译期能获取更多关于类型与成员的信息,类似“编译期元编程”。
  • 契约(Contract):在函数接口层面可声明“先决条件/后置条件”,让编译器或运行时更好地检测逻辑错误。
  • 模式匹配(Pattern Matching):借鉴函数式语言或其他语言特性,为分支逻辑提供更直观的写法。
  • 单位/量纲库:让物理量、单位换算在语言层面更好地表达,减少人为换算出错。

这些特性都在活跃讨论中,部分已有实验/提案版实现。

总结:拥抱当代 C++

从上世纪的 C with Classes,到如今的 C++20/23/30,C++ 已经走过了漫长的演化之路。它并非只是“面向对象”或“模板编程”,而是一个融合多范式(过程式、面向对象、泛型、函数式、元编程等)的大工具箱。

如果你还在使用 90 年代甚至更早风格的 C++,可以尝试逐步过渡到现代写法:

  • 资源管理:优先使用标准容器和智能指针,借助 RAII 保证安全和简洁。
  • 模块化:告别庞大的头文件和复杂的编译依赖,尝试 import 来替代大量 #include。
  • 泛型与概念:利用 concept 提升可读性和模板类型安全。
  • 编程准则:引入 C++ Core Guidelines,使用工具(静态/动态分析)帮你自动检测不安全用法。
  • Profile:期待标准落地后,可进一步强制或部分强制在项目中执行安全子集,让 C++ 更易维护。

C++ 的发展离不开众多程序员的实践与反馈,也离不开生态和工具的共同完善。对个人或团队而言,掌握并践行“21 世纪 C++”的核心理念,是写出高质量、可维护且高效的代码之关键。

责任编辑:赵宁宁 来源: everystep
相关推荐

2019-10-08 10:35:53

编译Linux内核

2013-06-03 09:36:24

21世纪代码写代码

2017-09-11 10:37:56

编程语言名单

2024-04-22 11:40:50

2011-04-20 13:14:33

BlackBerry黑莓RIM

2013-06-03 10:18:59

WindowsLinux微软Office

2022-11-14 12:38:29

2013-05-21 16:20:40

2012-10-09 13:41:09

数据科学家职业

2020-03-23 13:43:00

数据科学家大数据数据

2020-04-10 10:11:15

数据泄露漏洞信息安全

2020-12-22 09:42:47

生物识别网络安全

2013-11-20 11:35:44

SAP中国商业同略会

2011-02-17 14:27:16

WindowsMac

2020-01-08 13:40:01

戴尔

2021-11-03 21:00:19

智能建筑物联网

2018-12-10 07:22:27

物联网预测分析安全威胁

2022-06-07 16:40:09

区块链数字货币分布式账本

2019-11-11 14:15:54

大数据算法营销

2011-11-23 09:33:46

程序员
点赞
收藏

51CTO技术栈公众号