C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
内存模型
为 C++ 抽象机的目的定义了计算机内存存储的语义。
可为 C++ 程序所用的内存是一或多个字节的连续序列。内存中的每个字节拥有唯一的地址。
字节
字节(byte)是最小的可寻址内存单元。它被定义为相接的位序列,其大到足以保有任何 UTF-8
编码单元(256 个相异值)和 (C++14 起)基本执行字符集(96 个字符,要求必为单字节)的任何成员。与 C 相似,C++ 也支持 8 位或更大的字节。
char、unsigned char 和 signed char 类型把一个字节用于存储和值表示。字节中的位数可作为 CHAR_BIT 或 std::numeric_limits<unsigned char>::digits 访问。
内存位置
内存位置是
注意:语言的各种功能特性,例如引用和虚函数,可能涉及到程序不可访问,但为实现所管理的额外内存位置。
struct S { char a; // 内存位置 #1 int b : 5; // 内存位置 #2 int c : 11, // 内存位置 #2 (延续) : 0, d : 8; // 内存位置 #3 struct { int ee : 8; // 内存位置 #4 } e; } obj; // 对象 'obj' 由 4 个分离的内存位置组成
线程与数据竞争
执行线程是程序中的控制流,它始于 std::thread::thread、std::async 或以其他方式所进行的顶层函数调用。
任何线程都能潜在地访问程序中的任何对象(拥有自动或线程局部存储期的对象仍可为另一线程通过指针或引用访问)。
始终允许不同的执行线程同时访问(读和写)不同的内存位置,而无干涉或同步的任何要求。
当某个表达式的求值写入某个内存位置,而另一求值读或修改同一内存位置时,称这些表达式冲突。拥有两个冲突的求值的程序就有数据竞争,除非
- 两个求值都在同一线程上,或同一信号处理函数中执行,或
- 两个冲突的求值都是原子操作(见 std::atomic ),或
- 一个冲突的求值发生早于(happens-before)另一个(见 std::memory_order)
若出现数据竞争,则程序的行为未定义。
(特别是,std::mutex 的释放同步于,从而发生早于另一线程对同一 mutex 的获取,这使得可以用互斥锁来防止数据竞争)
int cnt = 0; auto f = [&]{cnt++;}; std::thread t1{f}, t2{f}, t3{f}; // 未定义行为
std::atomic<int> cnt{0}; auto f = [&]{cnt++;}; std::thread t1{f}, t2{f}, t3{f}; // OK
内存顺序
当线程从某个内存位置读取值时,它可能看到初值,同一线程所写入的值,或另一线程所写入的值。有关线程所作的写入操作对其他线程变为可见的顺序上的细节,见 std::memory_order。
向前进展
免妨碍
当只有一个未在标准库函数中阻塞的线程执行某个免锁的原子函数时,保证该执行将会完成(所有标准库免锁操作均为免妨碍的)。
免锁
当一或多个免锁原子函数同时运行时,保证其中至少一个将会完成(所有标准库免锁操作均为免锁的——确保其他线程不能不确定地活锁它们(例如以连续窃取缓存线的方式),是实现的工作)。
进展保证
合法的 C++ 程序中,每个线程最终要做下列之一:
- 终止
- 调用 I/O 库函数
- 通过 volatile 泛左值进行访问
- 进行原子操作或同步操作
没有线程能永远执行,而不做任何这些可观察行为。
注意,这意味着包含无限递归或无限循环(无论是实现为 for 语句 或是用 goto 循环还是其他方式)的程序具有未定义行为。这允许编译器移除所有无可观察行为的循环,而不必证明他们终将终止。
若线程执行了上述步骤之一(I/O、volatile、原子或同步操作),阻塞于标准库函数中,或调用由于某个未阻塞的并发线程而未能完成的原子免锁函数,则称它取得进展(make progress)。
并发向前进展若线程提供并发向前进展保证(concurrent forward progress guarantee),则只要它尚未终止,就将在有限量的时间内取得进展(定义如上),无关乎其他线程(若存在)是否取得进展。 标准鼓励但不要求主线程和 std::thread 所启动的线程提供并发向前进展保证。 并行向前进展若线程提供并行向前进展保证(parallel forward progress guarantee),则若线程尚未执行任何执行步骤(I/O、volatile、原子或同步操作),就不要求实现保证该线程终将取得进展,但一旦此线程开始执行步骤,则它提供并发向前进展保证(此规则描述线程池中以任意顺序执行任务的线程)。 弱并行向前进展若线程提供弱并行向前进展保证(weakly parallel forward progress guarantee),则不保证它终将取得进展,无关乎其他线程是否取得进展。 仍然能通过以向前进展保证委托进行阻塞来保证这种线程取得进展:若线程 P 以此方式阻塞于线程集合 S 的完成,则 S 中至少有一个线程将提供等于或强于 P 的向前进展保证。一旦该线程完成,则类似地强化 S 中的另一线程。一旦集合为空,则将解除 P 的阻塞。 来自 C++ 标准库的并行算法,均以向前保证委托阻塞于某个标准库所管理的线程的未指明集合的完成上。 |
(C++17 起) |