模块(Module)

​ 从 C 语言中,C++ 继承了 #include 机制,依赖从头文件使用文本形式包含 C++ 源代码,这些头文件中包含了接口的文本定义。一个流行的头文件可以在大型程序的各个单独编译的部分中被 #include 数百次。基本问题是:

  1. 不够卫生:一个头文件中的代码可能会影响同一翻译单元中包含的另一个 #include 中的代码的含义,因此 #include 并非顺序无关。宏是这里的一个主要问题,尽管不是唯一的问题。
  2. 分离编译的不一致性:两个翻译单元中同一实体的声明可能不一致,但并非所有此类错误都被编译器或链接器捕获。
  3. 编译次数过多:从源代码文本编译接口比较慢。从源代码文本反复地编译同一份接口非常慢。

例如:

​ 这段标准代码有 70 个左右的字符,但是在 #include 之后,它会产生 419909 个字符需要编译器来消化。尽管现代 C++ 编译器已有傲人的处理速度,但模块化问题已经迫在眉睫。

1
2
3
4
#include <iostream>
int main() {
std::cout << "Hello, C++!" << std::endl;
}

所以,在 C++ 程序中改进模块化是一个迫切的需求

​ C++20 模块化是对传统#include头文件机制的根本性替代,核心作用是「将代码按功能划分为独立模块,实现编译隔离、类型安全导入、编译速度大幅提升」—— 它解决了头文件重复包含、宏污染、编译冗余等几十年的历史问题,是 C++ 构建大型项目的核心基础设施升级

模块化通用定义

1. 核心概念

模块化是软件工程的核心思想,本质是 “分而治之”:

  • 模块:一个独立的功能单元(如 “数学计算模块”“网络请求模块”),包含「内部实现代码」和「对外暴露的接口」;是一个用于在翻译单元间分享声明和定义的语言特性。它们可以在某些地方替代使用头文件。

  • 封装:模块内部的细节对外部不可见,仅通过接口交互;

  • 复用:模块可被多个程序 / 其他模块导入使用,无需重复编写;

  • 解耦:模块间仅通过接口依赖,修改一个模块的内部实现不影响其他模块。

  • 顺序独立性:import X; import Y; 应该与 import Y; import X; 相同。

    换句话说,任何东西都不能隐式地从一个模块“泄漏”到另一个模块。

    这是 #include 文件的一个关键问题。

    #include 中的任何内容都会影响所有后续的 #include

    顺序独立性是“代码卫生”和性能的关键。

2. 模块化的核心价值(对比 “无模块的面条式代码”)

特性 无模块化(传统 C++ 头文件) 模块化(C++20)
代码封装 无(头文件所有符号全局可见) 有(仅export的接口对外暴露)
编译效率 低(头文件重复包含、重复编译) 高(模块仅编译一次,复用编译结果)
命名冲突 易发生(宏 / 全局符号污染) 无(模块内符号私有,仅接口可见)
依赖管理 文本替换(#include),易循环依赖 语义导入(import),天然隔离依赖
错误排查 链接期暴露错误,定位难 编译期校验接口,错误提前暴露

其主要优点如下:

  1. 没有头文件。

  2. 声明实现仍然可分离, 但非必要。

  3. 可以显式指定导出哪些类或函数。

  4. 不需要头文件重复引入宏(include guards)。

  5. 模块之间名称可以相同,并且不会冲突。

  6. 模块只处理一次,编译更快(头文件每次引入都需要处理,需要通过 pragma once 约束)。

  7. 预处理宏只在模块内有效。

  8. 模块的引入与引入顺序无关。

创建模块

源文件->添加->新建项->Module

创建***.ixx文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//创建模块
// mymodule.ixx //模块名和文件名没有强制要求,一般会相同
export module mymodule; // 模块声明
import <iostream>; // 导入声明 注意";"号

export void hello() { // 导出声明
std::cout << "Hello world!\n";
}
//可以用大括号一次性导出多个
export{
int val = 100;
void func3(){
cout << "func3()" << endl;
}
}

文件分工

文件类型 后缀 核心语法 内容职责
主接口单元 .ixx export module 模块名; 聚合所有分区接口(export import :分区名;),可补充少量全局接口声明
模块接口分区 .ixx export module 模块名:分区名; 拆分后的子接口声明(如代数 / 几何分区)
模块实现单元 .cpp module 模块名; 实现主接口 + 所有分区接口的逻辑,可拆分为多个 .cpp(如 math_algebra_impl.cppmath_geometry_impl.cpp

关键语法

语法元素 作用
export module 模块名 声明一个可导出的模块,模块文件的第一行必须是此声明
import 模块名 导入整个模块,可使用模块内所有export符号
import <标准库模块> 导入模块化的标准库(如import <iostream>,替代#include <iostream>
export 修饰符号(函数 / 类 / 变量),表示对外暴露;无export则为模块私有
module 模块名:分区名 声明模块分区,用于拆分大型模块
import :分区名 在模块内导入分区,export import :分区名表示对外暴露该分区的符号

接口分区

1.解决的问题

当模块的接口非常复杂(比如包含几十个函数 / 类),把所有export声明写在一个接口文件里会导致文件臃肿、维护困难。分区允许将接口拆分为:

  • 多个分区接口单元(负责暴露某一部分接口);
  • 一个主接口单元(聚合所有分区,对外提供统一的模块入口)。

2. 核心概念

单元类型 声明语法 作用
分区接口单元 export module 模块名:分区名; 暴露模块的一部分接口(如 “数学模块的代数部分”)
主接口单元 export module 模块名; 聚合所有分区接口,对外提供统一入口。通过 export import :分区名; 语法,将分区接口暴露给模块使用者。
模块实现单元(可选) module 模块名; 为分区接口提供实现(和普通模块实现一致)

关键语法细节

  • 分区接口单元:必须以 export module 模块名:分区名; 开头,定义该分区的导出接口。
  • 主接口单元:通过 export import :分区名; 导入并暴露分区接口;若省略 export,则分区为私有,仅模块内可见。
  • 外部使用:用户只能导入主模块(import 模块名;),无法直接导入分区。

匿名命名空间

​ 匿名命名空间是 C++ 中实现「文件级私有」的核心语法,用namespace {}定义的无名称命名空间,其内部的所有符号(函数、变量、类等)仅在当前编译单元(.cpp 文件)可见,等价于给符号加上static(但功能更全面)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 匿名命名空间(无名称)
namespace {
// 内部的所有符号仅当前文件可见
int internal_var = 100; // 文件级私有变量

void internal_func() { // 文件级私有函数
std::cout << "仅当前文件可见的函数" << std::endl;
}

class InternalClass { // 文件级私有类
public:
void print() { std::cout << "仅当前文件可见的类" << std::endl; }
};
}

// 当前文件内可直接访问匿名命名空间的符号(无需命名空间限定)
void test() {
internal_var = 200;
internal_func();
InternalClass obj;
obj.print();
}

核心特性

特性 具体说明
无名称 无法通过命名空间名::符号的方式访问,只能在当前文件内直接使用
文件级私有 符号仅在当前编译单元(.cpp 文件)可见,其他文件(包括同项目的其他.cpp)无法访问
内部链接 匿名命名空间的符号是「内部链接」,编译器会为每个编译单元生成独立的符号实例
等价于 static(但更优) 对函数 / 变量而言,namespace { void f(); } 等价于 static void f();,但匿名命名空间可修饰类 / 结构体,static不行

对比static:匿名命名空间的优势

static也能实现文件级私有,但仅支持函数和变量,无法修饰类 / 结构体;匿名命名空间是C++ 标准推荐的文件级封装方式,功能更全面:

场景 static 匿名命名空间
修饰函数 支持(static void f() 支持(namespace { void f(); }
修饰变量 支持(static int a; 支持(namespace { int a; }
修饰类 / 结构体 不支持 支持(namespace { class C {}; }
标准推荐 C++17 后不推荐(仅兼容) 标准推荐的文件级封装方式

协程(Coroutine)

概念

协程可以理解为用户态的轻量级线程,也常被称作 “协作式子程序”。

  • 通俗比喻:如果把操作系统管理的线程比作 “霸道的员工”(操作系统强行切换),那协程就是 “自觉的员工”(自己主动让出执行权)。线程的切换由操作系统内核控制,开销大;而协程的切换完全由程序员 / 代码控制,发生在用户态,几乎没有开销。
  • 核心特点:协程可以在执行过程中暂停(挂起),保存当前的执行状态,等需要时再恢复执行,而且切换时不需要陷入内核态,效率远高于线程。

协程与线程区别

特性 线程 协程
调度者 操作系统内核 程序员 / 编程语言
切换开销 大(内核态切换) 极小(用户态切换)
资源占用 高(MB 级栈空间) 极低(KB 级栈空间)
并行 / 并发 可真正并行(多核) 单线程内并发
同步方式 需加锁(如 mutex) 无需锁(协作式

​ C++ 提供了三个方法挂起协程:co_awaitco_yieldco_return

​ C++20协程只是提供协程机制,而不是提供协程库。C++20的协程是无栈协程,无栈协程是一个可以挂起/恢复的特殊函数,是函数调用的泛化,且只能被线程调用,本身并不抢占内核调度。

3 个核心关键字

C++20 提供了三个新关键字(co_awaitco_yieldco_return),如果一个函数中存在这三个关键字之一,那么它就是一个协程。

关键字 作用 场景
co_await 暂停协程,等待表达式完成后恢复 等待 IO、延迟、其他协程完成
co_yield 暂停协程并返回值,下次恢复继续执行 生成器(如无限序列)
co_return 结束协程并返回最终值 协程完成后返回结果
  • co_yield some_value: 保存当前协程的执行状态并挂起,返回some_value给调用者

  • co_await some_awaitable: 如果some_awaitable没有ready,就保存当前协程的执行状态并挂起

  • co_return some_value: 彻底结束当前协程,返回some_value给协程调用者

<=> 三向比较运算符

​ 也叫: 三路比较运算符,太空船运算符

<=> 是 C++20 新增的通用比较运算符,核心作用是一次定义,自动生成所有 6 种比较运算符(==、!=、<、>、<=、>=),解决传统比较运算符需要手动重载多个的冗余问题;它返回一个 “比较结果类型”,能直接表示 “小于 / 等于 / 大于” 三种状态。

基本概念

  • 符号<=>(因形状像太空船得名);
  • 作用:替代传统的operator</operator==等重载,一次性处理所有比较逻辑;
  • 返回值:
    • 对于内置类型(int/long 等):返回std::strong_ordering(强序,如0= 相等、<0= 小于、>0= 大于);
    • 对于浮点数:返回std::partial_ordering(偏序,支持 NaN);
    • 自定义类型:可返回std::strong_ordering/std::weak_ordering/std::partial_ordering

核心优势

​ 传统 C++ 中,要让自定义类型支持所有比较,需重载至少 2 个运算符(如==<),再手动推导其他;而<=>只需重载一次,编译器自动生成所有比较运算符,告别冗余的比较重载:

实现方式 需要重载的运算符 代码量 维护成本
传统方式 ==<(或更多)
三向比较运算符 operator<=>

关键语法规则

基础用法:内置类型

对于 int、double 等内置类型,<=>可直接使用,返回值可直接判断大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <compare> // 必须包含头文件
#include <iostream>
using namespace std;

int main() {
// 1. 整数比较(返回std::strong_ordering)
auto res1 = 5 <=> 3;
if (res1 > 0) cout << "5 > 3" << endl; // 输出:5 > 3
if (3 <=> 5 < 0) cout << "3 < 5" << endl; // 输出:3 < 5
if (4 <=> 4 == 0) cout << "4 == 4" << endl; // 输出:4 == 4

// 2. 浮点数比较(返回std::partial_ordering,支持NaN)
double a = 2.5, b = NAN;
auto res2 = a <=> b;
if (res2 == std::partial_ordering::unordered) {
cout << "a和b无法比较(包含NaN)" << endl;
}
return 0;
}

核心用法:自定义类型重载

对自定义类型重载<=>后,编译器会自动生成==/!=/</>/<=/>=,无需手动实现:

示例:自定义 Person 类,按年龄比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <compare>
#include <string>
#include <iostream>
using namespace std;

class Person {
private:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {}

// 重载三向比较运算符(核心:只需写一次)
auto operator<=>(const Person& other) const = default;
// = default:编译器自动按成员变量逐一向比较(先age,再name)
// 也可手动实现逻辑:return age <=> other.age;
};

int main() {
Person p1("Alice", 25);
Person p2("Bob", 30);
Person p3("Alice", 25);

// 自动生成的比较运算符,直接使用
if (p1 < p2) cout << "p1年龄小于p2" << endl; // 输出:p1年龄小于p2
if (p1 == p3) cout << "p1和p3相等" << endl; // 输出:p1和p3相等
if (p2 > p1) cout << "p2年龄大于p1" << endl; // 输出:p2年龄大于p1
if (p1 != p2) cout << "p1和p2不相等" << endl; // 输出:p1和p2不相等
return 0;
}
语法细节 说明
= default 编译器自动按成员变量 “逐一向比较”(成员声明顺序决定比较优先级),推荐使用;
手动实现逻辑 可自定义比较规则(如仅按 age 比较,忽略 name):return age <=> other.age;
const 修饰 重载时必须加const(因为比较不修改对象状态),否则编译报错;
返回值类型 无需显式指定,用auto推导即可(编译器自动匹配合适的顺序类型);
== 的特殊处理 若仅重载<=>= default,编译器会自动生成operator==;若手动实现<=>,需手动重载==

范围ranges

​ 范围库始于 Eric Niebler 对 STL 序列观念的推广和现代化的工作。它提供了更易于使用、更通用及性能更好的标准库算法。

​ Ranges 是 C++20 对传统 STL 算法的革命性升级,核心是将 “数据容器” 和 “算法” 解耦,通过 “视图(View)” 实现惰性求值、无拷贝的元素变换 / 筛选,让代码更简洁、高效、易读,彻底解决传统 STL 算法需要手写迭代器、冗余临时容器的问题。

核心概念

核心组成:范围(Range)+ 视图(View)+ 适配器(Adapter)

概念 核心定义 特点
Range 可被遍历的对象(容器、数组、生成器等),满足begin()/end()接口 所有 STL 容器(vector/string 等)、原生数组都天然是 Range
View 对 Range 的 “轻量级包装”,实现元素的变换 / 筛选,惰性求值(仅遍历时有计算) 无拷贝、O (1) 构造、不可变
Adapter 用于修改 Range 的操作(如 filter/transform),通过 ` `(管道符)链式调用

核心特性:惰性求值(Lazy Evaluation)

这是 Ranges 最高效的特性:

  • 视图(View)只是 “计算规则” 的描述,定义时不执行任何计算
  • 只有当遍历视图(如 for 循环、std::ranges::for_each)时,才会逐元素执行变换 / 筛选;
  • 无临时容器开销:传统 STL 的copy_if/transform会生成临时容器,Ranges 直接在原数据上按需计算。

常用视图适配器(Views)

C++20 提供了丰富的内置视图,覆盖绝大多数数据处理场景:

视图适配器 作用 示例
views::filter 筛选满足条件的元素 views::filter([](int x){return x%2==0;})
views::transform 变换元素(映射) views::transform([](int x){return x*x;})
views::take 取前 N 个元素 views::take (3)(取前 3 个)
views::drop 跳过前 N 个元素 views::drop (2)(跳过前 2 个)
views::reverse 反转元素顺序 views::reverse
views::slice 切片(取 [start, end) 区间) views::slice (2,5)(索引 2-4)
views::iota 生成连续整数序列(生成器) views::iota(1, 6)(生成 1,2,3,4,5)

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <vector>
#include <ranges>
#include <iostream>
using namespace std;
int main()
{
auto ints = views::iota(0, 10);//生成0-9
auto even = [](int i) { return 0 == i % 2; };
auto square = [](int i) { return i * i; };
for (int i : ints | views::filter(even) | views::transform(square))
cout << i << ' ';
return 0;
}

日期和时区

​ 日期库是多年工作和实际使用的结果,它基于 chrono 标准库的时间支持。在 2018 年,它进入了 C++20,并和旧的时间工具一起放在 <chrono> 中。

<chrono>是 C++11 引入的,核心是 “时间点” 和 “时间段”,C++20 新增了日期类型(year/month/day),更易用。

核心概念

不管是旧版本还是 C++20,<chrono>的核心都是 3 个概念,先吃透:

概念 大白话解释 代码示例(C++11+)
duration(时间段) 表示 “一段时间长度”(比如 1 小时、3 天) chrono::hours(1)chrono::days(3)(C++20)、chrono::seconds(30)
time_point(时间点) 表示 “某个具体时刻”(比如 2026-02-28 10:00) chrono::system_clock::now()(获取当前时间点)
clock(时钟) 获取时间点的 “工具” system_clock(系统时间,可转换为日历)、steady_clock(稳定时钟,用于计时)

C++20新增

新增 1:日历类型(year/month/day)—— 直观操作年月日

这是最核心的升级:直接用year/month/day定义日期,不用再手动转换time_t/tm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <chrono>
#include <iostream>
using namespace std;

int main() {
// C++20:直接定义日期(2026年2月28日)
chrono::year y{2026};
chrono::month m{2};
chrono::day d{28};
// 组合成年月日对象
chrono::year_month_day ymd = y/m/d;

// 输出(C++20支持直接打印)
cout << "日期:" << ymd << endl; // 输出:2026/02/28
// 单独获取年/月/日(转成int)
cout << "年:" << int(y) << " 月:" << int(m) << " 日:" << int(d) << endl;
return 0;
}

新增 2:时钟增强 —— 获取当前日期更简单

C++20 可以直接把 “当前时间点” 转成year_month_day,不用再绕time_t

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <chrono>
#include <iostream>
using namespace std;

int main() {
// 1. 获取当前系统时间点
auto now = chrono::system_clock::now();
// 2. 向下取整到“天”(去掉时分秒)
auto today = chrono::floor<chrono::days>(now);
// 3. 转成年月日
chrono::year_month_day ymd = today;

cout << "今天日期:" << ymd << endl; // 输出:2026/02/28
return 0;
}

新增 3:时区支持(std::chrono::time_zone)

C++20 新增了标准时区库,不用再依赖第三方库就能处理不同时区(比如 UTC 转北京时间)。

示例:UTC 时间转北京时间(东 8 区)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <chrono>
#include <iostream>
using namespace std;

int main() {
// 获取当前UTC时间点
auto utc_now = chrono::utc_clock::now();
// 转成北京时间(东8区,Asia/Shanghai)
auto bj_tz = chrono::locate_zone("Asia/Shanghai");
chrono::zoned_time bj_time{bj_tz, utc_now};

cout << "UTC时间:" << utc_now << endl; // UTC时间
cout << "北京时间:" << bj_time << endl; // 北京时间(UTC+8)
return 0;
}

新增 4:格式化与解析(C++20 std::format)

C++20 的<format>库支持直接格式化<chrono>的时间 / 日期,不用手动拼字符串。

示例 :格式化日期时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <chrono>
#include <format>
#include <iostream>
using namespace std;

int main() {
auto now = chrono::system_clock::now();
// 格式化输出:2026-02-28 10:30:45
cout << format("{:%Y-%m-%d %H:%M:%S}", now) << endl;
// 只格式化日期:2026/02/28
auto today = chrono::floor<chrono::days>(now);
cout << format("{:%Y/%m/%d}", today) << endl;
return 0;
}

示例 2:解析字符串为日期

把 “2026-02-28” 这样的字符串直接转成year_month_day,不用手动解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <chrono>
#include <format>
#include <iostream>
#include <sstream>
using namespace std;

int main() {
string date_str = "2026-02-28";
chrono::sys_days dp; // 存储解析后的日期

// 解析字符串
istringstream ss(date_str);
ss >> chrono::parse("%Y-%m-%d", dp);

// 验证:转成year_month_day输出
chrono::year_month_day ymd = dp;
cout << "解析后的日期:" << ymd << endl; // 2026/02/28
return 0;
}

C++20 vs C++11/17 <chrono> 核心对比

功能 C++11/17 C++20
日期定义 需手动拼 tm 结构体,易出错 直接用 year/month/day,直观
日期加减 需手动处理闰年 / 月末,麻烦 自动处理边界,直接 + days/months
时区支持 无标准支持,需第三方库 内置 time_zone,支持时区转换
格式化 / 解析 需手动拼字符串,无标准接口 用 std::format/parse,一行搞定
日历算法(星期 / 闰年) 需手动计算 内置 is_leap ()、weekday () 等工具

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <chrono>
using namespace std;
using namespace std::chrono;
int main()
{
// creating a year
auto y1 = year{ 2019 };
auto y2 = 2019y;
// creating a mouth
auto m1 = month{ 9 };
auto m2 = September;
// creating a day
auto d1 = day{ 18 };
auto d2 = 18d;

year_month_day date1{ 2022y,July, 21d };
auto date2 = 2022y / July / 21d;

chrono::year_month_day date3{ Monday[3] / July / 2022 };
cout << date1 << endl;
cout << date2 << endl;
cout << date3 << endl;
return 0;
}

格式化

​ iostream 库提供了类型安全的 I/O 的扩展,但是它的格式化工具比较弱。

​ 另外,还有的人不喜欢使用 << 分隔输出值的方式。

​ 格式化库提供了一种类 printf 的方式去组装字符串和格式化输出值,同时这种方法类型安全、快捷,并能和 iostream 协同工作。

​ 类型中带有 << 运算符的可以在一个格式化的字符串中输出。

1
2
3
4
string s1 ="C++";
cout << format("The string '{}' has {} characters", s1, s1.size());
cout << format("The string '{0}' has {1} characters", s1, s1.size()) << endl;
cout << format("The string '{1}' has {0} characters", s1.size(), s1) << endl;

std::format是 C++20 替代传统printf/cout拼接的新一代格式化工具,语法类似 Python 的 f-string,更安全、更易读、功能更强,还原生支持 C++20 新类型(如chrono日期时间)。需包含头文件<format>

std::format核心语法

1. 基础用法:占位符{}

  • 最简化:{} 按顺序匹配参数;
  • 指定位置:{n} 匹配第 n 个参数(从 0 开始);
  • 指定格式:{:格式符} 控制输出样式。

2.常用格式控制(重点)

格式符 作用 示例 输出
:.2f 浮点数保留 2 位小数 format("{:.2f}", 3.1415) 3.14
:d 十进制整数 format("{:d}", 10) 10
:x/:X 十六进制(小 / 大写) format("{:X}", 255) FF
:8s 字符串占 8 个字符(左对齐) format("{:8s}", "abc") “abc “
:08d 整数补 0 到 8 位 format("{:08d}", 123) 00000123

跨度

​ 越界访问,有时也称为缓冲区溢出,从 C 的时代以来就一直是一个严重的问题。考虑下面的例子:

1
2
3
4
5
void func1(int* p, int n) { // n 是什么?
for (int i = 0; i < n; ++i) {
p[i] = 7; // 是否可行?
}
}

span<T> 类模板就这样被放到 C++ 核心指南的支持库中。

1
2
3
4
5
void func(span<int> a) { // span 包含一个指针和一条大小信息
for (int& x : a) {
x = 7; // 可以
}
}

​ 范围 for 从跨度中提取范围,并准确地遍历正确数量的元素(无需代价高昂的范围检查)。这个例子说明了一个适当的抽象可以同时简化写法并提升性能。对于算法来说,相较于挨个检查每一个访问的元素,明确地使用一个范围(比如 span)要容易得多,开销也更低。

std::span

std::span是 C++20 新增的 “轻量级容器视图”,本质是 “指针 + 长度” 的组合,用来安全、高效地访问连续内存区域(数组、vector、C 数组等),无需拷贝数据,还能避免数组越界,彻底替代裸指针 + 长度的不安全写法。

span 核心概念

术语 大白话解释 核心特点
std::span 连续内存的 “视图”(不拥有数据) 1. 大小:仅占 2 个指针空间(指针 + 长度);2. 无拷贝:只是指向原数据,不复制;3. 安全:编译期 / 运行期可检查越界;4. 灵活:适配所有连续容器(vector、数组、C 数组)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <span>   // 必须包含的头文件
#include <iostream>

// 直接传span,自带长度,无需额外参数
void print_array(std::span<int> s) {
for (int num : s) { // 范围for循环,天然不越界
std::cout << num << " ";
}
}

int main() {
int arr[] = {1,2,3,4,5};
print_array(arr); // 自动推导长度,无需传5
return 0;
}

span 核心优势

  1. 零拷贝:只是视图,不复制原数据,内存开销极小(仅 2 个指针);
  2. 类型安全:编译期检查容器类型,避免裸指针的类型错误;
  3. 边界安全:支持越界检查(at()抛异常、contains()判断),避免数组越界崩溃;
  4. 简化代码:无需手动传递 “指针 + 长度”,函数参数更简洁;
  5. 兼容所有连续容器:vector、array、原生数组、动态数组都能适配,无需修改函数。

并发

​ C++ 并发编程的核心是 “多线程同时执行代码”,C++11 开始提供标准化的线程库(<thread>),后续版本持续增强,让开发者无需依赖平台 API(如 Windows 的 CreateThread、Linux 的 pthread),就能写出跨平台、安全的多线程代码。

​ C++ 中的并发主要靠线程(Thread) 实现:每个线程是独立的执行流,多个线程共享进程的内存空间(全局变量、堆内存),但有自己的栈空间。

C++ 并发的核心演进

版本 核心特性 作用
C++11 <thread>/<mutex> 基础线程创建、互斥锁(解决数据竞争)
C++17 std::scoped_lock 更安全的锁(避免死锁)
C++20 std::jthread/ 协程 自动析构的线程、轻量级并发(协程)