目录

1. 基础语法与关键字

C++98/03 const/volatile作用 · static作用域 · 指针与引用区别 · 类型转换(4种cast) · 宏与预处理器

C++11 auto类型推导 · nullptr · decltype · 范围for循环 · 原始字符串字面量 · alignof/alignas · using别名

C++14 二进制字面量 · 数字分隔符 · 泛型lambda · 函数返回类型推导 · [[deprecated]]属性

C++17 结构化绑定 · if/switch初始化语句 · 内联变量 · 折叠表达式 · std::byte

C++20 指定初始化 · 协程关键字(co_await等) · 模块(import) · 属性增强([[no_unique_address]])

2. 面向对象特性

C++98/03 类访问控制 · 构造/析构函数 · 继承与多态 · 虚函数表(vptr) · 运算符重载 · RTTI机制

C++11 委托构造 · 继承构造 · final/override · 强类型枚举 · 移动语义 · 显式缺省/删除函数(=default/=delete)

C++14 成员函数返回类型推导 · 放宽const成员函数约束

C++17 聚合体扩展初始化 · 内联静态成员 · 嵌套命名空间简化(namespace A::B)

C++20 概念约束类模板 · 协程成员函数 · 三路比较运算符(<=>) · 基于范围的operator[]

类与对象 继承与多态 构造函数与析构函数 虚函数与纯虚函数 拷贝构造函数与赋值运算符 友元函数与类 面向对象设计原则(SOLID)

3. 模板与泛型编程

C++98/03 函数/类模板基础 · 模板特化/偏特化 · 依赖名称解析(typename双义性)

C++11 可变参数模板 · 模板别名 · 外部模板 · 类型萃取(type_traits) · SFINAE与enable_if

C++14 变量模板 · 泛型lambda捕获 · 函数模板默认参数

C++17 类模板参数推导(CTAD) · 折叠表达式 · if constexpr · 结构化绑定模板

C++20 概念(concepts) · 约束模板 · 模板lambda · 简写函数模板 · 模板参数列表简化

函数模板与类模板 模板特化与偏特化 模板元编程(模板递归、SFINAE) 类型萃取(Type Traits) 泛型编程的应用(STL 容器等)

4. 内存管理

C++98/03 new/delete机制 · 内存分区(栈/堆等) · 深浅拷贝 · 定位new表达式

C++11 移动语义 · 右值引用 · 智能指针(unique_ptr/shared_ptr/weak_ptr) · 内存模型 · allocator_traits

C++14 make_unique · 共享指针数组支持 · 大小明确的delete

C++17 内存对齐控制 · 多态分配器(std::pmr) · 未初始化内存工具

C++20 原子智能指针 · 销毁操作概念化 · 协程内存优化

内存分配与释放(new/delete,malloc/free) 智能指针(std::unique_ptr, std::shared_ptr, std::weak_ptr) 内存泄漏与野指针 自定义内存管理

5. 异常处理

C++98/03 基本try-catch机制 · 异常规范(已弃用) · RAII模式 · 异常安全保证

C++11 noexcept关键字 · 异常传播改进 · exception_ptr(跨线程异常)

C++17 uncaught_exceptions()(嵌套异常) · 异常类型系统增强

C++20 协程异常处理 · 契约编程(未正式采纳) · 异常概念化

6. 并发与多线程

C++11 std::thread · 互斥量(mutex) · 条件变量 · future/promise · 原子操作库

C++14 共享锁超时 · 泛型原子操作 · 读写锁(shared_timed_mutex)

C++17 并行算法(std::execution) · scoped_lock · 共享指针原子操作 · 文件系统库

C++20 协程支持 · 信号量(counting_semaphore) · 屏障(barrier) · jthread(自动join) · 原子等待

7. 标准库关键组件

C++11 智能指针 · std::function/std::bind · 正则表达式 · 哈希容器 · 随机数库

C++14 自定义字面量 · 编译时整数序列

C++17 std::optional · std::variant · std::any · std::string_view · 并行算法

C++20 std::span(无所有权视图) · std::format(格式化) · 范围库(Ranges) · 日历与时区

8. 编译时计算

C++11 constexpr函数 · 常量表达式基础

C++14 constexpr扩展(分支/循环) · 变量模板的constexpr

C++17 if constexpr · constexpr lambda · 内联变量编译时初始化

C++20 consteval(立即函数) · constinit · 编译时虚函数 · 编译时容器(std::vector)

9. 初始化演进

C++98/03 构造函数初始化列表 · 值初始化

C++11 统一初始化({}语法) · std::initializer_list · 列表初始化优先级

C++20 指定初始化(Designated Initializers) · 聚合初始化扩展

virtual

 1class Shape {
 2public:
 3    virtual void draw() = 0;    // 纯虚函数 抽象接口 派生类必须实现
 4    virtual ~Shape() = default; // 基类的析构函数应为虚函数 使用默认生成的函数实现
 5};
 6
 7// = 0         表示纯虚函数
 8// = default   表示使用编译器生成的默认实现
 9
10#include <iostream>
11#include <vector>
12
13struct Base {
14    std::vector<int> data{1, 2, 3};
15    ~Base() = default;  // 默认析构
16};
17
18int main() {
19    Base b;
20} 
21
22// 编译器会把 ~Base() = default; 变成类似:
23
24~Base() {
25    data.~vector();   // 自动调用 vector<int> 的析构函数
26}

析构函数应为虚函数,防止通过基类指针删除派生类对象时,派生类的析构函数不被调用,导致资源泄漏。

 1class Base {
 2public:
 3    ~Base() { std::cout << "Base dtor\n"; }
 4};
 5
 6class Derived : public Base {
 7public:
 8    ~Derived() { std::cout << "Derived dtor\n"; }
 9};
10
11Base* p = new Derived();
12delete p;  // 只会调用 Base::~Base(),不会调用 Derived::~Derived()

智能指针

std::unique_ptr

std::unique_ptr 是 独占所有权的智能指针

  • 独占所有权:同一时间只能有一个 unique_ptr 指向同一对象
  • 不可拷贝(拷贝构造/赋值被禁用),但可移动
  • 生命周期结束时会自动调用 delete 释放资源
 1#include <iostream>
 2#include <memory>
 3
 4int main() {
 5    std::unique_ptr<int> p1(new int(42));  // 管理一个int
 6    std::cout << *p1 << "\n";
 7
 8    // std::unique_ptr<int> p2 = p1; // ❌ 不允许拷贝
 9    std::unique_ptr<int> p2 = std::move(p1); // ✅ 允许移动
10    if (!p1) std::cout << "p1 is empty\n";
11}

什么推荐 make_unique 而不是 new?

  1. 异常安全:

    c++ 17前,求值顺序未定义。先执行 new T,然后可能在传参时 g() 抛异常,结果 new 的结果还没交给unique_ptr,内存泄漏。

    1f(std::unique_ptr<T>(new T), g());  
    

    make_unique 内部是一条语句,new 出来的对象会立刻交给 unique_ptr,不会存在裸指针暂存的情况,资源会在异常传播时析构。

    1f(std::make_unique<T>(), g());  
    
  2. 更简洁:不需要显式写 new。

  3. 避免重复类型:

    1std::unique_ptr<MyClass> p1(new MyClass(1,2,3));   // 类型写了两遍
    2auto p2 = std::make_unique<MyClass>(1,2,3);        // 类型只写一次
    

局部静态变量

静态初始化顺序问题

静态初始化顺序问题(Static Initialization Order Fiasco)。C++ 中全局/静态变量的初始化顺序:

  • 同一个翻译单元(.cpp 文件),按照出现顺序初始化。
  • 不同翻译单元,初始化顺序是未定义的。
 1// ShapeFactory.h
 2class ShapeFactory {
 3private:
 4    static std::unordered_map<std::string, Creator> registry;
 5public:
 6    static void registerShape(const std::string& name, Creator c) {
 7        registry[name] = c;
 8    }
 9};
10
11// ShapeFactory.cpp
12std::unordered_map<std::string, ShapeFactory::Creator> ShapeFactory::registry;

在 另一个翻译单元 有静态对象注册:

1// Circle.cpp
2struct CircleRegister {
3    CircleRegister() {
4        ShapeFactory::registerShape("circle", [](){ return std::make_unique<Circle>(); });
5    }
6};
7static CircleRegister cr;

如果 ShapeFactory::registry 还没初始化,registerShape 就会访问未定义内存,程序可能崩溃或行为异常。这个问题叫 Static Initialization Order Fiasco(静态初始化顺序地狱)。

使用 getRegistry() 的好处

1static std::unordered_map<std::string, Creator>& getRegistry() {
2    static std::unordered_map<std::string, Creator> registry;
3    return registry;
4}

局部静态变量 在第一次调用时初始化,而不是编译时。这样无论 registerShape() 被哪个翻译单元的静态对象调用,registry 一定已经初始化。完全避免初始化顺序问题。

全局可见性问题(多个翻译单元)

也是初始化顺序问题的一种,在多个翻译单元中出现。假设你有三个 .cpp 文件:

1// Circle.cpp
2static struct { CircleRegister() { ShapeFactory::registerShape("circle", ...); } } cr;
3
4// Rectangle.cpp
5static struct { RectangleRegister() { ShapeFactory::registerShape("rectangle", ...); } } rr;
6
7// main.cpp
8int main() { ... }

静态成员变量 registry 是全局的,但初始化顺序不确定。如果 Circle.cpp 中的静态对象先调用 registerShape(),而 registry 还没初始化,就会访问未定义内存。局部静态函数延迟初始化,保证第一次使用时再创建 registry,完全解决跨翻译单元依赖问题。

线程安全问题

  • C++11前:静态成员变量初始化 不是线程安全。多个线程同时访问静态成员变量的初始化,可能会导致竞态条件。
  • C++11后:函数内部静态变量初始化是 线程安全的。
1void registerShapes() {
2    std::thread t1([](){ ShapeFactory::registerShape("circle", ...); });
3    std::thread t2([](){ ShapeFactory::registerShape("rectangle", ...); });
4    t1.join();
5    t2.join();
6}

如果 registry 是静态成员变量,C++11 之前可能出现访问冲突。使用局部静态变量,C++11+ 编译器保证初始化只执行一次,安全。

不同类型资源的风险等级

资源类型 OS清理 风险等级 主要问题
内存 ✅ 完全清理 🟢 低 主要是逻辑问题
文件描述符 ✅ 强制关闭 🟡 中 数据可能丢失
网络连接 ✅ 强制断开 🟠 中高 服务端资源泄漏
文件锁 ✅ 自动释放 🟡 中 锁文件可能残留
临时文件 ❌ 不会删除 🔴 高 磁盘空间浪费
数据库连接 ❌ 不会清理 🔴 高 连接池耗尽
共享内存 ⚠️ 部分清理 🟠 中高 可能需要手动清理

explicit

explicit 关键字用于修饰构造函数,防止隐式类型转换。

 1// 没有explicit的类
 2class Observer {
 3public:
 4    Observer(const std::string& name) { 
 5        std::cout << "Observer: " << name << std::endl; 
 6    }
 7};
 8
 9// 有explicit的类  
10class File {
11public:
12    explicit File(const std::string& filename) { 
13        std::cout << "File: " << filename << std::endl; 
14    }
15};
16
17void process(Observer obs) {
18    std::cout << "Processing observer\n";
19}
20
21int main() {
22    // 1. 赋值 - 没有explicit时可以这样写
23    Observer obs = "Alice";        // ❌ 意外:看起来在赋值字符串
24    
25    // 2. 函数传参 - 没有explicit时可以这样写  
26    process("Bob");                // ❌ 意外:创建了临时Observer对象
27    
28    // 3. 文件例子 - 有explicit时必须明确
29    // File f = "test.txt";        // ❌ 编译错误!
30    File f("test.txt");            // ✅ 必须明确创建
31    
32    return 0;
33}