←
C++ 标准库头文件速查手册
📖 目录
第一章 输入输出流
1.1 <iostream> —— 最基础的输入输出
1.2 <iomanip> —— 控制输出格式(排版美化)
1.3 <fstream> —— 读写文件
1.4 <sstream> —— 字符串流(把字符串当"文件"操作)
第二章 容器
2.1 <vector> —— 动态数组(最常用!)
2.2 <list> —— 双向链表
2.3 <deque> —— 双端队列
2.4 <array> —— 固定大小数组(C++11)
2.5 <forward_list> —— 单向链表(C++11)
2.6 <stack> —— 栈
2.7 <queue> —— 队列
2.8 <priority_queue> —— 优先队列(堆)
2.9 <set> / <multiset> —— 有序集合
2.10 <map> / <multimap> —— 有序字典
2.11 <unordered_set> / <unordered_multiset> —— 无序集合(C++11)
2.12 <unordered_map> / <unordered_multimap> —— 无序字典(C++11)
第三章 算法
3.1 <algorithm> —— 通用算法
3.2 <numeric> —— 数值算法
第四章 字符串与字符处理
4.1 <string> —— 字符串(最常用!)
4.2 <string_view> —— 字符串视图(C++17)
4.3 <cctype> —— 字符判断与转换
4.4 <cstring> —— C 风格字符串操作
第五章 数学与数值
5.1 <cmath> —— 数学函数
5.2 <cstdlib> —— 随机数与通用工具
5.3 <random> —— 高质量随机数(C++11)
5.4 <complex> —— 复数
5.5 <limits> —— 类型极限值
5.6 <ratio> —— 编译期有理数(C++11)
第六章 时间与日期
6.1 <chrono> —— 现代时间库(C++11)
6.2 <ctime> —— C 风格时间
第七章 内存管理
7.1 <memory> —— 智能指针(C++11 重点!)
7.2 <new> —— 内存分配原语
第八章 多线程与并发(C++11 起)
8.1 <thread> —— 线程
8.2 <mutex> —— 互斥锁
8.3 <condition_variable> —— 条件变量
8.4 <future> —— 异步任务
8.5 <atomic> —— 原子操作
8.6 <shared_mutex> —— 读写锁(C++14/17)
第九章 函数对象与泛型编程
9.1 <functional> —— 函数包装与绑定
9.2 <type_traits> —— 类型特征(C++11)
9.3 <utility> —— 通用工具
9.4 <tuple> —— 元组(C++11)
9.5 <variant> —— 类型安全联合体(C++17)
9.6 <any> —— 任意类型容器(C++17)
9.7 <optional> —— 可选值(C++17)
第十章 异常处理
10.1 <exception> —— 异常基类
10.2 <stdexcept> —— 标准异常类
10.3 <system_error> —— 系统错误(C++11)
第十一章 类型信息(RTTI)
11.1 <typeinfo> —— 运行时类型识别
第十二章 正则表达式(C++11)
12.1 <regex> —— 正则表达式
第十三章 文件系统(C++17)
13.1 <filesystem> —— 文件系统操作
第十四章 C++20 新特性头文件
14.1 <format> —— 格式化(C++20)
14.2 <ranges> —— 范围库(C++20)
14.3 <span> —— 连续内存视图(C++20)
14.4 <concepts> —— 概念(C++20)
14.5 <compare> —— 三路比较(C++20)
第十六章 其他重要头文件
16.1 <cassert> —— 断言
16.2 <bitset> —— 位集
16.3 <initializer_list> —— 初始化列表(C++11)
16.4 <charconv> —— 低开销数值转换(C++17)
16.5 <numbers> —— 数学常量(C++20)
16.6 <source_location> —— 源码位置(C++20)
第十七章 头文件使用要点

C++ 标准库头文件速查手册

增强学习版 · 适合 iPhone 阅读

16 章·62 个小节·453 个代码示例
第一章 输入输出流

▼1.1 <iostream> —— 最基础的输入输出C++

是什么

📚 <iostream> 是 C++ 的“输入输出流”库。数据像水流一样在程序和外部设备之间流动:cin 把键盘输入流进程序,cout 把程序结果流到屏幕。

💡 生活比喻:cin 是程序的“眼睛/耳朵”,cout 是“嘴巴”,cerr 是“警报器”,clog 是“日记本”。

⚠️ 核心特点:C++ 的 << 和 >> 运算符可以连续拼接,类型安全且自动推断格式,比 C 的 printf/scanf 更不容易写错。

cout << x —— 标准输出(往屏幕显示)

🎯 功能:把变量、字符串、表达式结果等“插入”到标准输出流 cout,显示在屏幕上。<< 可以连续拼接多个内容。

💡 比喻:就像说话一样,一句接一句:cout << "年龄:" << age << endl; 相当于说“年龄:”然后报出 age 的值,最后换行。

🔧 声明:ostream& operator<<(ostream& os, const T& x);

↩️ 返回值:返回输出流本身,因此可以连续使用 <<。

C++
#include <iostream>
#include <string>
using namespace std;
int main() {
    string name = "Tom";
    int age = 20;
    double score = 95.5;
    cout << "姓名:" << name << endl;
    cout << "年龄:" << age << ",分数:" << score << endl;
    return 0;
}

cin >> x —— 标准输入(从键盘读取)

🎯 功能:从标准输入流 cin 读取数据,用 >> “提取”到变量中。连续使用可一次性读多个变量。

💡 比喻:就像快递员根据地址把包裹分发到不同变量。注意:cin >> 遇到空格、回车、Tab 就会停下,所以一次只能读一个“词”。

🔧 声明:istream& operator>>(istream& is, T& x);

↩️ 返回值:返回输入流本身,可连续读取;失败时流进入错误状态。

C++
#include <iostream>
using namespace std;
int main() {
    int a, b;
    cout << "请输入两个整数(空格分隔):";
    cin >> a >> b;
    cout << a << " + " << b << " = " << a + b << endl;
    return 0;
}

getline(cin, str) —— 读取一整行

🎯 功能:从输入流读取一整行(包括空格),直到遇到换行符,把结果存入 string 变量 str,换行符本身不会存进去。

💡 比喻:就像把一整句话原封不动抄下来。相比 cin >> 只能读一个词,getline 能读含空格的完整句子。

🔧 声明:istream& getline(istream& is, string& str);

↩️ 返回值:返回输入流 is,遇到文件结束或错误时返回的流状态可检测。

C++
#include <iostream>
#include <string>
using namespace std;
int main() {
    string line;
    cout << "请输入一行文字:";
    getline(cin, line);  // 读取一整行,含空格
    cout << "你输入的是:" << line << endl;
    return 0;
}

cerr / clog —— 标准错误与日志输出

🎯 功能:cerr 用于输出错误信息,不缓冲、立即显示;clog 用于记录日志,有缓冲、效率更高。

💡 比喻:cerr 像火灾警报器,必须立刻响;clog 像日常流水账,可以攒一批再记到日志文件。

🔧 声明:ostream& cerr; ostream& clog;

↩️ 返回值:两者都是 ostream 对象,使用和 cout 一样,返回输出流。

C++
#include <iostream>
using namespace std;
int main() {
    int code = 404;
    cerr << "❌ 错误:服务器返回 " << code << endl;  // 立即显示
    clog << "[INFO] 用户访问了首页" << endl;         // 日志记录
    return 0;
}

endl vs \\n —— 换行与刷新

🎯 功能:endl 输出一个换行符并强制刷新缓冲区;\n 只输出换行符,不强制刷新。

💡 比喻:就像发微信:endl 是“打字 + 立刻发送”,\n 只是“打字换行”,内容可能还暂存在输入框里,等到合适时机再一起发。

🔧 声明:ostream& endl(ostream& os);

↩️ 返回值:返回输出流本身。

C++
#include <iostream>
using namespace std;
int main() {
    cout << "第一行" << endl;   // 换行 + 刷新
    cout << "第二行\n";          // 只换行,不刷新,效率稍高
    cout << "第三行" << endl;
    return 0;
}

flush —— 刷新缓冲区

🎯 功能:强制把输出缓冲区里的内容立刻显示出来,但不额外输出换行。

💡 比喻:就像催一下收银员:“先把已点的菜结了”,不要等最后再统一打印小票。

🔧 声明:ostream& flush(ostream& os);

↩️ 返回值:返回输出流本身。

C++
#include <iostream>
using namespace std;
int main() {
    cout << "正在加载...";
    cout.flush();  // 立刻显示,不换行
    // ... 模拟耗时操作 ...
    cout << "完成!" << endl;
    return 0;
}
⚠️ 常见坑: 1. 先用 cin >> 再接着用 getline 时,输入行尾的换行符会留在缓冲区,导致 getline 读到空行。解决:在中间加 cin.ignore();。 2. endl 不只是换行,还会刷新缓冲区;大量输出时推荐用 \n 提高效率。 3. cin 失败后会保留错误状态,后续读取可能都失败,需要用 cin.clear() 和 cin.ignore() 恢复。
💡 什么时候用:正常输出用 cout,错误信息用 cerr,日志用 clog;读一行用 getline,读单个词用 cin >>。

👉下一节:iomanip —— 控制输出格式 →

▼1.2 <iomanip> —— 控制输出格式(排版美化)C++

是什么

📚 <iomanip> 是 C++ 的“输出排版工具箱”。它提供了一批流操纵符,让你控制 cout 输出的宽度、精度、对齐方式、进制、填充字符等。

💡 生活比喻:如果把 cout 当作写字,iomanip 就是尺子、橡皮和格线——让输出整整齐齐。

⚠️ 注意:部分操纵符(如 setw)只对下一次输出有效;部分(如 fixed、hex)会持续生效,直到被重置。

setw(n) —— 设置输出宽度

🎯 功能:让下一次输出至少占 n 个字符位置,不够时默认用空格在左侧补齐。

💡 比喻:就像打印表格时先画好固定宽度的单元格:内容不够宽就自动补空格,超过宽度则原样输出。

🔧 声明:std::ostream& std::setw(int n);

↩️ 返回值:返回输出流本身,可继续链式输出。

C++
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    cout << "[" << setw(5) << 12 << "]" << endl;   // [   12]
    cout << "[" << setw(5) << 12 << 34 << "]" << endl; // [   1234]
    return 0;
}

setprecision(n) / fixed / scientific —— 控制浮点数精度

🎯 功能:setprecision(n) 设置有效数字或小数位数;配合 fixed 表示固定小数位,scientific 表示科学计数法。

💡 比喻:就像切蛋糕:setprecision 决定切几块,fixed 是“每块大小固定”,scientific 是“用 1.23×10ⁿ 的方式描述”。

🔧 声明:std::ostream& std::setprecision(int n); std::ostream& std::fixed(std::ostream& os);

↩️ 返回值:返回输出流本身。一旦设置,后续浮点输出都会受影响,直到再次修改。

C++
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    double pi = 3.1415926535;
    cout << setprecision(4) << pi << endl;          // 3.142
    cout << fixed << setprecision(2) << pi << endl; // 3.14
    cout << scientific << pi << endl;               // 3.14e+00
    return 0;
}

setfill(c) —— 设置填充字符

🎯 功能:配合 setw 使用,把默认的左侧空格填充换成指定字符。

💡 比喻:就像表格空格里不再画空白,而是写上你喜欢的符号,比如 0 或 *。

🔧 声明:std::ostream& std::setfill(char c);

↩️ 返回值:返回输出流本身。设置后会一直生效,直到被新的 setfill 覆盖。

C++
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    cout << setfill('0') << setw(5) << 12 << endl; // 00012
    cout << setfill('*') << setw(6) << 12 << endl; // ****12
    return 0;
}

left / right / internal —— 控制对齐方式

🎯 功能:left 左对齐,right 右对齐(默认),internal 让符号在最左、数值在右(常用于负数)。

💡 比喻:就像Word里的左对齐、右对齐、居中对齐按钮。配合 setw 一起用,才能看出对齐效果。

🔧 声明:std::ostream& std::left(std::ostream& os); std::ostream& std::right(std::ostream& os);

↩️ 返回值:返回输出流本身。对齐方式会持续生效。

C++
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
    cout << left  << setw(8) << 12 << "|" << endl; // 12      |
    cout << right << setw(8) << 12 << "|" << endl; //       12|
    return 0;
}

boolalpha / noboolalpha —— 布尔值文字输出

🎯 功能:boolalpha 让 bool 输出 true/false;noboolalpha 恢复为 1/0。

💡 比喻:就像把“是/否”开关的指示灯从数字改成文字,让人一看就懂。

🔧 声明:std::ostream& std::boolalpha(std::ostream& os);

↩️ 返回值:返回输出流本身。设置后持续生效。

C++
#include <iostream>
using namespace std;
int main() {
    bool ok = true;
    cout << boolalpha << ok << " " << false << endl;   // true false
    cout << noboolalpha << ok << " " << false << endl; // 1 0
    return 0;
}

hex / dec / oct —— 设置整数进制

🎯 功能:分别把整数按十六进制、十进制、八进制输出。

💡 比喻:就像同一个数字用中文、英文、阿拉伯文写:值一样,但显示形式不同。

🔧 声明:std::ostream& std::hex(std::ostream& os); std::ostream& std::dec(std::ostream& os);

↩️ 返回值:返回输出流本身。进制设置会持续生效。

C++
#include <iostream>
using namespace std;
int main() {
    int n = 255;
    cout << dec << n << endl; // 255
    cout << hex << n << endl; // ff
    cout << oct << n << endl; // 377
    return 0;
}

showpoint / noshowpoint —— 强制显示小数点

🎯 功能:showpoint 强制浮点数显示小数点及尾随零;noshowpoint 恢复默认。

💡 比喻:就像写金额时总要带“元”和小数点,showpoint 让 5.0 显示成 5.000,看起来更正式。

🔧 声明:std::ostream& std::showpoint(std::ostream& os);

↩️ 返回值:返回输出流本身。设置后持续生效。

C++
#include <iostream>
using namespace std;
int main() {
    double x = 5.0;
    cout << showpoint << x << endl;    // 5.00000
    cout << noshowpoint << x << endl;  // 5
    return 0;
}
⚠️ 易混淆点: 1. setw 只对下一次输出有效;fixed、hex、setfill 等会持续生效。 2. fixed 设置的是小数位数,需要配合 setprecision 使用。 3. 进制操纵符也会影响输入流 cin,例如 cin >> hex >> n 会以十六进制读取。
💡 常见组合:cout << fixed << setprecision(2) << setw(8) << setfill('0') << right << x; 可一次性控制精度、宽度、填充和对齐。

👉下一节:fstream —— 读写文件 →

▼1.3 <fstream> —— 读写文件C++

是什么

📚 fstream 把文件当作流来读写,ifstream 负责输入、ofstream 负责输出、fstream 可同时进行输入输出。

💡 生活比喻:文件就像一本笔记本——ifstream 是翻开书阅读,ofstream 是拿起笔书写,fstream 则是边读边写的活页本。

⚠️ 注意事项:打开文件后务必检查是否成功;默认写模式会清空原有内容,追加请用 ios::app。

ifstream 构造函数/打开文件 —— 从文件读取

🎯 功能:创建输入文件流对象并关联到指定文件,用于从硬盘读取数据。

💡 比喻:就像图书馆借书:构造函数是办理借阅,>> 操作符则是一页一页地读内容。

🔧 声明:std::ifstream(const std::string& filename, ios_base::openmode mode = ios_base::in);

↩️ 返回值:构造完成后可用 bool 转换或 is_open() 判断是否成功打开。

C++
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ifstream fin("data.txt");
    if (!fin) {
        cerr << "无法打开 data.txt" << endl;
        return 1;
    }
    string word;
    fin >> word;
    cout << "读取到: " << word << endl;
    fin.close();
    return 0;
}

ofstream 构造函数/打开文件 —— 向文件写入

🎯 功能:创建输出文件流对象并关联到指定文件,用于把数据写入硬盘。

💡 比喻:就像打开一本新笔记本准备记录:如果笔记本已存在,默认会撕掉旧内容重写。

🔧 声明:std::ofstream(const std::string& filename, ios_base::openmode mode = ios_base::out);

↩️ 返回值:构造完成后可用 bool 转换或 is_open() 判断是否成功打开。

C++
#include <fstream>
#include <iostream>
using namespace std;

int main() {
    ofstream fout("output.txt");
    if (!fout) {
        cerr << "无法创建 output.txt" << endl;
        return 1;
    }
    fout << "Hello, fstream!" << endl;
    fout << "C++ 文件流写入示例" << endl;
    fout.close();
    return 0;
}

fstream 与打开模式(ios::in / ios::out / ios::app / ios::binary 等)

🎯 功能:fstream 既可读也可写,通过打开模式组合控制访问权限和行为。

💡 比喻:就像一把多功能瑞士军刀:不同模式是不同刀刃,按需求组合才能切出正确形状。

🔧 声明:std::fstream(const std::string& filename, ios_base::openmode mode);

↩️ 返回值:按指定模式打开文件,读写操作受其约束。

C++
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    fstream fs("data.txt", ios::in | ios::out | ios::trunc);
    if (!fs) {
        cerr << "无法打开 data.txt" << endl;
        return 1;
    }
    fs << "写入一行内容" << endl;
    fs.seekg(0);
    string line;
    getline(fs, line);
    cout << "读出: " << line << endl;
    fs.close();
    return 0;
}

getline / >> / get 读文件

🎯 功能:三种常用读取方式:>> 按空白分隔读取、getline 按行读取、get 逐个字符读取。

💡 比喻:就像吃面条:>> 是一口一口夹断,getline 是一碗一碗端,get 则是一根一根挑。

🔧 声明:std::istream& getline(std::istream& is, std::string& str); std::istream& operator>>(std::istream& is, T& value); std::istream& get(char& c);

↩️ 返回值:流对象本身,可用于链式调用或判断读取是否成功。

C++
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ofstream fout("demo.txt");
    fout << "Hello World\n第二行内容\n";
    fout.close();

    ifstream fin("demo.txt");
    string word, line;
    char c;

    fin >> word;
    cout << ">> 读取单词: " << word << endl;

    fin.ignore();
    getline(fin, line);
    cout << "getline 读取行: " << line << endl;

    fin.get(c);
    cout << "get 读取字符: " << c << endl;

    fin.close();
    return 0;
}

close 关闭文件

🎯 功能:显式关闭文件流,释放关联的系统资源;对象析构时也会自动关闭。

💡 比喻:就像读完书后合上封面、还回书架,让资源可以被他人使用。

🔧 声明:void close();

↩️ 返回值:无返回值。

C++
#include <fstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ofstream fout("temp.txt");
    fout << "临时数据" << endl;
    fout.close();

    ifstream fin("temp.txt");
    string s;
    fin >> s;
    cout << "关闭后重新读取: " << s << endl;
    fin.close();
    return 0;
}

👉下一节:1.4 字符串流 →

▼1.4 <sstream> —— 字符串流C++

是什么

📚 <sstream> 把字符串当内存中的「文件」来读写,istringstream 解析字符串、ostringstream 构造字符串、stringstream 同时读写。

💡 生活比喻:普通字符串像一张写满字的纸;字符串流则像是一块可擦写小黑板,既能往上写,也能从上面读,还能反复擦除重用。

⚠️ 注意事项:复用 stringstream 时,clear() 只清状态标志,str("") 才清内容,两者通常要一起用。

istringstream —— 从字符串解析数据

🎯 功能:将已有字符串当作输入源,按格式提取各种类型的数据。

💡 比喻:就像把快递单上的文字拆成姓名、电话、地址,分别填入不同格子里。

🔧 声明:std::istringstream(const std::string& str);

↩️ 返回值:解析成功时目标变量被赋值,失败时流进入失败状态。

C++
#include <sstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    string data = "Alice 25 98.5";
    istringstream iss(data);

    string name;
    int age;
    double score;
    iss >> name >> age >> score;

    cout << name << " " << age << " " << score << endl;
    return 0;
}

ostringstream —— 把数据写入字符串

🎯 功能:把各种类型数据按流式操作拼接成一个字符串。

💡 比喻:就像把不同颜色的积木按顺序塞进透明管子里,最后倒出完整的一串。

🔧 声明:std::ostringstream();

↩️ 返回值:通过 str() 成员函数返回拼接好的 std::string。

C++
#include <sstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    ostringstream oss;
    oss << "Name: " << "Bob" << ", Age: " << 30 << ", Score: " << 88.5;

    string result = oss.str();
    cout << result << endl;
    return 0;
}

stringstream —— 同时读写字符串

🎯 功能:兼具输入和输出能力,可在同一段内存字符串上反复读写。

💡 比喻:就像一块双面磁性白板:一面可以写,另一面可以读,擦净后还能反复使用。

🔧 声明:std::stringstream();

↩️ 返回值:通过 str() 获取或设置内容,通过 >> / << 进行读写。

C++
#include <sstream>
#include <iostream>
#include <string>
using namespace std;

int main() {
    stringstream ss;
    ss << "2024-08-12";

    string date;
    ss >> date;
    cout << "读取: " << date << endl;

    ss.clear();
    ss.str("");
    ss << "New content";
    cout << "重置后: " << ss.str() << endl;

    return 0;
}

👉下一节:2.1 vector →

▼2.1 <vector> —— 动态数组(最常用!)C++

是什么

📚 <vector> 是 C++ 的“会自动变长的数组”。普通数组的大小在编译时就定死了,而 vector 能随时添加、删除元素,并在容量不够时自动扩容。

💡 生活比喻:vector 就像一列连续的长凳。刚开始只坐了 3 个人,后面来了新同学就自动往后加椅子;只要知道座位号(下标),一步就能走到对应位置。但如果在队伍中间插入或删掉一个人,后面所有人都得挪位置。

⚠️ 核心特点:元素在内存中连续存放,支持随机访问;尾部增删快,中间增删慢。

vector<T> —— 创建动态数组

🎯 功能:声明一个类型为 T 的 vector。可以用空构造、指定长度、指定长度和初值,或直接用初始化列表。

💡 比喻:就像租房子——可以租空房、租 5 间空房、租 5 间都配好家具的房子,也可以直接拎包入住精装房。

🔧 常见形式:vector<T> v; / vector<T> v(n); / vector<T> v(n, val); / vector<T> v = {a, b, c};

↩️ 说明:构造完成后 v.size() 返回当前元素个数;不指定初值时,内置类型会被默认初始化(例如 0)。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v1;                 // 空 vector
    vector<int> v2(5);              // 5 个元素,默认值 0
    vector<int> v3(5, 10);          // 5 个元素,都是 10
    vector<int> v4 = {1, 2, 3, 4};  // 初始化列表(C++11)
    cout << "v4 size = " << v4.size() << endl;  // 4
    return 0;
}

push_back(x) —— 在末尾添加元素

🎯 功能:接受一个元素 x,把它复制到 vector 的末尾。如果当前容量不够,会自动扩容。

💡 比喻:就像在队伍最后面新增一把椅子,让新同学坐下。大多数时候非常快,但偶尔椅子不够用了,需要把所有人搬到更大的场地(扩容)。

🔧 声明:void push_back(const T& x);

↩️ 返回值:无返回值。扩容后 size() 增加 1。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    v.push_back(40);  // 末尾添加 40
    // v 变成 {10, 20, 30, 40}
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

pop_back() —— 删除末尾元素

🎯 功能:删掉 vector 的最后一个元素,不需要参数。调用前请确保 vector 非空。

💡 比喻:就像把队伍最后一把椅子撤掉,上面坐的人离场。这个动作只影响队尾,其他人纹丝不动。

🔧 声明:void pop_back();

↩️ 返回值:无返回值。size() 减少 1,容量通常不变。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    v.pop_back();  // 删除末尾的 30
    // v 变成 {10, 20}
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

size() / empty() —— 查个数、判空

🎯 功能:size() 返回当前元素个数;empty() 在个数为 0 时返回 true。

💡 比喻:size() 就像数队伍里有多少人;empty() 就像看队伍是不是空荡荡。

🔧 声明:size_t size() const; bool empty() const;

↩️ 返回值:size() 返回元素个数;empty() 返回是否为空。两者都是 O(1)。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v;
    cout << "开始时为空?" << (v.empty() ? "是" : "否") << endl;
    v.push_back(1);
    v.push_back(2);
    cout << "元素个数:" << v.size() << endl;  // 2
    cout << "现在为空?" << (v.empty() ? "是" : "否") << endl;
    return 0;
}

clear() —— 清空所有元素

🎯 功能:删除 vector 中的所有元素,让它变成空 vector。

💡 比喻:就像一键清空收纳盒,把所有东西拿出来扔掉,盒子本身还在。

🔧 声明:void clear();

↩️ 返回值:无返回值。size() 变为 0,容量通常不变。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    v.clear();  // 清空
    cout << "清空后 size = " << v.size() << endl;  // 0
    return 0;
}

operator[] / at(i) —— 访问第 i 个元素

🎯 功能:v[i] 和 v.at(i) 都返回第 i 个元素的引用。下标从 0 开始。

💡 比喻:就像凭座位号找人。v[i] 是“直接喊座位号”,喊错了可能出乱子;at(i) 是“先核对座位号在不在范围内”,错了会安全地报错。

🔧 声明:T& operator[](size_t i); T& at(size_t i);

↩️ 返回值:返回对应位置的元素引用,可以读取也可以修改。at 越界会抛出 out_of_range 异常。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    cout << "v[0] = " << v[0] << endl;        // 10
    cout << "v.at(1) = " << v.at(1) << endl;  // 20
    v[2] = 99;                                // 修改元素
    // v = {10, 20, 99}
    return 0;
}

front() / back() —— 访问首尾元素

🎯 功能:front() 返回第一个元素,back() 返回最后一个元素。

💡 比喻:就像直接看队伍最前面和最后面的人,不用数下标。

🔧 声明:T& front(); T& back();

↩️ 返回值:分别返回首元素和尾元素的引用。调用前请确保 vector 非空。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    cout << "首元素:" << v.front() << endl;  // 10
    cout << "尾元素:" << v.back() << endl;   // 30
    return 0;
}

insert(pos, x) —— 在指定位置插入元素

🎯 功能:在迭代器 pos 指向的位置之前插入元素 x。位置通常用 begin() + i 表示。

💡 比喻:就像让新同学插队到第 i 个位置,后面所有人都要向后挪一位,所以中间插入比较慢。

🔧 声明:iterator insert(iterator pos, const T& x);

↩️ 返回值:返回指向新插入元素的迭代器。中间插入的时间复杂度是 O(n)。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    v.insert(v.begin() + 1, 15);  // 在下标 1 处插入 15
    // v 变成 {10, 15, 20, 30}
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

erase(pos) —— 删除指定位置元素

🎯 功能:删除迭代器 pos 指向的元素。也可写成 erase(first, last) 删除一个区间。

💡 比喻:就像让队伍中某个人离开,后面的人都要向前补位,所以中间删除也比较慢。

🔧 声明:iterator erase(iterator pos); iterator erase(iterator first, iterator last);

↩️ 返回值:返回被删除元素下一个位置的迭代器。删除单个元素的时间复杂度是 O(n)。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30, 40};
    v.erase(v.begin() + 1);  // 删除下标 1 的元素 20
    // v 变成 {10, 30, 40}
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

resize(n) / resize(n, val) —— 改变元素个数

🎯 功能:resize(n) 把 vector 变成 n 个元素;新增元素默认初始化,也可指定为 val。若 n 小于当前大小,多余元素会被删除。

💡 比喻:就像重新调整队伍长度:人不够就拉几个占位,人多了就把后面的裁掉。

🔧 声明:void resize(size_t n); void resize(size_t n, const T& val);

↩️ 返回值:无返回值。时间复杂度与新增/删除元素个数成正比。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    v.resize(5);        // {10, 20, 30, 0, 0}
    v.resize(7, 8);     // {10, 20, 30, 0, 0, 8, 8}
    v.resize(2);        // {10, 20}
    for (int x : v) cout << x << " ";
    cout << endl;
    return 0;
}

reserve(n) / capacity() —— 预留空间与当前容量

🎯 功能:reserve(n) 提前分配至少能容纳 n 个元素的空间;capacity() 返回当前容量(不移动元素的前提下最多能装多少)。

💡 比喻:就像提前租一个更大的场地,但场上实际坐多少人由 size() 决定。这样可以避免每次来人都换场地(扩容)。

🔧 声明:void reserve(size_t n); size_t capacity() const;

↩️ 返回值:reserve 无返回值;capacity() 返回当前容量。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v;
    v.reserve(100);
    cout << "容量:" << v.capacity() << ",元素数:" << v.size() << endl;
    // 容量 &gt;= 100,size = 0
    return 0;
}

begin() / end() —— 迭代器

🎯 功能:begin() 返回指向第一个元素的迭代器;end() 返回指向“最后一个元素之后”的迭代器,常用于遍历。

💡 比喻:迭代器就像队伍里的引导员:begin() 站在第一个人前面,end() 站在最后一个人后面,两个引导员之间的就是所有成员。

🔧 声明:iterator begin(); iterator end();

↩️ 返回值:返回对应位置的迭代器。对空 vector,begin() == end()。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    for (auto it = v.begin(); it != v.end(); ++it) {
        cout << *it << " ";  // 10 20 30
    }
    cout << endl;
    return 0;
}

三种常用遍历方式

方式一:下标遍历——最直观,适合需要知道位置的场景。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    for (size_t i = 0; i < v.size(); i++) {
        cout << "v[" << i << "] = " << v[i] << endl;
    }
    return 0;
}

方式二:范围 for(C++11,推荐)——简洁,不需要手动管理下标。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    for (int x : v) {
        cout << x << " ";  // 10 20 30
    }
    cout << endl;
    return 0;
}

方式三:迭代器遍历——最通用,配合 insert / erase 时使用。

C++
#include <vector>
#include <iostream>
using namespace std;
int main() {
    vector<int> v = {10, 20, 30};
    for (auto it = v.begin(); it != v.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}
⚠️ 安全提醒:v[i] 越界访问是未定义行为,可能导致崩溃或奇怪结果;需要边界检查时优先用 v.at(i)。另外,插入/删除元素可能使已有迭代器失效,不要在遍历过程中随意修改容器。
💡 什么时候用 vector:需要随机访问、主要在尾部增删、数据量会变化时,vector 是首选容器。只在频繁中间插入删除时,才考虑 list 或 deque。

👉下一节:list —— 双向链表 →

▼2.2 <list> —— 双向链表C++

是什么

📚 list 是 STL 中的双向链表容器,插入和删除效率高,但不支持随机访问。

💡 生活比喻:list 像一列火车,每节车厢都知道前后车厢是谁;在任意车厢之间加挂或摘掉车厢很快,但要找到第 N 节车厢必须从头数过去。

⚠️ 注意事项:list 没有 [] 运算符和 at();遍历一般用迭代器;sort 等算法需调用成员函数版本,因为通用 std::sort 需要随机访问迭代器。

list 构造函数

🎯 功能:构造空 list、指定个数默认值、拷贝构造、迭代器区间构造等。

💡 比喻:就像调度不同长度的列车——可以是一列空车、一列装满相同货物的车,或复制一列已有列车的编组。

🔧 声明:std::list<T> lst;
std::list<T> lst(n);
std::list<T> lst(n, value);
std::list<T> lst(other);
std::list<T> lst(first, last);

↩️ 返回值:构造出的 list 对象。

C++
#include <iostream>
#include <list>
#include <vector>
using namespace std;

int main() {
    list<int> a;
    list<int> b(3, 100);
    list<int> c(b);

    vector<int> v = {1, 2, 3, 4};
    list<int> d(v.begin(), v.end());

    cout << "b: ";
    for (int x : b) cout << x << " ";
    cout << "\nd: ";
    for (int x : d) cout << x << " ";
    cout << endl;

    return 0;
}

push_back / push_front / pop_back / pop_front

🎯 功能:在链表尾部或头部添加/移除元素。

💡 比喻:就像列车在始发站或终点站快速加挂/摘掉车厢,两端操作都非常高效。

🔧 声明:void push_back(const T& value);
void push_front(const T& value);
void pop_back();
void pop_front();

↩️ 返回值:无返回值;pop 前需确保容器非空。

C++
#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> lst = {2, 3};

    lst.push_front(1);
    lst.push_back(4);

    cout << "添加后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    lst.pop_front();
    lst.pop_back();

    cout << "删除后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    return 0;
}

insert / erase

🎯 功能:insert 在指定迭代器位置前插入元素;erase 删除指定位置的元素或区间。

💡 比喻:在列车中间加挂车厢时,只需把前后两节车厢重新挂钩;摘掉车厢同理,不会影响整列火车的其他部分。

🔧 声明:iterator insert(iterator pos, const T& value);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);

↩️ 返回值:insert 返回指向新元素的迭代器;erase 返回被删除元素之后的迭代器。

C++
#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> lst = {1, 3, 5};

    auto it = lst.begin();
    ++it;
    lst.insert(it, 2);

    cout << "insert 后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    it = lst.begin();
    ++it;
    lst.erase(it);

    cout << "erase 后: ";
    for (int x : lst) cout << x << " ";
    cout << endl;

    return 0;
}

sort / merge / reverse / unique

🎯 功能:成员函数算法——排序、合并有序链表、反转、去重相邻重复元素。

💡 比喻:list 自带一套列车调度工具:sort 重新排列车厢顺序,merge 把两列已排序列车拼接,reverse 让列车掉头,unique 合并连续重复车厢。

🔧 声明:void sort();
void merge(list& other);
void reverse();
void unique();

↩️ 返回值:无返回值;直接修改当前 list。

C++
#include <iostream>
#include <list>
using namespace std;

int main() {
    list<int> a = {3, 1, 4, 1, 5, 9, 2, 6};
    list<int> b = {2, 4, 6, 8};

    a.sort();
    a.unique();
    a.merge(b);
    a.reverse();

    cout << "处理后: ";
    for (int x : a) cout << x << " ";
    cout << endl;

    return 0;
}

👉下一节:2.3 deque →

▼2.3 <deque> —— 双端队列C++

是什么

📚 deque 是双端队列容器,支持两端高效插入删除,同时支持随机访问。

💡 生活比喻:deque 像一条两端都有闸机的传送带,货物可以从左右两端快速上下;同时中控室可以直接按编号调取任意位置的货物。

⚠️ 注意事项:deque 的迭代器在某些插入操作后可能失效;其内存通常分块存储,因此中间插入效率不如 list,随机访问效率略逊于 vector。

deque 构造函数

🎯 功能:构造空 deque、指定个数默认值、拷贝构造、迭代器区间构造等。

💡 比喻:就像搭建不同规格的传送带——空传送带、铺满相同货物的传送带,或复制一条已有传送带。

🔧 声明:std::deque<T> dq;
std::deque<T> dq(n);
std::deque<T> dq(n, value);
std::deque<T> dq(other);
std::deque<T> dq(first, last);

↩️ 返回值:构造出的 deque 对象。

C++
#include <iostream>
#include <deque>
#include <vector>
using namespace std;

int main() {
    deque<int> a;
    deque<int> b(3, 7);
    deque<int> c(b);

    vector<int> v = {1, 2, 3};
    deque<int> d(v.begin(), v.end());

    cout << "b: ";
    for (int x : b) cout << x << " ";
    cout << "\nd: ";
    for (int x : d) cout << x << " ";
    cout << endl;

    return 0;
}

push_back / push_front / pop_back / pop_front

🎯 功能:在双端队列的尾部或头部添加/移除元素。

💡 比喻:就像传送带两端的闸机,工人可以随时在左右两端装卸货物。

🔧 声明:void push_back(const T& value);
void push_front(const T& value);
void pop_back();
void pop_front();

↩️ 返回值:无返回值;pop 前需确保非空。

C++
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> dq = {2, 3};

    dq.push_front(1);
    dq.push_back(4);

    cout << "添加后: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    dq.pop_front();
    dq.pop_back();

    cout << "删除后: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    return 0;
}

operator[] / at / front / back

🎯 功能:访问元素;[] 不做边界检查,at 做边界检查并可能抛异常,front/back 访问首尾元素。

💡 比喻:operator[] 像直接按货架编号拿货,图快但不验证;at 像经过仓库管理员核对编号,出错会叫停;front/back 则是直接取传送带两端的货物。

🔧 声明:T& operator[](size_type pos);
T& at(size_type pos);
T& front();
T& back();

↩️ 返回值:返回对应位置元素的引用。

C++
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> dq = {10, 20, 30, 40, 50};

    cout << "dq[2] = " << dq[2] << endl;
    cout << "dq.at(2) = " << dq.at(2) << endl;
    cout << "front = " << dq.front() << endl;
    cout << "back = " << dq.back() << endl;

    return 0;
}

insert / erase

🎯 功能:在指定位置插入或删除元素。

💡 比喻:在传送带中间加装或拆掉一段,需要牵动附近的货物,因此比两端操作慢一些。

🔧 声明:iterator insert(iterator pos, const T& value);
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);

↩️ 返回值:insert 返回指向新元素的迭代器;erase 返回被删除元素之后的迭代器。

C++
#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> dq = {1, 3, 5};

    auto it = dq.begin() + 1;
    dq.insert(it, 2);

    cout << "insert 后: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    dq.erase(dq.begin() + 1);

    cout << "erase 后: ";
    for (int x : dq) cout << x << " ";
    cout << endl;

    return 0;
}

👉下一节:2.4 array →

▼2.4 <array> —— 固定大小数组(C++11)C++

是什么

📚 array 是对固定大小数组的轻量封装,保留数组的性能,同时提供 STL 容器接口。

💡 生活比喻:array 像一组固定长度的抽屉柜——抽屉数量在出厂时就定好了,不能增减,但每个抽屉都能用统一的方式打开和查看。

⚠️ 注意事项:array 大小是模板参数,必须在编译期确定;不能自动扩容;可以整体赋值、比较、用 get 获取元素。

array 构造函数

🎯 功能:array 大小在模板参数中指定,可用初始化列表填充。

💡 比喻:就像购买一个 5 格抽屉柜,格子数出厂固定,但你可以决定每个格子里放什么。

🔧 声明:std::array<T, N> arr = { ... };

↩️ 返回值:构造出的 array 对象。

C++
#include <iostream>
#include <array>
using namespace std;

int main() {
    array<int, 5> arr = {1, 2, 3, 4, 5};
    array<int, 3> zeros = {0};

    cout << "arr: ";
    for (int x : arr) cout << x << " ";
    cout << "\nzeros: ";
    for (int x : zeros) cout << x << " ";
    cout << endl;

    return 0;
}

at / operator[] / front / back / data

🎯 功能:访问元素;at 做边界检查,[] 不做,front/back 访问首尾,data 返回底层数组指针。

💡 比喻:at 是管理员核对抽屉编号后取物,[] 是直接拉开抽屉,front/back 是最上和最下抽屉,data 则是拿到柜子的物理地址。

🔧 声明:T& at(size_type pos);
T& operator[](size_type pos);
T& front();
T& back();
T* data();

↩️ 返回值:返回元素引用或底层数组指针。

C++
#include <iostream>
#include <array>
using namespace std;

int main() {
    array<int, 4> arr = {10, 20, 30, 40};

    cout << "arr[1] = " << arr[1] << endl;
    cout << "arr.at(1) = " << arr.at(1) << endl;
    cout << "front = " << arr.front() << endl;
    cout << "back = " << arr.back() << endl;
    cout << "data 指向首元素 = " << *arr.data() << endl;

    return 0;
}

size / empty / fill / swap

🎯 功能:size 返回元素个数,empty 判断是否为空,fill 用指定值填充全部元素,swap 交换两个同尺寸 array。

💡 比喻:size 是抽屉总数,empty 看柜子是否空置,fill 是把所有抽屉装上同一种物品,swap 则是两个同规格柜子整体对调位置。

🔧 声明:size_type size() const;
bool empty() const;
void fill(const T& value);
void swap(array& other);

↩️ 返回值:size 返回元素个数;empty 返回是否为空;fill 和 swap 无返回值。

C++
#include <iostream>
#include <array>
using namespace std;

int main() {
    array<int, 4> a = {1, 2, 3, 4};
    array<int, 4> b = {9, 9, 9, 9};

    cout << "a.size() = " << a.size() << ", empty = " << a.empty() << endl;

    a.fill(5);
    cout << "fill 后 a: ";
    for (int x : a) cout << x << " ";
    cout << endl;

    a.swap(b);
    cout << "swap 后 a: ";
    for (int x : a) cout << x << " ";
    cout << endl;

    return 0;
}

get<I> / tuple_size / tuple_element

🎯 功能:array 支持 tuple 接口,get<I> 获取第 I 个元素,tuple_size 获取大小,tuple_element 获取元素类型。

💡 比喻:把 array 看作一份装订好的档案册,get<I> 是按索引抽出某一页,tuple_size 是总页数,tuple_element 是某一页的类型标签。

🔧 声明:T& get<I>(array<T, N>& arr);
std::tuple_size<array<T, N>>::value
std::tuple_element<I, array<T, N>>::type

↩️ 返回值:get 返回第 I 个元素引用;tuple_size 是编译期常量;tuple_element 是类型别名。

C++
#include <iostream>
#include <array>
#include <tuple>
using namespace std;

int main() {
    array<int, 3> arr = {100, 200, 300};

    cout << "get<0> = " << get<0>(arr) << endl;
    cout << "get<2> = " << get<2>(arr) << endl;

    constexpr size_t n = tuple_size<array<int, 3>>::value;
    cout << "tuple_size = " << n << endl;

    tuple_element<1, array<int, 3>>::type x = arr[1];
    cout << "tuple_element<1> 值 = " << x << endl;

    return 0;
}

👉下一节:2.5 forward_list →

▼2.5 <forward_list> —— 单向链表(C++11)C++

是什么

📚 forward_list 是单向链表容器,每个节点只保存下一个节点的指针,内存开销比 list 更小。

💡 生活比喻:forward_list 像一串只能单向传递的接力棒队伍,每个人只知道把棒交给下一个人;队伍紧凑、开销小,但回头找人很麻烦。

⚠️ 注意事项:forward_list 只提供前向迭代器,没有 size() 成员函数,插入删除多在给定位置之后进行(insert_after / erase_after)。

forward_list 构造函数

🎯 功能:构造空 forward_list、指定个数默认值、拷贝构造、迭代器区间构造等。

💡 比喻:就像组织一支接力队——可以空队、全员持相同接力棒,或复制一支已有队伍的编制。

🔧 声明:std::forward_list<T> fl;
std::forward_list<T> fl(n);
std::forward_list<T> fl(n, value);
std::forward_list<T> fl(other);
std::forward_list<T> fl(first, last);

↩️ 返回值:构造出的 forward_list 对象。

C++
#include <iostream>
#include <forward_list>
#include <vector>
using namespace std;

int main() {
    forward_list<int> a;
    forward_list<int> b(3, 7);
    forward_list<int> c(b);

    vector<int> v = {1, 2, 3};
    forward_list<int> d(v.begin(), v.end());

    cout << "b: ";
    for (int x : b) cout << x << " ";
    cout << "\nd: ";
    for (int x : d) cout << x << " ";
    cout << endl;

    return 0;
}

push_front / pop_front

🎯 功能:在链表头部添加或移除元素。

💡 比喻:接力队只能从排头加入或淘汰队员,因为每个队员只知道自己后面是谁。

🔧 声明:void push_front(const T& value);
void pop_front();

↩️ 返回值:无返回值;pop 前需确保非空。

C++
#include <iostream>
#include <forward_list>
using namespace std;

int main() {
    forward_list<int> fl = {2, 3};

    fl.push_front(1);
    fl.push_front(0);

    cout << "push 后: ";
    for (int x : fl) cout << x << " ";
    cout << endl;

    fl.pop_front();

    cout << "pop 后: ";
    for (int x : fl) cout << x << " ";
    cout << endl;

    return 0;
}

insert_after / erase_after

🎯 功能:在给定迭代器之后插入或删除元素。

💡 比喻:在接力队中,教练只能在某位队员身后插入新队员,或让某位队员后面的队员离队,因为没人知道自己的前一位是谁。

🔧 声明:iterator insert_after(iterator pos, const T& value);
iterator erase_after(iterator pos);
iterator erase_after(iterator first, iterator last);

↩️ 返回值:insert_after 返回指向新元素的迭代器;erase_after 返回被删除元素之后的迭代器。

C++
#include <iostream>
#include <forward_list>
using namespace std;

int main() {
    forward_list<int> fl = {1, 3, 5};

    auto it = fl.begin();
    fl.insert_after(it, 2);

    cout << "insert_after 后: ";
    for (int x : fl) cout << x << " ";
    cout << endl;

    it = fl.begin();
    fl.erase_after(it);

    cout << "erase_after 后: ";
    for (int x : fl) cout << x << " ";
    cout << endl;

    return 0;
}

sort / reverse / unique / splice_after

🎯 功能:成员函数算法——排序、反转、去重、拼接另一个 forward_list。

💡 比喻:这是接力队的专项训练:sort 按速度重新排队,reverse 让队伍反向传递,unique 合并连续重复的队员,splice_after 把另一支队伍接到指定队员身后。

🔧 声明:void sort();
void reverse();
void unique();
void splice_after(iterator pos, forward_list& other);

↩️ 返回值:无返回值;直接修改当前 forward_list。

C++
#include <iostream>
#include <forward_list>
using namespace std;

int main() {
    forward_list<int> a = {3, 1, 4, 1, 5, 9, 2, 6};
    forward_list<int> b = {7, 8};

    a.sort();
    a.unique();
    a.reverse();

    auto it = a.begin();
    a.splice_after(it, b);

    cout << "处理后: ";
    for (int x : a) cout << x << " ";
    cout << endl;

    return 0;
}

👉下一节:2.6 set / multiset →

▼2.6 <stack> —— 栈C++

是什么

📚 stack 是 STL 提供的容器适配器,遵循后进先出(LIFO)原则,只允许在一端(栈顶)进行插入和删除。

💡 生活比喻:stack 像一摞盘子——最后放上去的盘子最先被取走,最先放上去的只能等上面全部拿走。

⚠️ 注意事项:stack 没有迭代器,不能遍历;top 前必须确认非空,否则行为未定义;默认底层容器是 deque,也可以指定 vector/list。

stack 构造函数

🎯 功能:构造空栈,或用已有容器拷贝构造。

💡 比喻:就像新买一组空盘子,或直接用别人的一摞盘子复制一份。

🔧 声明:std::stack<T> st;
std::stack<T, Container> st;
std::stack<T> st(container);

↩️ 返回值:构造出的 stack 对象。

C++
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

int main() {
    stack<int> s1;

    vector<int> v = {1, 2, 3};
    stack<int, vector<int>> s2(v);

    cout << "s2.top() = " << s2.top() << endl;
    cout << "s2.size() = " << s2.size() << endl;
    return 0;
}

push / pop / top —— 入栈、出栈与访问栈顶

🎯 功能:push 把元素压入栈顶,pop 移除栈顶元素,top 访问栈顶元素。

💡 比喻:push 是往盘子摞上再放一个盘子,pop 是拿走最上面的盘子,top 是看一眼最上面的盘子是什么。

🔧 声明:void push(const T& value);
void pop();
T& top();

↩️ 返回值:push/pop 无返回值;top 返回栈顶元素引用。

C++
#include <iostream>
#include <stack>
using namespace std;

int main() {
    stack<int> s;
    s.push(10);
    s.push(20);
    s.push(30);

    cout << "栈顶: " << s.top() << endl;
    s.pop();
    cout << "弹出后栈顶: " << s.top() << endl;
    return 0;
}

empty / size —— 判空与大小

🎯 功能:empty 判断栈是否为空,size 返回栈中元素个数。

💡 比喻:empty 看盘子摞是不是空了,size 数一共有几个盘子。

🔧 声明:bool empty() const;
size_type size() const;

↩️ 返回值:empty 返回是否为空;size 返回元素个数。

C++
#include <iostream>
#include <stack>
using namespace std;

int main() {
    stack<int> s;
    cout << "是否为空: " << (s.empty() ? "是" : "否") << endl;
    cout << "大小: " << s.size() << endl;

    s.push(1);
    s.push(2);
    cout << "压入后大小: " << s.size() << endl;
    cout << "是否为空: " << (s.empty() ? "是" : "否") << endl;
    return 0;
}

emplace / swap —— 原地构造与交换

🎯 功能:emplace 在栈顶原地构造元素,swap 交换两个栈的内容。

💡 比喻:emplace 像直接在盘子摞顶端烧制一个定制盘子,省去先做好再搬上去的步骤;swap 则是两摞盘子整体对调。

🔧 声明:template void emplace(Args&&... args);
void swap(stack& other);

↩️ 返回值:均无返回值。

C++
#include <iostream>
#include <stack>
#include <string>
using namespace std;

int main() {
    stack<pair<int, string>> s;
    s.emplace(1, "apple");
    s.emplace(2, "banana");

    cout << "top: (" << s.top().first << ", " << s.top().second << ")" << endl;

    stack<pair<int, string>> other;
    other.emplace(3, "cherry");

    s.swap(other);
    cout << "swap 后 top: (" << s.top().first << ", " << s.top().second << ")" << endl;
    return 0;
}

👉下一节:2.7 queue →

▼2.7 <queue> —— 队列C++

是什么

📚 queue 是 STL 提供的容器适配器,遵循先进先出(FIFO)原则,元素从队尾入队,从队头出队。

💡 生活比喻:queue 像超市收银台的排队队伍——先到的人先结账离开,新来的人排在最后。

⚠️ 注意事项:queue 同样没有迭代器;front 和 back 前需确保非空;默认底层容器是 deque。

queue 构造函数

🎯 功能:构造空队列,或用已有容器拷贝构造。

💡 比喻:就像新开一条空通道,或复制一条已有队伍的排列。

🔧 声明:std::queue<T> q;
std::queue<T, Container> q;
std::queue<T> q(container);

↩️ 返回值:构造出的 queue 对象。

C++
#include <iostream>
#include <queue>
#include <list>
using namespace std;

int main() {
    queue<int> q1;

    list<int> lst = {1, 2, 3};
    queue<int, list<int>> q2(lst);

    cout << "q2.front() = " << q2.front() << endl;
    cout << "q2.back() = " << q2.back() << endl;
    return 0;
}

push / pop / front / back —— 入队、出队与访问两端

🎯 功能:push 在队尾入队,pop 移除队头元素,front/back 分别访问队头和队尾元素。

💡 比喻:push 是新顾客站到队伍末尾,pop 是前面的顾客结完账离开,front 看排第一的人,back 看最后到的人。

🔧 声明:void push(const T& value);
void pop();
T& front();
T& back();

↩️ 返回值:push/pop 无返回值;front/back 返回对应元素引用。

C++
#include <iostream>
#include <queue>
using namespace std;

int main() {
    queue<int> q;
    q.push(10);
    q.push(20);
    q.push(30);

    cout << "队头: " << q.front() << ", 队尾: " << q.back() << endl;
    q.pop();
    cout << "弹出后队头: " << q.front() << endl;
    return 0;
}

empty / size —— 判空与大小

🎯 功能:empty 判断队列是否为空,size 返回队列元素个数。

💡 比喻:empty 看队伍是不是空了,size 数队伍里还有多少人。

🔧 声明:bool empty() const;
size_type size() const;

↩️ 返回值:empty 返回是否为空;size 返回元素个数。

C++
#include <iostream>
#include <queue>
using namespace std;

int main() {
    queue<int> q;
    cout << "是否为空: " << (q.empty() ? "是" : "否") << endl;
    cout << "大小: " << q.size() << endl;

    q.push(1);
    q.push(2);
    cout << "入队后大小: " << q.size() << endl;
    return 0;
}

emplace / swap —— 原地构造与交换

🎯 功能:emplace 在队尾原地构造元素,swap 交换两个队列的内容。

💡 比喻:emplace 像在队伍末尾直接为新顾客办理登记手续;swap 则是两条队伍整体互换位置。

🔧 声明:template void emplace(Args&&... args);
void swap(queue& other);

↩️ 返回值:均无返回值。

C++
#include <iostream>
#include <queue>
#include <string>
using namespace std;

int main() {
    queue<pair<int, string>> q;
    q.emplace(1, "Alice");
    q.emplace(2, "Bob");

    cout << "front: (" << q.front().first << ", " << q.front().second << ")" << endl;

    queue<pair<int, string>> other;
    other.emplace(3, "Carol");

    q.swap(other);
    cout << "swap 后 front: (" << q.front().first << ", " << q.front().second << ")" << endl;
    return 0;
}

👉下一节:2.8 priority_queue →

▼2.8 <priority_queue> —— 优先队列(堆)C++

是什么

📚 priority_queue 是容器适配器,底层通常用堆实现,每次取出的都是当前优先级最高(默认最大)的元素。

💡 生活比喻:priority_queue 像急诊室分诊台——不是谁先来谁先看病,而是病情最重的优先处理;你也可以自定义「病情轻重」的判断规则。

⚠️ 注意事项:默认大顶堆,top 返回最大元素;自定义比较器时语义与 sort 相反(返回 true 表示优先级低);没有迭代器,不能直接遍历底层容器。

priority_queue 构造函数

🎯 功能:构造空优先队列,或用迭代器区间、初始化列表构造。

💡 比喻:就像根据一批病人的病情自动排好就诊顺序,病情重的自动排到最前面。

🔧 声明:std::priority_queue<T> pq;
std::priority_queue<T> pq(first, last);
std::priority_queue<T> pq(initializer_list);

↩️ 返回值:构造出的 priority_queue 对象。

C++
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

int main() {
    priority_queue<int> pq;
    pq.push(3);
    pq.push(1);
    pq.push(4);

    cout << "top = " << pq.top() << endl;

    vector<int> v = {5, 2, 7};
    priority_queue<int> pq2(v.begin(), v.end());
    cout << "pq2 top = " << pq2.top() << endl;
    return 0;
}

push / pop / top —— 入队、出队与访问队首

🎯 功能:push 插入元素并自动调整堆,pop 移除优先级最高元素,top 访问最高优先级元素。

💡 比喻:push 是新病人报到后由分诊台自动排序;pop 是看完病离开;top 是当前需要优先处理的病人。

🔧 声明:void push(const T& value);
void pop();
T& top();

↩️ 返回值:push/pop 无返回值;top 返回最高优先级元素引用。

C++
#include <iostream>
#include <queue>
using namespace std;

int main() {
    priority_queue<int> pq;
    pq.push(30);
    pq.push(10);
    pq.push(50);
    pq.push(20);

    while (!pq.empty()) {
        cout << pq.top() << " ";
        pq.pop();
    }
    cout << endl;
    return 0;
}

empty / size —— 判空与大小

🎯 功能:empty 判断优先队列是否为空,size 返回元素个数。

💡 比喻:empty 看候诊区是否还有人,size 看候诊人数。

🔧 声明:bool empty() const;
size_type size() const;

↩️ 返回值:empty 返回是否为空;size 返回元素个数。

C++
#include <iostream>
#include <queue>
using namespace std;

int main() {
    priority_queue<int> pq;
    pq.push(1);
    pq.push(2);
    pq.push(3);

    cout << "大小: " << pq.size() << endl;
    cout << "是否为空: " << (pq.empty() ? "是" : "否") << endl;

    while (!pq.empty()) pq.pop();
    cout << "清空后是否为空: " << (pq.empty() ? "是" : "否") << endl;
    return 0;
}

自定义比较器示例

🎯 功能:通过自定义比较器,可改变优先级规则,例如实现小顶堆或按结构体某字段排序。

💡 比喻:就像给分诊台换一套病情评估标准:可以改为「年龄小的优先」,或「等待时间久的优先」。

🔧 声明:std::priority_queue<T, Container, Compare> pq;

↩️ 返回值:构造出的优先队列,按 Compare 规则排列元素。

C++
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

struct Task {
    int id;
    int priority;
};

struct TaskCompare {
    bool operator()(const Task& a, const Task& b) {
        return a.priority > b.priority;  // 数字小优先级高(小顶堆)
    }
};

int main() {
    priority_queue<Task, vector<Task>, TaskCompare> pq;
    pq.push({1, 3});
    pq.push({2, 1});
    pq.push({3, 2});

    while (!pq.empty()) {
        Task t = pq.top();
        cout << "Task " << t.id << " (priority " << t.priority << ")\n";
        pq.pop();
    }
    return 0;
}

👉下一节:2.9 set / multiset →

▼2.9 <set> / <multiset> —— 有序集合C++

是什么

📚 std::set 和 std::multiset 是基于红黑树实现的有序关联容器,元素按关键字自动排序;set 不允许重复关键字,multiset 允许重复。

💡 生活比喻:set 像一本词汇表,每个单词只出现一次且按字母顺序排列;multiset 则像允许同义词多次出现的词频表。

⚠️ 注意事项:插入、删除、查找时间复杂度均为 O(log n);容器内元素不可修改(关键字是 const);自定义比较器必须满足严格弱序,否则行为未定义。

set 构造函数

🎯 功能:可构造空 set、拷贝构造、用迭代器区间或初始化列表构造;multiset 接口相同,仅允许重复。

💡 比喻:像开办一个按学号排序的班级花名册;可以一开始是空册子,也可以直接按已有名单或花名册拷贝生成。

🔧 声明:std::set<T> s;
std::set<T> s(first, last);
std::set<T> s(init_list);
std::set<T, Compare> s;

↩️ 返回值:构造出的 set/multiset 对象。

C++
#include <iostream>
#include <set>
#include <vector>
using namespace std;

int main() {
    set<int> s1;
    s1.insert(3);
    s1.insert(1);
    s1.insert(2);

    vector<int> v = {5, 2, 5, 8};
    set<int> s2(v.begin(), v.end());

    multiset<int> ms = {4, 4, 4, 1};

    cout << "s1: ";
    for (int x : s1) cout << x << " ";
    cout << "\nms: ";
    for (int x : ms) cout << x << " ";
    cout << endl;
    return 0;
}

insert / emplace

🎯 功能:insert 插入元素(set 中重复元素不插入),emplace 在容器内原地构造元素,避免临时对象拷贝。

💡 比喻:insert 像把一张写好的卡片放入排序盒;emplace 像直接在盒子里现场制作卡片,少了一步从外面拿进来的过程。

🔧 声明:std::pair<iterator, bool> insert(const T& value);
iterator emplace(Args&&... args);

↩️ 返回值:insert 返回 pair<迭代器, 是否成功>;emplace 返回指向插入元素的迭代器。

C++
#include <iostream>
#include <set>
#include <string>
using namespace std;

int main() {
    set<pair<int, string>> s;
    auto r1 = s.insert({1, "apple"});
    auto r2 = s.emplace(2, "banana");
    auto r3 = s.insert({1, "apple"});  // 重复

    cout << "第一次插入: " << (r1.second ? "成功" : "失败") << endl;
    cout << "第二次插入: " << (r2.second ? "成功" : "失败") << endl;
    cout << "第三次插入: " << (r3.second ? "成功" : "失败") << endl;

    for (const auto& p : s) {
        cout << p.first << ": " << p.second << endl;
    }
    return 0;
}

find / count / lower_bound / upper_bound / equal_range

🎯 功能:find 查找元素;count 统计出现次数;lower_bound/upper_bound 返回不小于/大于 key 的第一个位置;equal_range 返回 key 的范围。

💡 比喻:find 像按姓名找人;count 像统计同名人数;lower_bound/upper_bound 像查字典时翻到「该词可能出现的第一页」和「该词结束后的下一页」;equal_range 则是直接标出该词在字典中的起止页。

🔧 声明:iterator find(const T& key);
size_type count(const T& key) const;
iterator lower_bound(const T& key);
iterator upper_bound(const T& key);
std::pair<iterator, iterator> equal_range(const T& key);

↩️ 返回值:find 返回迭代器或 end;count 返回出现次数;lower_bound/upper_bound 返回对应迭代器;equal_range 返回迭代器对。

C++
#include <iostream>
#include <set>
using namespace std;

int main() {
    multiset<int> ms = {1, 3, 3, 3, 5, 7};

    auto it = ms.find(3);
    if (it != ms.end()) cout << "找到 3" << endl;

    cout << "3 出现次数: " << ms.count(3) << endl;

    auto lo = ms.lower_bound(3);
    auto hi = ms.upper_bound(3);
    cout << "3 的范围: [" << *lo << ", " << *hi << ")" << endl;

    auto range = ms.equal_range(3);
    for (auto it = range.first; it != range.second; ++it) {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}

erase —— 删除元素

🎯 功能:按值、迭代器或迭代器区间删除元素;set 删除值返回删除数量,multiset 可删除多个相同元素。

💡 比喻:erase 像从花名册上划掉指定学生;按值划掉会划掉所有同名学生(multiset),按迭代器则只划掉指定那一行。

🔧 声明:size_type erase(const T& key);
iterator erase(iterator position);
iterator erase(iterator first, iterator last);

↩️ 返回值:按值删除返回删除数量;按迭代器删除返回被删元素之后的迭代器。

C++
#include <iostream>
#include <set>
using namespace std;

int main() {
    multiset<int> ms = {1, 2, 3, 3, 3, 4, 5};

    size_t n = ms.erase(3);
    cout << "删除 3 的个数: " << n << endl;

    auto it = ms.find(4);
    if (it != ms.end()) ms.erase(it);

    cout << "剩余: ";
    for (int x : ms) cout << x << " ";
    cout << endl;
    return 0;
}

👉下一节:2.10 map / multimap →

▼2.10 <map> / <multimap> —— 有序字典C++

是什么

📚 std::map 和 std::multimap 是存储键值对的有序关联容器,按键自动排序;map 中键唯一,multimap 中同一键可对应多个值。

💡 生活比喻:map 像图书馆的索引卡片柜,一个书名只有一张卡片;multimap 则像分类目录,一个主题下可以挂多张推荐书单。

⚠️ 注意事项:operator[] 在键不存在时会自动插入默认值;查找优先用 find 或 C++20 的 contains;multimap 遍历同一键应使用 equal_range;键不可修改。

map 构造函数

🎯 功能:可构造空 map、拷贝构造、用迭代器区间或初始化列表构造;multimap 接口相同,仅允许重复键。

💡 比喻:像建立一本空白字典,或直接复印一本已有字典,也可以用一叠词条批量生成。

🔧 声明:std::map<Key, T> m;
std::map<Key, T> m(first, last);
std::map<Key, T> m(init_list);
std::multimap<Key, T> mm;

↩️ 返回值:构造出的 map/multimap 对象。

C++
#include <iostream>
#include <map>
#include <vector>
using namespace std;

int main() {
    map<string, int> m1;
    m1["apple"] = 3;
    m1["banana"] = 5;

    vector<pair<string, int>> v = {{"cat", 2}, {"dog", 4}};
    map<string, int> m2(v.begin(), v.end());

    multimap<string, int> mm = {{"x", 1}, {"x", 2}, {"y", 3}};

    cout << "m1[apple] = " << m1["apple"] << endl;
    cout << "mm 中 x 的数量: " << mm.count("x") << endl;
    return 0;
}

operator[] / insert / emplace

🎯 功能:operator[] 按键访问或插入;insert 插入键值对;emplace 原地构造 pair,避免临时对象。

💡 比喻:operator[] 像查字典时找不到就自动创建空白词条;insert 是手动塞入一张新卡片;emplace 则是现场把词条写好并插入。

🔧 声明:T& operator[](const Key& key);
std::pair<iterator, bool> insert(const pair<Key, T>& value);
iterator emplace(Args&&... args);

↩️ 返回值:operator[] 返回值引用;insert 返回是否成功;emplace 返回指向元素的迭代器。

C++
#include <iostream>
#include <map>
using namespace std;

int main() {
    map<string, int> score;
    score["Alice"] = 90;
    score["Bob"] = 85;

    auto r = score.insert({"Alice", 95});  // 已存在,不插入
    cout << "insert Alice: " << (r.second ? "成功" : "失败") << endl;

    score.emplace("Carol", 88);

    for (const auto& p : score) {
        cout << p.first << ": " << p.second << endl;
    }
    return 0;
}

find / count / lower_bound / upper_bound

🎯 功能:find 按键查找;count 统计键数量;lower_bound/upper_bound 返回键范围边界。

💡 比喻:find 是按书名找卡片;count 是统计某作者名下有几本书;lower_bound/upper_bound 是按拼音首字母翻到该词条前后页。

🔧 声明:iterator find(const Key& key);
size_type count(const Key& key) const;
iterator lower_bound(const Key& key);
iterator upper_bound(const Key& key);

↩️ 返回值:find 返回迭代器或 end;count 返回键出现次数;lower_bound/upper_bound 返回对应边界迭代器。

C++
#include <iostream>
#include <map>
using namespace std;

int main() {
    map<string, int> m = {{"apple", 1}, {"banana", 2}, {"cherry", 3}};

    auto it = m.find("banana");
    if (it != m.end()) {
        cout << "找到 banana: " << it->second << endl;
    }

    cout << "banana 数量: " << m.count("banana") << endl;

    auto lo = m.lower_bound("banana");
    auto hi = m.upper_bound("banana");
    cout << "lower: " << lo->first << ", upper: " << hi->first << endl;
    return 0;
}

erase —— 删除键值对

🎯 功能:按键、迭代器或区间删除元素;map 按值删除返回 0 或 1,multimap 可删除多个。

💡 比喻:像从字典里撕掉某张卡片;按关键词撕会撕掉所有同名卡片(multimap),按页码撕只撕掉指定一页。

🔧 声明:size_type erase(const Key& key);
iterator erase(iterator position);
iterator erase(iterator first, iterator last);

↩️ 返回值:按键删除返回删除数量;按迭代器删除返回下一个迭代器。

C++
#include <iostream>
#include <map>
using namespace std;

int main() {
    map<string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}};

    m.erase("b");

    auto it = m.find("c");
    if (it != m.end()) m.erase(it);

    cout << "剩余: ";
    for (const auto& p : m) cout << p.first << "=" << p.second << " ";
    cout << endl;
    return 0;
}

👉下一节:2.11 unordered_set / unordered_multiset →

▼2.11 <unordered_set> / <unordered_multiset> —— 无序集合(C++11)C++

是什么

📚 std::unordered_set 和 std::unordered_multiset 是 C++11 引入的基于哈希表实现的无序关联容器;unordered_set 键唯一,unordered_multiset 允许重复,元素不保证任何顺序。

💡 生活比喻:unordered_set 像超市的自助储物柜——输入取件码(hash)直接定位柜子,柜子之间没有先后次序;unordered_multiset 允许同一个取件码对应多个包裹。

⚠️ 注意事项:平均 O(1) 查找,最坏 O(n);迭代顺序不稳定,不能依赖顺序;自定义类型需提供 hash 函数和判等运算符;负载因子过高时性能下降,可通过 rehash/reserve 预分配。

unordered_set 构造函数

🎯 功能:可构造空哈希集合、拷贝构造、用迭代器区间或初始化列表构造;unordered_multiset 接口相同。

💡 比喻:像开办一个没有固定编号规则的临时储物区,可以空着,也可以直接把一堆包裹倒进来。

🔧 声明:std::unordered_set<T> s;
std::unordered_set<T> s(first, last);
std::unordered_set<T> s(init_list);
std::unordered_set<T, Hash, Pred> s;

↩️ 返回值:构造出的 unordered_set/multiset 对象。

C++
#include <iostream>
#include <unordered_set>
#include <vector>
using namespace std;

int main() {
    unordered_set<int> s1;
    s1.insert(3);
    s1.insert(1);
    s1.insert(2);

    vector<int> v = {5, 2, 5, 8};
    unordered_set<int> s2(v.begin(), v.end());

    unordered_multiset<int> ms = {4, 4, 4, 1};

    cout << "s1 size = " << s1.size() << ", ms size = " << ms.size() << endl;
    return 0;
}

insert / emplace / find / count / erase

🎯 功能:insert/emplace 插入元素;find 按值查找;count 统计出现次数;erase 按值或迭代器删除。

💡 比喻:insert/emplace 是存包裹;find 是凭取件码找包裹;count 是查该取件码下有几个包裹;erase 是取走或清掉包裹。

🔧 声明:std::pair<iterator, bool> insert(const T& value);
iterator emplace(Args&&... args);
iterator find(const T& key);
size_type count(const T& key) const;
size_type erase(const T& key);

↩️ 返回值:insert 返回 pair<迭代器, 是否成功>;find 返回迭代器或 end;count 返回数量;erase 返回删除数量。

C++
#include <iostream>
#include <unordered_set>
using namespace std;

int main() {
    unordered_multiset<int> ms;
    ms.insert(10);
    ms.insert(20);
    ms.emplace(10);

    cout << "count(10) = " << ms.count(10) << endl;

    auto it = ms.find(20);
    if (it != ms.end()) cout << "找到 20" << endl;

    ms.erase(10);
    cout << "erase 后 count(10) = " << ms.count(10) << endl;
    return 0;
}

rehash / reserve / bucket_count

🎯 功能:rehash 强制重新分配桶数量;reserve 预分配足够桶以容纳指定元素数;bucket_count 返回当前桶数。

💡 比喻:rehash 像快递爆仓后重新整理货架数量;reserve 像提前按预计包裹量准备好货架,避免临时搬货;bucket_count 则是当前实际货架数。

🔧 声明:void rehash(size_type n);
void reserve(size_type n);
size_type bucket_count() const;

↩️ 返回值:rehash/reserve 无返回值;bucket_count 返回当前桶数量。

C++
#include <iostream>
#include <unordered_set>
using namespace std;

int main() {
    unordered_set<int> s;
    s.reserve(1000);

    for (int i = 0; i < 100; i++) s.insert(i);

    cout << "bucket_count = " << s.bucket_count() << endl;
    cout << "load_factor  = " << s.load_factor() << endl;

    s.rehash(200);
    cout << "rehash 后 bucket_count = " << s.bucket_count() << endl;
    return 0;
}

👉下一节:2.12 unordered_map / unordered_multimap →

▼2.12 <unordered_map> / <unordered_multimap> —— 无序字典(C++11)C++

是什么

📚 std::unordered_map 和 std::unordered_multimap 是 C++11 引入的基于哈希表实现的无序键值对容器;unordered_map 键唯一,unordered_multimap 允许同一键对应多个值,元素不保证顺序。

💡 生活比喻:unordered_map 像快递驿站按手机号取件——通过 hash 直接定位货架,不必排队;unordered_multimap 则允许同一个手机号下挂多个包裹。

⚠️ 注意事项:operator[] 不存在键时会插入默认值;迭代顺序不稳定;自定义键类型需提供 std::hash 特化和 operator==;大量数据前用 reserve 预分配,可减少 rehash 开销。

unordered_map 构造函数

🎯 功能:可构造空哈希表、拷贝构造、用迭代器区间或初始化列表构造;unordered_multimap 接口相同。

💡 比喻:像新建一家快递驿站,可以空着开业,也可以直接把一批已分拣的包裹上架。

🔧 声明:std::unordered_map<Key, T> m;
std::unordered_map<Key, T> m(first, last);
std::unordered_map<Key, T> m(init_list);
std::unordered_map<Key, T, Hash, Pred> m;

↩️ 返回值:构造出的 unordered_map/multimap 对象。

C++
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

int main() {
    unordered_map<string, int> m1;
    m1["Alice"] = 90;

    vector<pair<string, int>> v = {{"Bob", 85}, {"Carol", 88}};
    unordered_map<string, int> m2(v.begin(), v.end());

    unordered_multimap<string, int> mm = {{"team", 1}, {"team", 2}};

    cout << "m1 size = " << m1.size() << ", mm size = " << mm.size() << endl;
    return 0;
}

operator[] / insert / emplace / find / count / erase

🎯 功能:operator[] 访问或插入;insert/emplace 插入键值对;find/count/erase 完成查找、计数与删除。

💡 比喻:operator[] 像取件时手机号不存在就自动开个新柜子;insert/emplace 是主动寄存;find/count/erase 分别是查找、统计和取走包裹。

🔧 声明:T& operator[](const Key& key);
std::pair<iterator, bool> insert(const pair<Key, T>& value);
iterator emplace(Args&&... args);
iterator find(const Key& key);
size_type count(const Key& key) const;
size_type erase(const Key& key);

↩️ 返回值:operator[] 返回值引用;insert 返回是否成功;find 返回迭代器或 end;count 返回数量;erase 返回删除数量。

C++
#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
    unordered_map<string, int> score;
    score["Alice"] = 90;
    score.insert({"Bob", 85});
    score.emplace("Carol", 88);

    auto it = score.find("Bob");
    if (it != score.end()) {
        cout << "Bob: " << it->second << endl;
    }

    cout << "count(Alice) = " << score.count("Alice") << endl;

    score.erase("Alice");
    cout << "erase 后 count(Alice) = " << score.count("Alice") << endl;
    return 0;
}

rehash / reserve

🎯 功能:rehash 调整桶数量;reserve 按预计元素数预分配桶,减少重新哈希带来的性能开销。

💡 比喻:reserve 像根据双 11 预估单量提前扩建货架;rehash 则是在发现实际单量远超预期后临时重新规划货架数量。

🔧 声明:void rehash(size_type n);
void reserve(size_type n);

↩️ 返回值:均无返回值。

C++
#include <iostream>
#include <unordered_map>
using namespace std;

int main() {
    unordered_map<int, int> m;
    m.reserve(10000);

    for (int i = 0; i < 1000; i++) {
        m[i] = i * i;
    }

    cout << "bucket_count = " << m.bucket_count() << endl;
    cout << "load_factor  = " << m.load_factor() << endl;

    m.rehash(500);
    cout << "rehash 后 bucket_count = " << m.bucket_count() << endl;
    return 0;
}

👉下一节:3.1 算法 →

▼3.1 <algorithm> —— 通用算法C++

是什么

📚 <algorithm> 是 STL 的算法库,提供排序、查找、遍历、二分搜索、复制移动、旋转反转、最值等大量非成员函数模板,常与容器和迭代器搭配使用。

💡 生活比喻:<algorithm> 像图书馆的万能整理工具箱——里面既有按字母排序书架的 sort,也有按书名找书的 find,还有统计某本书出现次数的 count。

⚠️ 注意事项:算法通常不检查迭代器是否有效;排序/二分相关算法要求范围满足前提(如已排序);优先使用标准算法而非手写循环,可提高可读性和性能。

sort / stable_sort —— 排序

🎯 功能:sort 对区间 [first, last) 进行快速排序(不保证稳定);stable_sort 保持相等元素的原始相对顺序。

💡 比喻:sort 像快速整理扑克牌,只保证大小顺序;stable_sort 像整理档案,同名文件原来谁在前,整理后仍然谁在前。

🔧 声明:void sort(RandomIt first, RandomIt last);
void sort(RandomIt first, RandomIt last, Compare comp);
void stable_sort(RandomIt first, RandomIt last);

↩️ 返回值:均无返回值,区间元素变为有序。

C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
    sort(v.begin(), v.end());
    cout << "sort: ";
    for (int x : v) cout << x << " ";
    cout << endl;

    vector<pair<int, string>> ps = {{2, "b"}, {1, "a"}, {2, "a"}};
    stable_sort(ps.begin(), ps.end());
    cout << "stable_sort: ";
    for (auto& p : ps) cout << "(" << p.first << "," << p.second << ") ";
    cout << endl;

    return 0;
}

find / find_if / count / count_if —— 查找与计数

🎯 功能:find 找第一个等于 value 的元素;find_if 找第一个满足谓词的元素;count 统计出现次数;count_if 统计满足谓词的次数。

💡 比喻:find 像按名字找人;find_if 像按条件找人(比如「第一个戴眼镜的」);count 像点名统计人数;count_if 像统计符合某个条件的人数。

🔧 声明:Iter find(Iter first, Iter last, const T& value);
Iter find_if(Iter first, Iter last, UnaryPred pred);
Diff count(Iter first, Iter last, const T& value);
Diff count_if(Iter first, Iter last, UnaryPred pred);

↩️ 返回值:find/find_if 返回指向元素的迭代器,未找到返回 last;count/count_if 返回计数值。

C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5, 3, 3};

    auto it = find(v.begin(), v.end(), 3);
    if (it != v.end()) cout << "find 3 在索引 " << (it - v.begin()) << endl;

    auto it2 = find_if(v.begin(), v.end(), [](int x) { return x > 3; });
    if (it2 != v.end()) cout << "第一个大于 3 的是 " << *it2 << endl;

    cout << "3 出现次数: " << count(v.begin(), v.end(), 3) << endl;
    cout << "大于 3 的个数: " << count_if(v.begin(), v.end(), [](int x) { return x > 3; }) << endl;

    return 0;
}

for_each —— 遍历执行

🎯 功能:对区间 [first, last) 的每个元素调用一次函数或函数对象,常用于打印、累加副作用等操作。

💡 比喻:for_each 像逐张发传单——对队伍里的每个人(每个元素)都做同样的动作,不需要关心队伍有多长。

🔧 声明:UnaryFunc for_each(Iter first, Iter last, UnaryFunc f);

↩️ 返回值:返回传入的函数对象 f(可保留状态)。

C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};

    cout << "for_each 打印: ";
    for_each(v.begin(), v.end(), [](int x) { cout << x << " "; });
    cout << endl;

    int sum = 0;
    for_each(v.begin(), v.end(), [&sum](int x) { sum += x; });
    cout << "sum = " << sum << endl;

    return 0;
}

binary_search / lower_bound / upper_bound —— 二分搜索

🎯 功能:binary_search 判断元素是否存在于已排序区间;lower_bound 返回第一个不小于 value 的位置;upper_bound 返回第一个大于 value 的位置。

💡 比喻:binary_search 像查字典确认某个词是否存在;lower_bound 像翻到该词可能出现的第一页;upper_bound 像翻到该词结束后的下一页。

🔧 声明:bool binary_search(Iter first, Iter last, const T& value);
Iter lower_bound(Iter first, Iter last, const T& value);
Iter upper_bound(Iter first, Iter last, const T& value);

↩️ 返回值:binary_search 返回 true/false;lower_bound/upper_bound 返回迭代器。

C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 3, 3, 3, 5, 7, 9};

    bool ok = binary_search(v.begin(), v.end(), 5);
    cout << "5 是否存在: " << (ok ? "是" : "否") << endl;

    auto lo = lower_bound(v.begin(), v.end(), 3);
    auto hi = upper_bound(v.begin(), v.end(), 3);
    cout << "3 的范围: [" << (lo - v.begin()) << ", " << (hi - v.begin()) << ")" << endl;

    return 0;
}

copy / move / reverse / rotate —— 复制、移动、反转与旋转

🎯 功能:copy 复制元素到目标区间;move 移动元素(调用移动语义);reverse 反转区间;rotate 把元素循环左移到指定位置。

💡 比喻:copy 像复印文件;move 像把文件从 A 柜搬到 B 柜;reverse 像把整排书倒过来放;rotate 像把书架上的书整体向左轮转若干本。

🔧 声明:OutIt copy(Iter first, Iter last, OutIt d_first);
OutIt move(Iter first, Iter last, OutIt d_first);
void reverse(Iter first, Iter last);
Iter rotate(Iter first, Iter n_first, Iter last);

↩️ 返回值:copy/move 返回目标区间尾后迭代器;reverse 无返回值;rotate 返回原 first 元素的新位置。

C++
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
using namespace std;

int main() {
    vector<int> a = {1, 2, 3, 4, 5};
    vector<int> b(a.size());
    copy(a.begin(), a.end(), b.begin());

    vector<string> s = {"a", "b", "c", "d", "e"};
    vector<string> t;
    move(s.begin(), s.end(), back_inserter(t));

    reverse(a.begin(), a.end());
    rotate(b.begin(), b.begin() + 2, b.end());

    cout << "reverse a: ";
    for (int x : a) cout << x << " ";
    cout << "\nrotate b: ";
    for (int x : b) cout << x << " ";
    cout << "\nmove t: ";
    for (const auto& x : t) cout << x << " ";
    cout << endl;

    return 0;
}

max / min / swap / clamp —— 最值、交换与限幅

🎯 功能:max/min 返回较大/较小值;swap 交换两个对象;clamp 把值限制在 [lo, hi] 范围内(C++17)。

💡 比喻:max/min 像比较两本书的厚薄;swap 像交换两人手里的杯子;clamp 像给数值装上护栏——太低就抬到最低线,太高就压到最高线。

🔧 声明:const T& max(const T& a, const T& b);
const T& min(const T& a, const T& b);
void swap(T& a, T& b);
const T& clamp(const T& v, const T& lo, const T& hi);

↩️ 返回值:max/min/clamp 返回对应值的 const 引用;swap 无返回值。

C++
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;

int main() {
    int a = 10, b = 20;
    cout << "max = " << max(a, b) << ", min = " << min(a, b) << endl;

    swap(a, b);
    cout << "swap 后 a=" << a << ", b=" << b << endl;

    cout << "clamp(5, 10, 20) = " << clamp(5, 10, 20) << endl;
    cout << "clamp(15, 10, 20) = " << clamp(15, 10, 20) << endl;
    cout << "clamp(25, 10, 20) = " << clamp(25, 10, 20) << endl;

    return 0;
}

👉下一节:3.2 numeric →

▼3.2 <numeric> —— 数值算法C++

是什么

📚 <numeric> 提供数值计算相关的通用算法,包括求和、内积、前缀和、差分、序列生成以及最大公约数/最小公倍数等。

💡 生活比喻:<numeric> 像一台多功能计算器——accumulate 做连加,inner_product 做点乘,iota 自动填充等差数列,gcd/lcm 帮你约分通分。

⚠️ 注意事项:accumulate 的初始值类型决定结果类型,建议显式指定;gcd/lcm 要求 C++17 并包含 <numeric>;大部分算法都支持自定义二元操作。

accumulate —— 累积求和

🎯 功能:计算区间 [first, last) 内元素与初始值的累积结果,默认做加法,也支持自定义二元操作。

💡 比喻:accumulate 像把一摞账单逐项累加;你可以从 0 开始加,也可以从已有余额开始,甚至自定义「乘法」规则。

🔧 声明:T accumulate(Iter first, Iter last, T init);
T accumulate(Iter first, Iter last, T init, BinaryOp op);

↩️ 返回值:返回累积后的结果值。

C++
#include <numeric>
#include <iostream>
#include <vector>
#include <functional>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};

    int sum = accumulate(v.begin(), v.end(), 0);
    cout << "sum = " << sum << endl;

    int prod = accumulate(v.begin(), v.end(), 1, multiplies<int>());
    cout << "prod = " << prod << endl;

    double avg = accumulate(v.begin(), v.end(), 0.0) / v.size();
    cout << "avg = " << avg << endl;

    return 0;
}

inner_product —— 内积

🎯 功能:计算两个等长序列对应元素乘积之和,也支持自定义加法和乘法操作。

💡 比喻:inner_product 像计算购物车里每件商品「数量 × 单价」的总价——把两个清单逐项相乘再相加。

🔧 声明:T inner_product(Iter1 first1, Iter1 last1, Iter2 first2, T init);
T inner_product(..., T init, BinaryOp1 add, BinaryOp2 mul);

↩️ 返回值:返回内积结果值。

C++
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> price = {10, 20, 30};
    vector<int> count = {2, 3, 1};

    int total = inner_product(price.begin(), price.end(), count.begin(), 0);
    cout << "总价 = " << total << endl;

    return 0;
}

partial_sum / adjacent_difference —— 前缀和与相邻差分

🎯 功能:partial_sum 生成前缀和序列;adjacent_difference 计算相邻元素差分(后一项减前一项)。

💡 比喻:partial_sum 像马拉松的累计里程牌——每到一个点就显示从起点到现在的总距离;adjacent_difference 像每段路程的距离牌——显示这一段跑了多少公里。

🔧 声明:OutIt partial_sum(Iter first, Iter last, OutIt d_first);
OutIt adjacent_difference(Iter first, Iter last, OutIt d_first);

↩️ 返回值:均返回目标区间尾后迭代器。

C++
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};

    vector<int> prefix(v.size());
    partial_sum(v.begin(), v.end(), prefix.begin());
    cout << "前缀和: ";
    for (int x : prefix) cout << x << " ";
    cout << endl;

    vector<int> diff(v.size());
    adjacent_difference(v.begin(), v.end(), diff.begin());
    cout << "相邻差分: ";
    for (int x : diff) cout << x << " ";
    cout << endl;

    return 0;
}

iota —— 填充递增序列

🎯 功能:对区间 [first, last) 依次填充从 value 开始的连续递增序列(value, value+1, ...)。

💡 比喻:iota 像给一群座位依次贴号——从指定号码开始,每张椅子比前一张大 1。

🔧 声明:void iota(Iter first, Iter last, T value);

↩️ 返回值:无返回值,区间被填充为递增序列。

C++
#include <numeric>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> v(10);
    iota(v.begin(), v.end(), 1);

    cout << "iota 结果: ";
    for (int x : v) cout << x << " ";
    cout << endl;

    return 0;
}

gcd / lcm —— 最大公约数与最小公倍数(C++17)

🎯 功能:gcd 返回两个整数的最大公约数;lcm 返回最小公倍数;要求两个参数均为整数类型。

💡 比喻:gcd 像把两根不同长度的绳子按最大等长段剪开;lcm 像找一块能刚好被两种步长铺满的最小地砖。

🔧 声明:constexpr T gcd(T m, T n);
constexpr T lcm(T m, T n);

↩️ 返回值:返回最大公约数或最小公倍数。

C++
#include <numeric>
#include <iostream>
using namespace std;

int main() {
    int a = 12, b = 18;

    cout << "gcd(" << a << "," << b << ") = " << gcd(a, b) << endl;
    cout << "lcm(" << a << "," << b << ") = " << lcm(a, b) << endl;

    return 0;
}

👉下一节:4.1 string →

▼4.1 <string> —— 字符串(最常用!)C++

是什么

📚 std::string 是 C++ 中最常用的字符串类,封装了动态字符数组,自动管理内存,提供丰富的构造、赋值、追加、插入、查找、比较和子串等操作。

💡 生活比喻:std::string 像一根可以自动伸缩、随意拼接、还能按关键字搜索内容的智能绳子——比 C 风格字符数组安全、方便得多。

⚠️ 注意事项:string 的下标 [] 不检查越界,at() 会抛异常;与 C API 交互时用 c_str() 获取末尾带 \0 的字符指针;比较大小按字典序;大量修改时注意重新分配带来的迭代器失效。

string 构造函数

🎯 功能:string 支持默认构造、C 字符串构造、拷贝构造、子串构造、重复字符构造、迭代器范围构造等多种初始化方式。

💡 比喻:string 的构造函数像不同规格的剪刀和模板:可以从空白开始剪、从已有纸条复制、只截取中间一段,也可以重复印同一个字符。

🔧 声明:string();
string(const string& str);
string(const string& str, size_t pos, size_t len = npos);
string(const char* s);
string(const char* s, size_t n);
string(size_t n, char c);
template<class It> string(It first, It last);

↩️ 返回值:返回初始化好的 string 对象。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1;                    /* 空串 */
    string s2("hello");           /* C 字符串 */
    string s3(s2);                /* 拷贝 */
    string s4(s2, 1, 3);          /* 子串 "ell" */
    string s5(5, 'x');            /* "xxxxx" */
    string s6({'a', 'b', 'c'});   /* 初始化列表 */

    cout << "s1=[" << s1 << "] s2=[" << s2 << "] s3=[" << s3 << "]\n";
    cout << "s4=[" << s4 << "] s5=[" << s5 << "] s6=[" << s6 << "]\n";

    return 0;
}

operator= / assign —— 赋值

🎯 功能:operator= 支持字符串、C 字符串、字符的赋值;assign 提供更多赋值形式,如子串、重复字符、迭代器范围等。

💡 比喻:operator= 像把新纸条装进字符串盒子并清空旧纸条;assign 则是更灵活的装载方式,可以只装一段、装多个副本或从别的容器搬运。

🔧 声明:string& operator=(const string& str);
string& operator=(const char* s);
string& operator=(char c);
string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);
string& assign(const char* s);
string& assign(size_t n, char c);

↩️ 返回值:均返回 *this。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;

    s = "hello";
    cout << "operator=: " << s << endl;

    s.assign("world");
    cout << "assign cstr: " << s << endl;

    s.assign("abcdef", 2, 3);   /* "cde" */
    cout << "assign substr: " << s << endl;

    s.assign(4, '=');
    cout << "assign repeat: " << s << endl;

    return 0;
}

operator+= / append / push_back / pop_back —— 追加与尾部操作

🎯 功能:operator+= 和 append 在尾部追加字符串、C 字符串或子串;push_back 追加单个字符;pop_back 移除最后一个字符。

💡 比喻:operator+=/append 像把新纸条粘在绳子末尾;push_back 像一颗一颗把珠子穿到末尾;pop_back 则像从末尾摘掉最后一颗珠子。

🔧 声明:string& operator+=(const string& str);
string& operator+=(const char* s);
string& operator+=(char c);
string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);
void push_back(char c);
void pop_back();

↩️ 返回值:operator+=/append 返回 *this;push_back/pop_back 无返回值。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "Hi";

    s += " ";
    s += "C++";
    cout << "after +=: " << s << endl;

    s.append("!");
    s.append(" Welcome", 0, 8);
    cout << "after append: " << s << endl;

    s.push_back('.');
    cout << "after push_back: " << s << endl;

    s.pop_back();
    cout << "after pop_back: " << s << endl;

    return 0;
}

insert / erase / replace / substr —— 插入、删除、替换与取子串

🎯 功能:insert 在指定位置插入内容;erase 删除指定区间;replace 替换子串;substr 返回子串副本。

💡 比喻:insert 像在绳子的指定位置打个结并塞入新线;erase 像剪掉一段;replace 像把一段拆下来换成新的;substr 像复印其中一段而不影响原绳。

🔧 声明:string& insert(size_t pos, const string& str);
string& erase(size_t pos = 0, size_t len = npos);
string& replace(size_t pos, size_t len, const string& str);
string substr(size_t pos = 0, size_t len = npos) const;

↩️ 返回值:insert/erase/replace 返回 *this;substr 返回新的 string 副本。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "Hello World";

    s.insert(5, ",");
    cout << "insert: " << s << endl;

    s.erase(5, 1);
    cout << "erase: " << s << endl;

    s.replace(6, 5, "C++");
    cout << "replace: " << s << endl;

    string sub = s.substr(6, 3);
    cout << "substr: " << sub << endl;

    return 0;
}

find / rfind / find_first_of / find_last_of —— 查找

🎯 功能:find 从前向后查找;rfind 从后向前查找;find_first_of 查找任意一个字符首次出现位置;find_last_of 查找任意一个字符最后出现位置。

💡 比喻:find 像从绳子左端开始找图案;rfind 像从右端开始找;find_first_of 像检查「这些字符中有没有任意一个出现」并返回第一个命中;find_last_of 则返回最后一个命中。

🔧 声明:size_t find(const string& str, size_t pos = 0) const;
size_t rfind(const string& str, size_t pos = npos) const;
size_t find_first_of(const string& str, size_t pos = 0) const;
size_t find_last_of(const string& str, size_t pos = npos) const;

↩️ 返回值:返回找到的位置索引;未找到返回 string::npos。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "abracadabra";

    cout << "find 'a': " << s.find('a') << endl;
    cout << "find 'a' from 1: " << s.find('a', 1) << endl;
    cout << "rfind 'a': " << s.rfind('a') << endl;
    cout << "first_of 'xyz': " << s.find_first_of("xyz") << endl;
    cout << "last_of 'abc': " << s.find_last_of("abc") << endl;

    if (s.find('z') == string::npos) {
        cout << "'z' 未找到\n";
    }

    return 0;
}

c_str / data / length / size / empty / clear —— 常用属性与访问

🎯 功能:c_str/data 返回底层字符数组;length/size 返回字符数;empty 判断是否为空;clear 清空字符串。

💡 比喻:c_str/data 像把 string 的内容摊平成普通纸条展示;length/size 是量绳子的长度;empty 检查是不是空绳;clear 则一把清空所有内容。

🔧 声明:const char* c_str() const;
const char* data() const;
size_t length() const;
size_t size() const;
bool empty() const;
void clear();

↩️ 返回值:c_str/data 返回 const char*;length/size 返回字符数;empty 返回是否为空;clear 无返回值。

C++
#include <iostream>
#include <string>
#include <cstring>
using namespace std;

int main() {
    string s = "Hello";

    cout << "size=" << s.size() << " length=" << s.length() << endl;
    cout << "empty=" << s.empty() << endl;
    cout << "c_str=" << s.c_str() << endl;
    cout << "strlen(c_str)=" << strlen(s.c_str()) << endl;

    s.clear();
    cout << "after clear: size=" << s.size() << " empty=" << s.empty() << endl;

    return 0;
}

compare / swap / resize —— 比较、交换与调整大小

🎯 功能:compare 按字典序比较字符串;swap 交换两个 string 内容;resize 改变字符串长度,可指定填充字符。

💡 比喻:compare 像把两根绳子并排比长短和字母顺序;swap 像交换两人手中的绳子;resize 像把绳子截短或补长,补长时可以用指定字符填充。

🔧 声明:int compare(const string& str) const;
void swap(string& str);
void resize(size_t n);
void resize(size_t n, char c);

↩️ 返回值:compare 返回负数/零/正数;swap/resize 无返回值。

C++
#include <iostream>
#include <string>
using namespace std;

int main() {
    string a = "apple";
    string b = "banana";

    cout << "compare apple vs banana: " << a.compare(b) << endl;

    a.swap(b);
    cout << "after swap: a=" << a << " b=" << b << endl;

    string s = "hi";
    s.resize(5, 'x');
    cout << "resize up: " << s << endl;

    s.resize(2);
    cout << "resize down: " << s << endl;

    return 0;
}

getline 读取 string

🎯 功能:std::getline 从输入流读取一整行(含空格),直到遇到分隔符(默认换行符),并存入 string。

💡 比喻:cin >> 像按单词取货,遇到空格就停;getline 则像用一把尺子从传送带上整条拉取,直到遇到换行符才把一整行收进 string。

🔧 声明:istream& getline(istream& is, string& str);
istream& getline(istream& is, string& str, char delim);

↩️ 返回值:返回输入流 is,可用于判断是否读取成功。

C++
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

int main() {
    string data = "Hello C++ world\nThis is a test";
    istringstream iss(data);

    string line;
    while (getline(iss, line)) {
        cout << "读到: [" << line << "]\n";
    }

    return 0;
}

👉下一节:4.2 string_view →

▼4.2 <string_view> —— 字符串视图(C++17)C++

是什么

📚 std::string_view(C++17)是一个只读、非 owning 的字符串视图,可以指向 string、C 字符串或其他连续字符序列,避免拷贝开销。

💡 生活比喻:string 像把整本书复印一份拿在手里;string_view 则像只拿一张写着页码和范围的便签——不拥有原书,但能随时查看指定内容,又快又省空间。

⚠️ 注意事项:string_view 不管理内存,其指向的字符串必须保证生命周期覆盖视图使用期;不要返回指向局部 string 的 string_view;修改原字符串后视图内容也会变化;通常按值传递 string_view 即可。

string_view 构造函数

🎯 功能:string_view 可从 C 字符串、string、字符数组或指针范围构造,不发生内存分配。

💡 比喻:string_view 的构造像在不同书本上贴便利贴:可以直接贴在 C 字符串上、贴在 string 上,也可以只覆盖某一段范围。

🔧 声明:constexpr string_view() noexcept;
constexpr string_view(const char* s);
constexpr string_view(const char* s, size_t len);
constexpr string_view(const string& str);

↩️ 返回值:返回指向原数据的 string_view 对象。

C++
#include <iostream>
#include <string>
#include <string_view>
using namespace std;

int main() {
    string s = "Hello World";
    const char *cstr = "C string";

    string_view v1;               /* 空视图 */
    string_view v2(cstr);         /* C 字符串 */
    string_view v3(s);            /* 从 string */
    string_view v4(cstr + 1, 3);  /* 子范围 " st" */
    string_view v5("literal");    /* 字符串字面量 */

    cout << "v2=" << v2 << " v3=" << v3 << " v4=" << v4 << " v5=" << v5 << endl;

    return 0;
}

data / size / length / empty —— 基础属性

🎯 功能:data 返回指向字符数据的指针;size/length 返回视图长度;empty 判断是否为空。

💡 比喻:data 像便签上写的「内容从第几页开始」;size/length 是便签覆盖的字数;empty 表示便贴上有没有字。

🔧 声明:constexpr const char* data() const noexcept;
constexpr size_t size() const noexcept;
constexpr size_t length() const noexcept;
constexpr bool empty() const noexcept;

↩️ 返回值:data 返回 const char*;size/length 返回长度;empty 返回是否为空。

C++
#include <iostream>
#include <string_view>
using namespace std;

int main() {
    string_view sv = "Hello";

    cout << "sv=" << sv << endl;
    cout << "size=" << sv.size() << " length=" << sv.length() << endl;
    cout << "empty=" << sv.empty() << endl;
    cout << "data 指向: " << sv.data() << endl;

    return 0;
}

operator[] / at / front / back —— 字符访问

🎯 功能:operator[] 和下标访问字符;at 进行边界检查;front/back 返回首尾字符。

💡 比喻:operator[] 像直接翻到便签的第几个字;at 像请一位管理员帮你确认页码是否合法;front/back 则是直接看便签的第一个和最后一个字。

🔧 声明:constexpr const char& operator[](size_t pos) const;
constexpr const char& at(size_t pos) const;
constexpr const char& front() const;
constexpr const char& back() const;

↩️ 返回值:返回对应位置字符的 const 引用。

C++
#include <iostream>
#include <string_view>
using namespace std;

int main() {
    string_view sv = "Hello";

    cout << "operator[0]=" << sv[0] << endl;
    cout << "at(1)=" << sv.at(1) << endl;
    cout << "front=" << sv.front() << " back=" << sv.back() << endl;

    return 0;
}

substr / find / compare —— 子串、查找与比较

🎯 功能:substr 返回新视图;find 查找子串或字符位置;compare 按字典序比较。

💡 比喻:substr 像从便签上再撕下更小的便签;find 像在便签里找关键词位置;compare 则是把两张便签的内容按字典顺序比较。

🔧 声明:constexpr string_view substr(size_t pos = 0, size_t len = npos) const;
constexpr size_t find(string_view v, size_t pos = 0) const;
constexpr int compare(string_view v) const;

↩️ 返回值:substr 返回 string_view;find 返回位置或 npos;compare 返回负数/零/正数。

C++
#include <iostream>
#include <string_view>
using namespace std;

int main() {
    string_view sv = "Hello World";

    cout << "substr(6,5)=" << sv.substr(6, 5) << endl;
    cout << "find 'World': " << sv.find("World") << endl;
    cout << "find 'x': " << sv.find('x') << endl;

    string_view a = "apple";
    string_view b = "banana";
    cout << "compare: " << a.compare(b) << endl;

    return 0;
}

👉下一节:4.3 cctype →

▼4.3 <cctype> —— 字符判断与转换C++

是什么

📚 <cctype> 是 C++ 对 C 头文件 <ctype.h> 的封装,提供对单个 char 的字符分类判断(字母、数字、空白等)和大小写转换函数。

💡 生活比喻:<cctype> 像一位熟悉 ASCII 字母表的守门员——对每个字符检查「你是字母?数字?空格?大写?」,并能把小写变大写、大写变小写。

⚠️ 注意事项:传入参数必须是 unsigned char 值或 EOF;直接传入有符号 char 可能在字符值大于 127 时产生未定义行为;这些函数通常只保证对 C locale 的字符行为正确。

isalpha / isdigit / isalnum / isspace / ispunct —— 字符分类判断

🎯 功能:isalpha 判断字母;isdigit 判断数字;isalnum 判断字母或数字;isspace 判断空白字符;ispunct 判断标点符号。

💡 比喻:isalpha 像问「你是不是字母表里的成员」;isdigit 像问「你是不是 0-9 的数字」;isalnum 像问「你至少属于字母或数字之一」;isspace 检查是不是空格、制表符或换行;ispunct 则检查是不是逗号、句号等标点。

🔧 声明:int isalpha(int ch);
int isdigit(int ch);
int isalnum(int ch);
int isspace(int ch);
int ispunct(int ch);

↩️ 返回值:若属于对应类别返回非零(真),否则返回 0。

C++
#include <iostream>
#include <cctype>
using namespace std;

int main() {
    const char *s = "A 1,\t!";
    for (const char *p = s; *p; ++p) {
        unsigned char c = *p;
        cout << "'" << c << "': "
             << "alpha=" << isalpha(c)
             << " digit=" << isdigit(c)
             << " alnum=" << isalnum(c)
             << " space=" << isspace(c)
             << " punct=" << ispunct(c) << endl;
    }

    return 0;
}

isupper / islower —— 大小写判断

🎯 功能:isupper 判断是否为大写字母;islower 判断是否为小写字母。

💡 比喻:isupper 像检查名片上的名字是不是全大写印刷体;islower 则检查是不是小写手写体。

🔧 声明:int isupper(int ch);
int islower(int ch);

↩️ 返回值:若符合条件返回非零(真),否则返回 0。

C++
#include <iostream>
#include <cctype>
using namespace std;

int main() {
    const char *s = "Hello123";
    for (const char *p = s; *p; ++p) {
        unsigned char c = *p;
        cout << "'" << c << "': upper=" << isupper(c)
             << " lower=" << islower(c) << endl;
    }

    return 0;
}

toupper / tolower —— 大小写转换

🎯 功能:toupper 把小写字母转为大写;tolower 把大写字母转为小写;非字母字符保持不变。

💡 比喻:toupper 像把便签上的字母全部改成大写印章;tolower 则改成小写印章;本来就不是字母的内容保持原样。

🔧 声明:int toupper(int ch);
int tolower(int ch);

↩️ 返回值:返回转换后的字符;若无需转换则返回原字符。

C++
#include <iostream>
#include <cctype>
using namespace std;

int main() {
    const char *s = "Hello World! 123";
    for (const char *p = s; *p; ++p) {
        unsigned char c = *p;
        cout << (char)toupper(c);
    }
    cout << endl;

    for (const char *p = s; *p; ++p) {
        unsigned char c = *p;
        cout << (char)tolower(c);
    }
    cout << endl;

    return 0;
}

👉下一节:4.4 cstring →

▼4.4 <cstring> —— C 风格字符串操作C++

是什么

📚 <cstring> 是 C++ 对 <string.h> 的封装,提供 C 风格字符串(以 \0 结尾的 char 数组)的长度计算、拷贝、连接、比较、查找及内存块操作函数。

💡 生活比喻:<cstring> 像一套手工处理绳子的原始工具——能量长度、剪一段、接一段、找结点,也可以批量处理一大块布料(内存)。用得好很快,用不好容易割手。

⚠️ 注意事项:C 风格字符串必须保证以 \0 结尾;strcpy/strcat 不检查缓冲区大小,优先使用带 n 的版本或 std::string;memset/memcpy/memmove 操作的是字节数,注意目标缓冲区足够大;memcpy 要求源和目标不重叠,重叠区域请用 memmove。

strlen / strcpy / strncpy —— 长度、拷贝与受限拷贝

🎯 功能:strlen 返回字符串长度(不含 \0);strcpy 把源字符串拷贝到目标缓冲区;strncpy 拷贝最多 n 个字符,若源较短会用 \0 填充剩余空间。

💡 比喻:strlen 像量绳子的实际长度;strcpy 像把整条绳子复制到新地方;strncpy 像只复制指定长度,并在剩余空间里补上空位。

🔧 声明:size_t strlen(const char* s);
char* strcpy(char* dest, const char* src);
char* strncpy(char* dest, const char* src, size_t n);

↩️ 返回值:strlen 返回长度;strcpy/strncpy 返回 dest。

C++
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    const char *src = "Hello";
    char dest[32];

    cout << "strlen(src)=" << strlen(src) << endl;

    strcpy(dest, src);
    cout << "strcpy: " << dest << endl;

    char buf[8] = {0};
    strncpy(buf, "Hello World", sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';
    cout << "strncpy: " << buf << endl;

    return 0;
}

strcat / strncat —— 连接与受限连接

🎯 功能:strcat 把源字符串追加到目标字符串末尾;strncat 最多追加 n 个字符,并保证结果以 \0 结尾。

💡 比喻:strcat 像把两根绳子直接系在一起;strncat 像只把源绳子的前 n 段接到目标绳子上,并自动打好结尾结。

🔧 声明:char* strcat(char* dest, const char* src);
char* strncat(char* dest, const char* src, size_t n);

↩️ 返回值:均返回 dest。

C++
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    char dest[64] = "Hello, ";
    strcat(dest, "World");
    cout << "strcat: " << dest << endl;

    char dest2[64] = "C++";
    strncat(dest2, " Programming", 4);
    cout << "strncat: " << dest2 << endl;

    return 0;
}

strcmp / strncmp —— 字符串比较

🎯 功能:strcmp 按字典序比较两个完整字符串;strncmp 最多比较前 n 个字符。

💡 比喻:strcmp 像把两根绳子从头到尾逐段比粗细;strncmp 则像只比较前 n 厘米,后面的部分不再过问。

🔧 声明:int strcmp(const char* s1, const char* s2);
int strncmp(const char* s1, const char* s2, size_t n);

↩️ 返回值:返回负数(s1<s2)、零(相等)或正数(s1>s2)。

C++
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    const char *a = "apple";
    const char *b = "banana";

    cout << "strcmp apple vs banana: " << strcmp(a, b) << endl;
    cout << "strncmp app vs app 3: " << strncmp(a, "app", 3) << endl;
    cout << "strncmp app vs ape 3: " << strncmp(a, "ape", 3) << endl;

    return 0;
}

strchr / strstr / memset / memcpy / memmove —— 查找与内存操作

🎯 功能:strchr 查找字符首次出现位置;strstr 查找子串;memset 把内存区域填充为指定字节;memcpy 复制不重叠内存;memmove 可处理重叠内存。

💡 比喻:strchr 像在一根绳子里找到某个珠子第一次出现的位置;strstr 像找到一段图案首次出现的位置;memset 像把一块布全部染成同一种颜色;memcpy/memmove 像复印布料,后者即使原布和新布有重叠也能正确复印。

🔧 声明:char* strchr(const char* s, int c);
char* strstr(const char* haystack, const char* needle);
void* memset(void* dest, int ch, size_t n);
void* memcpy(void* dest, const void* src, size_t n);
void* memmove(void* dest, const void* src, size_t n);

↩️ 返回值:strchr/strstr 返回找到位置的指针或 NULL;memset/memcpy/memmove 返回 dest。

C++
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    const char *s = "Hello World";

    const char *p1 = strchr(s, 'o');
    cout << "strchr 'o': " << (p1 ? p1 : "not found") << endl;

    const char *p2 = strstr(s, "World");
    cout << "strstr \"World\": " << (p2 ? p2 : "not found") << endl;

    char buf[16] = {0};
    memset(buf, 'A', 10);
    cout << "memset: " << buf << endl;

    char src[] = "123456789";
    char dst[16];
    memcpy(dst, src, strlen(src) + 1);
    cout << "memcpy: " << dst << endl;

    /* 重叠移动 */
    char arr[] = "abcdef";
    memmove(arr + 2, arr, 4);
    arr[6] = '\0';
    cout << "memmove overlap: " << arr << endl;

    return 0;
}

👉下一节:5.1 后续章节 →

▼5.1 <cmath> —— 数学函数C++

是什么

📚 <cmath> 提供常用数学函数,包括三角函数、指数对数、幂运算、绝对值以及各类取整函数。

💡 生活比喻:像科学计算器上的按钮集合:平方根、正弦、对数、取整等运算一键可得,精度随类型而变。

⚠️ 注意事项:这些函数位于 std 命名空间;C++ 同时提供 float、double、long double 重载,调用时无需像 C 那样手动选择后缀。

std::sqrt / std::pow / std::sin / std::cos / std::tan

🎯 功能:sqrt 计算平方根,pow 计算幂,sin/cos/tan 计算三角函数值。

💡 比喻:像木匠的直角尺与量角器:sqrt 量对角线长度,pow 按指数放大尺寸,sin/cos/tan 测量倾斜角度。

🔧 声明:double sqrt(double x);
double pow(double base, double exp);
double sin(double x);
double cos(double x);
double tan(double x);

↩️ 返回值:返回对应类型的计算结果;定义域错误时可能返回 NaN 并设置 errno。

C++
#include <iostream>
#include <cmath>

int main() {
    std::cout << "sqrt(16) = " << std::sqrt(16.0) << '\n';
    std::cout << "pow(2, 10) = " << std::pow(2.0, 10.0) << '\n';
    std::cout << "sin(pi/2) = " << std::sin(3.14159265358979323846 / 2) << '\n';
    std::cout << "cos(0) = " << std::cos(0.0) << '\n';
    std::cout << "tan(pi/4) = " << std::tan(3.14159265358979323846 / 4) << '\n';
    return 0;
}

std::log / std::exp / std::fabs / std::floor / std::ceil

🎯 功能:log 计算自然对数,exp 计算 e 的幂,fabs 取绝对值,floor 向下取整,ceil 向上取整。

💡 比喻:像电梯楼层显示:floor 总是向下显示最近楼层,ceil 总是向上显示,fabs 只关心距离不看方向。

🔧 声明:double log(double x);
double exp(double x);
double fabs(double x);
double floor(double x);
double ceil(double x);

↩️ 返回值:返回对应类型的计算结果;log(0) 可能返回 -inf,log(负数) 返回 NaN。

C++
#include <iostream>
#include <cmath>

int main() {
    std::cout << "log(e) = " << std::log(2.718281828459045) << '\n';
    std::cout << "exp(1) = " << std::exp(1.0) << '\n';
    std::cout << "fabs(-3.14) = " << std::fabs(-3.14) << '\n';
    std::cout << "floor(2.9) = " << std::floor(2.9) << '\n';
    std::cout << "ceil(2.1) = " << std::ceil(2.1) << '\n';
    return 0;
}

std::round / std::trunc / std::fmod

🎯 功能:round 四舍五入到最近整数,trunc 向零截断,fmod 计算浮点取模。

💡 比喻:像银行柜台的零钱找零:round 按四舍五入给整元,trunc 直接舍去小数,fmod 只关心剩下的几分钱。

🔧 声明:double round(double x);
double trunc(double x);
double fmod(double x, double y);

↩️ 返回值:round/trunc 返回最接近的整数值(类型仍为浮点);fmod 返回 x / y 的余数。

C++
#include <iostream>
#include <cmath>

int main() {
    std::cout << "round(2.5) = " << std::round(2.5) << '\n';
    std::cout << "round(-2.5) = " << std::round(-2.5) << '\n';
    std::cout << "trunc(2.9) = " << std::trunc(2.9) << '\n';
    std::cout << "trunc(-2.9) = " << std::trunc(-2.9) << '\n';
    std::cout << "fmod(10.5, 3.2) = " << std::fmod(10.5, 3.2) << '\n';
    return 0;
}

👉下一节:5.2 <cstdlib> —— 随机数与通用工具 →

▼5.2 <cstdlib> —— 随机数与通用工具C++

是什么

📚 <cstdlib> 提供随机数生成、字符串转数值、程序终止、快速排序与二分查找等通用工具函数。

💡 生活比喻:像一个老式的工具百宝箱:虽然古朴,但随机数、排序、退出程序等功能随手可用。

⚠️ 注意事项:std::rand 的随机质量一般且线程安全性取决于实现;现代 C++ 推荐使用 <random>;字符串转换函数失败时行为较简单,需自行校验输入。

std::rand / std::srand

🎯 功能:rand 返回 0 到 RAND_MAX 之间的伪随机整数;srand 用种子初始化随机数生成器。

💡 比喻:像抽奖箱里的球:srand 决定球的初始排列,rand 每次摸出一个球。

🔧 声明:int rand(void);
void srand(unsigned int seed);

↩️ 返回值:rand 返回伪随机整数;srand 无返回值;相同种子会得到相同的随机序列。

C++
#include <iostream>
#include <cstdlib>
#include <ctime>

int main() {
    std::srand(static_cast<unsigned int>(std::time(nullptr)));
    std::cout << "random numbers: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << std::rand() % 100 << ' ';
    }
    std::cout << '\n';
    return 0;
}

std::atoi / std::atol / std::atof / std::strtol / std::strtod

🎯 功能:将 C 字符串转换为整数或浮点数;atoi/atof 使用简单但无法检测错误,strtol/strtod 更健壮。

💡 比喻:像把写在纸上的数字输入计算器:atoi 是直接抄写,strtol 还会检查有没有写错并告诉你抄到哪里结束。

🔧 声明:int atoi(const char *str);
long atol(const char *str);
double atof(const char *str);
long strtol(const char *str, char **endptr, int base);
double strtod(const char *str, char **endptr);

↩️ 返回值:atoi/atof 返回转换结果,无法区分错误;strtol/strtod 返回结果并通过 endptr 报告未处理部分。

C++
#include <iostream>
#include <cstdlib>

int main() {
    const char *s1 = "12345";
    const char *s2 = "3.14159extra";
    char *end = nullptr;

    std::cout << "atoi = " << std::atoi(s1) << '\n';
    std::cout << "atof = " << std::atof(s2) << '\n';

    long lv = std::strtol(s2, &end, 10);
    std::cout << "strtol = " << lv << ", remaining = " << end << '\n';

    double dv = std::strtod(s2, &end);
    std::cout << "strtod = " << dv << ", remaining = " << end << '\n';
    return 0;
}

std::exit / std::abort / std::atexit

🎯 功能:exit 正常终止程序并清理资源;abort 异常终止并产生信号;atexit 注册在 exit 时调用的函数。

💡 比喻:像剧场的闭幕流程:exit 是正常谢幕关灯,abort 是拉闸紧急散场,atexit 是提前安排的清场人员。

🔧 声明:void exit(int status);
void abort(void);
int atexit(void (*func)(void));

↩️ 返回值:exit 返回状态码给操作系统;abort 不保证清理资源;atexit 成功返回 0,失败返回非零。

C++
#include <iostream>
#include <cstdlib>

void cleanup() {
    std::cout << "cleanup registered by atexit\n";
}

int main() {
    std::atexit(cleanup);
    std::cout << "main body\n";
    std::exit(0);
    // 下面这行不会执行
    std::cout << "unreachable\n";
    return 0;
}

std::qsort / std::bsearch

🎯 功能:qsort 对数组进行快速排序;bsearch 对已排序数组进行二分查找。

💡 比喻:像图书馆整理书架:qsort 把书按编号排好,bsearch 在排好的书架上快速找到目标编号。

🔧 声明:void qsort(void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *));
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*cmp)(const void *, const void *));

↩️ 返回值:qsort 无返回值,原地排序;bsearch 返回找到元素的指针,未找到返回 nullptr。

C++
#include <iostream>
#include <cstdlib>

int cmp_int(const void *a, const void *b) {
    int x = *static_cast<const int *>(a);
    int y = *static_cast<const int *>(b);
    return (x > y) - (x < y);
}

int main() {
    int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
    std::size_t n = sizeof(arr) / sizeof(arr[0]);

    std::qsort(arr, n, sizeof(int), cmp_int);
    std::cout << "sorted: ";
    for (std::size_t i = 0; i < n; ++i) std::cout << arr[i] << ' ';
    std::cout << '\n';

    int key = 5;
    int *found = static_cast<int *>(std::bsearch(&key, arr, n, sizeof(int), cmp_int));
    std::cout << (found ? "found" : "not found") << '\n';
    return 0;
}

👉下一节:5.3 <random> —— 高质量随机数(C++11) →

▼5.3 <random> —— 高质量随机数(C++11)C++

是什么

📚 <random> 提供随机数引擎与分布类,可生成高质量、可复现且满足统计需求的伪随机数序列。

💡 生活比喻:像一台精密的发牌机:既能保证每张牌的随机性,也能根据种子复现同一局牌。

⚠️ 注意事项:推荐用 std::random_device 做种子,配合 mt19937 引擎与合适的分布类;不要跨线程共享同一个引擎对象。

random_device

🎯 功能:random_device 是一个非确定性随机数源,通常用于为伪随机引擎生成高质量种子。

💡 比喻:像气象站的噪声采集器:从环境中提取不可预测的初始信号,用来启动随机数发生器。

🔧 声明:std::random_device rd;

↩️ 返回值:调用 rd() 返回随机整数;若平台不支持非确定性源,可能回退到确定性实现。

C++
#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::cout << "random device values: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << rd() << ' ';
    }
    std::cout << '\n';
    return 0;
}

mt19937 / mt19937_64

🎯 功能:梅森旋转算法引擎,mt19937 生成 32 位整数,mt19937_64 生成 64 位整数,周期长且分布均匀。

💡 比喻:像一台高性能的洗牌机:周期极长,能把整副牌洗得非常均匀,适合模拟与蒙特卡洛计算。

🔧 声明:std::mt19937 gen(seed);
std::mt19937_64 gen64(seed);

↩️ 返回值:调用 gen() 返回伪随机整数;种子相同时序列完全相同。

C++
#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());

    std::cout << "mt19937 samples: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << gen() << ' ';
    }
    std::cout << '\n';
    return 0;
}

uniform_int_distribution / uniform_real_distribution

🎯 功能:将引擎输出映射到指定范围的均匀分布;前者产生整数,后者产生浮点数。

💡 比喻:像把随机数投到标有刻度的尺子上:uniform_int 落在整数刻度,uniform_real 落在连续区间内的任意位置。

🔧 声明:std::uniform_int_distribution<int> dist(a, b);
std::uniform_real_distribution<double> dist(a, b);

↩️ 返回值:返回闭区间或半开区间内的随机值,具体边界由分布类型决定。

C++
#include <iostream>
#include <random>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<int> i_dist(1, 6);
    std::uniform_real_distribution<double> r_dist(0.0, 1.0);

    std::cout << "dice rolls: ";
    for (int i = 0; i < 5; ++i) std::cout << i_dist(gen) << ' ';
    std::cout << "\nfloats: ";
    for (int i = 0; i < 5; ++i) std::cout << r_dist(gen) << ' ';
    std::cout << '\n';
    return 0;
}

normal_distribution

🎯 功能:生成服从正态分布(高斯分布)的随机数,常用于模拟自然噪声、测量误差等。

💡 比喻:像工厂生产零件的尺寸误差:大多数接近均值,偏离越远出现越少,整体呈钟形曲线。

🔧 声明:std::normal_distribution<double> dist(mean, stddev);

↩️ 返回值:返回以 mean 为均值、stddev 为标准差的随机浮点数。

C++
#include <iostream>
#include <random>
#include <cmath>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::normal_distribution<double> dist(0.0, 1.0);

    std::cout << "normal samples: ";
    for (int i = 0; i < 5; ++i) {
        std::cout << std::round(dist(gen) * 100) / 100 << ' ';
    }
    std::cout << '\n';
    return 0;
}

👉下一节:5.4 <complex> —— 复数 →

▼5.4 <complex> —— 复数C++

是什么

📚 <complex> 提供复数模板类 std::complex<T>,支持构造、访问、模长、幅角及常用复数数学函数。

💡 生活比喻:像数学课上的复平面:横轴是实部,纵轴是虚部,平面上的每个点都能用一个复数表示。

⚠️ 注意事项:C++ 复数是 std::complex<T>,不是 C99 的 _Complex;支持 float、double、long double 三种特化,数学函数在 std 命名空间中。

complex<T> 构造函数

🎯 功能:std::complex<T> 可通过实部、虚部或极坐标形式构造,也支持从标量隐式构造。

💡 比喻:像用坐标或极径角度来定位地图上的点:既可以说向东 3 向北 4,也可以说距离 5、方向 53 度。

🔧 声明:std::complex<double> z(3.0, 4.0);
std::complex<double> z2(5.0); // 实部 5,虚部 0

↩️ 返回值:构造一个复数对象,实部和虚部分别初始化为给定值。

C++
#include <iostream>
#include <complex>

int main() {
    std::complex<double> z1(3.0, 4.0);
    std::complex<double> z2(5.0);  // 5 + 0i
    std::complex<double> z3 = z1 + z2;

    std::cout << "z1 = " << z1 << '\n';
    std::cout << "z2 = " << z2 << '\n';
    std::cout << "z1 + z2 = " << z3 << '\n';
    return 0;
}

real / imag / abs / arg

🎯 功能:real 与 imag 分别访问实部和虚部;abs 计算模长;arg 计算幅角(相位)。

💡 比喻:像把向量分解:real 是水平分量,imag 是垂直分量,abs 是箭头长度,arg 是箭头与横轴的夹角。

🔧 声明:T real(const std::complex<T> &z);
T imag(const std::complex<T> &z);
T abs(const std::complex<T> &z);
T arg(const std::complex<T> &z);

↩️ 返回值:real/imag/abs 返回对应标量类型;arg 返回以弧度表示的幅角。

C++
#include <iostream>
#include <complex>
#include <cmath>

int main() {
    std::complex<double> z(3.0, 4.0);
    std::cout << "real = " << std::real(z) << '\n';
    std::cout << "imag = " << std::imag(z) << '\n';
    std::cout << "abs = " << std::abs(z) << '\n';
    std::cout << "arg = " << std::arg(z) << " rad\n";
    return 0;
}

polar / conj / exp / log / sqrt / pow

🎯 功能:polar 由模长和幅角构造复数;conj 求共轭;exp/log/sqrt/pow 执行对应复数运算。

💡 比喻:像复平面上的旋转与缩放:polar 给定长度和角度直接画出向量,exp 把复数映射到单位圆上的旋转。

🔧 声明:std::complex<T> polar(T r, T theta);
std::complex<T> conj(const std::complex<T> &z);
std::complex<T> exp(const std::complex<T> &z);
std::complex<T> log(const std::complex<T> &z);
std::complex<T> sqrt(const std::complex<T> &z);
std::complex<T> pow(const std::complex<T> &base, const std::complex<T> &exp);

↩️ 返回值:返回复数结果;log 有分支切割,sqrt 选择主值。

C++
#include <iostream>
#include <complex>
#include <cmath>

int main() {
    std::complex<double> z = std::polar(5.0, 0.927295218);
    std::cout << "polar -> " << z << '\n';
    std::cout << "conj = " << std::conj(z) << '\n';
    std::cout << "exp = " << std::exp(std::complex<double>(0.0, 3.1415926535)) << '\n';
    std::cout << "log = " << std::log(z) << '\n';
    std::cout << "sqrt = " << std::sqrt(std::complex<double>(-1.0, 0.0)) << '\n';
    std::cout << "pow = " << std::pow(std::complex<double>(0.0, 1.0), 2) << '\n';
    return 0;
}

👉下一节:5.5 <limits> —— 类型极限值 →

▼5.5 <limits> —— 类型极限值C++

是什么

📚 <limits> 通过 std::numeric_limits<T> 在编译期或运行期查询类型的最大值、最小值、精度、特殊值等属性。

💡 生活比喻:像汽车仪表盘上的极限刻度:告诉你油箱最大容量、极速上限和速度表的读数精度误差。

⚠️ 注意事项:numeric_limits 是模板类特化,比 C 的宏常量更类型安全;浮点类型的 quiet_NaN、infinity 可用来构造或检测特殊值。

numeric_limits<T>::max / min / lowest

🎯 功能:max 返回可表示的最大有限值;min 对整数返回最小值,对浮点返回最小正规格化正数;lowest 返回最小有限值。

💡 比喻:像温度计的上下限:max 是最高刻度,lowest 是最低刻度,min 对浮点来说更像是“最小一格”的精度。

🔧 声明:static constexpr T max() noexcept;
static constexpr T min() noexcept;
static constexpr T lowest() noexcept;

↩️ 返回值:返回对应类型的极限值;可用于泛型代码中避免硬编码常量。

C++
#include <iostream>
#include <limits>

int main() {
    std::cout << "int max = " << std::numeric_limits<int>::max() << '\n';
    std::cout << "int min = " << std::numeric_limits<int>::min() << '\n';
    std::cout << "double max = " << std::numeric_limits<double>::max() << '\n';
    std::cout << "double lowest = " << std::numeric_limits<double>::lowest() << '\n';
    return 0;
}

epsilon / infinity / quiet_NaN

🎯 功能:epsilon 返回 1 与下一个可表示值之间的差;infinity 返回正无穷;quiet_NaN 返回静默 NaN。

💡 比喻:像精密天平的最小刻度:epsilon 是天平能分辨的最小重量差,infinity 是超载显示,NaN 是读数错误。

🔧 声明:static constexpr T epsilon() noexcept;
static constexpr T infinity() noexcept;
static constexpr T quiet_NaN() noexcept;

↩️ 返回值:返回对应类型的特殊浮点值;用于比较、初始化或检测非法计算结果。

C++
#include <iostream>
#include <limits>

int main() {
    std::cout << "epsilon = " << std::numeric_limits<double>::epsilon() << '\n';
    std::cout << "infinity = " << std::numeric_limits<double>::infinity() << '\n';
    std::cout << "quiet_NaN = " << std::numeric_limits<double>::quiet_NaN() << '\n';

    double inf = std::numeric_limits<double>::infinity();
    std::cout << "inf > max? " << (inf > std::numeric_limits<double>::max()) << '\n';
    return 0;
}

digits / is_signed / is_integer

🎯 功能:digits 返回尾数位数;is_signed 表示类型是否有符号;is_integer 表示是否为整数类型。

💡 比喻:像查看食材标签:digits 是有效成分位数,is_signed 是否含正负,is_integer 是整袋还是分装。

🔧 声明:static constexpr int digits;
static constexpr bool is_signed;
static constexpr bool is_integer;

↩️ 返回值:返回编译期常量,可用于模板元编程或运行时分支判断。

C++
#include <iostream>
#include <limits>

int main() {
    std::cout << "int digits = " << std::numeric_limits<int>::digits << '\n';
    std::cout << "int is_signed = " << std::numeric_limits<int>::is_signed << '\n';
    std::cout << "int is_integer = " << std::numeric_limits<int>::is_integer << '\n';
    std::cout << "double digits = " << std::numeric_limits<double>::digits << '\n';
    std::cout << "double is_integer = " << std::numeric_limits<double>::is_integer << '\n';
    return 0;
}

👉下一节:5.6 <ratio> —— 编译期有理数(C++11) →

▼5.6 <ratio> —— 编译期有理数(C++11)C++

是什么

📚 <ratio> 提供编译期有理数运算,常用于时间库、单位换算等零开销抽象,结果在编译期就已确定。

💡 生活比喻:像建筑工程图纸上标注的精确比例:1:100、3:4 等在编译期就已确定,无需运行时计算。

⚠️ 注意事项:ratio 会自动约分,分母不能为零;所有运算结果都是编译期常量,不占用运行时开销。

ratio / ratio_add / ratio_subtract / ratio_multiply / ratio_divide

🎯 功能:ratio<N,D> 表示有理数 N/D;ratio_add、ratio_subtract、ratio_multiply、ratio_divide 在编译期完成四则运算。

💡 比喻:像菜谱中的换算表:1 杯 = 250 毫升、1 勺 = 15 毫升,编译期已经把“几杯加几勺”算成了统一单位。

🔧 声明:using half = std::ratio<1, 2>;
using quarter = std::ratio<1, 4>;
using sum = std::ratio_add<half, quarter>;

↩️ 返回值:运算结果产生新的 ratio 类型,其 num 与 den 成员分别为约分后的分子与分母。

C++
#include <iostream>
#include <ratio>

int main() {
    using half = std::ratio<1, 2>;
    using quarter = std::ratio<1, 4>;
    using one = std::ratio<1, 1>;

    using sum = std::ratio_add<half, quarter>;
    using diff = std::ratio_subtract<one, half>;
    using prod = std::ratio_multiply<half, quarter>;
    using quot = std::ratio_divide<one, quarter>;

    std::cout << "1/2 + 1/4 = " << sum::num << '/' << sum::den << '\n';
    std::cout << "1 - 1/2 = " << diff::num << '/' << diff::den << '\n';
    std::cout << "1/2 * 1/4 = " << prod::num << '/' << prod::den << '\n';
    std::cout << "1 / 1/4 = " << quot::num << '/' << quot::den << '\n';
    return 0;
}

ratio_equal / ratio_less

🎯 功能:ratio_equal 和 ratio_less 在编译期比较两个 ratio 类型是否相等或前者是否小于后者。

💡 比喻:像比较两张不同比例尺的地图:编译期先把它们换算到同一比例,再判断大小或是否相同。

🔧 声明:std::ratio_equal<R1, R2>::value
std::ratio_less<R1, R2>::value

↩️ 返回值:返回编译期布尔常量 value,可用于 static_assert 或模板分支。

C++
#include <iostream>
#include <ratio>
#include <type_traits>

int main() {
    using a = std::ratio<2, 4>;
    using b = std::ratio<1, 2>;
    using c = std::ratio<1, 3>;

    std::cout << "2/4 == 1/2? " << std::ratio_equal<a, b>::value << '\n';
    std::cout << "1/3 < 1/2? " << std::ratio_less<c, b>::value << '\n';

    static_assert(std::ratio_equal<a, b>::value, "2/4 should equal 1/2");
    static_assert(std::ratio_less<c, b>::value, "1/3 should be less than 1/2");
    return 0;
}

👉下一节:6.1 其他标准库组件 →

▼6.1 <chrono> —— 现代时间库(C++11)C++

是什么

📚 介绍:<chrono> 是 C++11 引入的现代时间库,提供类型安全、高精度的时间类型,避免了 C 风格时间单位混用导致的错误。

💡 比喻:它像一套带刻度和单位的精密实验室秒表——秒、毫秒、微秒各自有标签,绝不会把“1 秒”和“1000 毫秒”当成同一个东西。

⚠️ 注意:测程序耗时时优先使用 steady_clock(单调不回调),获取挂历时间用 system_clock,high_resolution_clock 可能是前两者的别名。

std::duration

🎯 功能:表示一段时长,由“计数类型 + 时间单位比例”组成,例如 seconds、milliseconds。

💡 比喻:像不同容量的量杯,杯子上印着“秒”“毫秒”单位,永远不会把 1 杯和 1000 毫升搞混。

🔧 声明:template<class Rep, class Period = std::ratio<1>> class duration;

↩️ 返回值:类型本身无返回值;调用 .count() 返回底层计数值。

C++
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;

int main() {
    seconds s(5);
    milliseconds ms(2500);
    microseconds total = duration_cast<microseconds>(s) + ms;
    cout << "5s = " << s.count() << " seconds\n";
    cout << "2500ms = " << ms.count() << " ms\n";
    cout << "total = " << total.count() << " us\n";
    return 0;
}

std::time_point

🎯 功能:表示某个时钟上的一个具体时刻,通常用“距离该时钟纪元(epoch)的 duration”来衡量。

💡 比喻:像火车票上的发车时间——它不是一个孤立的数字,而是相对于“零时刻”的一套完整坐标。

🔧 声明:template<class Clock, class Duration = typename Clock::duration> class time_point;

↩️ 返回值:类型本身无返回值;.time_since_epoch() 返回对应的 duration。

C++
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;

int main() {
    system_clock::time_point now = system_clock::now();
    system_clock::time_point epoch;
    seconds elapsed = duration_cast<seconds>(now - epoch);
    cout << "seconds since epoch: " << elapsed.count() << "\n";
    return 0;
}

system_clock / steady_clock / high_resolution_clock

🎯 功能:三种标准时钟:system_clock 对系统挂历时间,steady_clock 单调递增,high_resolution_clock 提供最高精度。

💡 比喻:实验室里有三面钟:墙上的标准时钟、永不回拨的计时码表、以及记录最快动作的高速摄影时钟。

🔧 声明:class system_clock; class steady_clock; class high_resolution_clock;

↩️ 返回值:now() 返回对应的 time_point;system_clock 还提供 to_time_t / from_time_t 与 time_t 互转。

C++
#include <chrono>
#include <ctime>
#include <iostream>
using namespace std;
using namespace std::chrono;

int main() {
    auto sys_now = system_clock::now();
    time_t tt = system_clock::to_time_t(sys_now);
    cout << "system: " << ctime(&tt);

    auto t0 = steady_clock::now();
    long long sum = 0;
    for (int i = 0; i < 1000000; ++i) sum += i;
    auto t1 = steady_clock::now();
    cout << "steady elapsed: "
         << duration_cast<microseconds>(t1 - t0).count() << " us\n";

    auto hi = high_resolution_clock::now();
    cout << "high_resolution ticks since epoch: "
         << hi.time_since_epoch().count() << "\n";
    return 0;
}

now() / duration_cast / time_since_epoch

🎯 功能:now() 获取当前时间点;duration_cast 在不同精度时长间安全转换;time_since_epoch 返回距纪元时长。

💡 比喻:就像先看表、再按需要把“几分几秒”换算成“毫秒数”,并记录从午夜起一共过了多少秒。

🔧 声明:static time_point Clock::now(); template<class To, ...> constexpr To duration_cast(const duration<Rep, Period>&); Duration time_point::time_since_epoch() const;

↩️ 返回值:now() 返回 time_point;duration_cast 返回目标 duration 类型;time_since_epoch 返回 duration。

C++
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;

int main() {
    auto start = high_resolution_clock::now();
    long long sum = 0;
    for (int i = 0; i < 10000000; ++i) sum += i;
    auto end = high_resolution_clock::now();

    auto us = duration_cast<microseconds>(end - start);
    auto ms = duration_cast<milliseconds>(us);
    cout << "elapsed us: " << us.count() << "\n";
    cout << "elapsed ms: " << ms.count() << "\n";
    cout << "epoch ns: " << end.time_since_epoch().count() << "\n";
    return 0;
}
下一节:6.2 <ctime> —— C 风格时间

▼6.2 <ctime> —— C 风格时间C 兼容

是什么

📚 介绍:<ctime> 是 C 语言时间函数在 C++ 中的包装,核心类型是 time_t 和 struct tm,兼容性好但类型安全较弱。

💡 比喻:它像一台老式机械挂钟——人人都能看懂,也能跨房间对时,但走时不准或被人拨回时不会提醒你。

⚠️ 注意:localtime / gmtime 返回指向内部静态存储的指针,多线程下不安全;strftime 缓冲区要足够大。

std::time

🎯 功能:获取当前日历时间,以 1970-01-01 00:00:00 UTC 起的秒数表示。

💡 比喻:像按一下秒表并把当前读数抄到纸上,这张纸就是 time_t。

🔧 声明:std::time_t std::time(std::time_t* t);

↩️ 返回值:成功返回当前时间戳;失败返回 static_cast<std::time_t>(-1)。

C++
#include <ctime>
#include <iostream>
using namespace std;

int main() {
    time_t now = time(nullptr);
    if (now == static_cast<time_t>(-1)) {
        cerr << "time failed\n";
        return 1;
    }
    cout << "timestamp: " << now << "\n";
    return 0;
}

std::localtime / std::gmtime

🎯 功能:把 time_t 转换为本地时区或 UTC 的 struct tm 分解时间。

💡 比喻:像把一串总秒数翻译成“年月日时分秒”的完整日历格。

🔧 声明:std::tm* std::localtime(const std::time_t* t); std::tm* std::gmtime(const std::time_t* t);

↩️ 返回值:返回指向静态 std::tm 的指针;失败返回 nullptr。

C++
#include <ctime>
#include <iostream>
using namespace std;

int main() {
    time_t now = time(nullptr);
    tm* local = localtime(&now);
    tm* utc = gmtime(&now);
    if (local && utc) {
        cout << "local hour: " << local->tm_hour << "\n";
        cout << "utc hour: " << utc->tm_hour << "\n";
    }
    return 0;
}

std::strftime

🎯 功能:按照指定格式把 struct tm 输出为字符串。

💡 比喻:像一张日期格式模板,把“年月日”按你想要的顺序打印成便签。

🔧 声明:std::size_t std::strftime(char* s, std::size_t max, const char* fmt, const std::tm* tm);

↩️ 返回值:成功返回写入的字符数(不含 \0);空间不足或失败返回 0。

C++
#include <ctime>
#include <iostream>
using namespace std;

int main() {
    time_t now = time(nullptr);
    tm* local = localtime(&now);
    char buf[80];
    size_t n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
    cout << "formatted: " << (n ? buf : "failed") << "\n";
    return 0;
}

std::difftime / std::mktime

🎯 功能:difftime 计算两个 time_t 的秒差;mktime 把本地 struct tm 转换回 time_t。

💡 比喻:difftime 像两把尺子量差距;mktime 像把散装的“年月日”拼回一个总秒数。

🔧 声明:double std::difftime(std::time_t end, std::time_t start); std::time_t std::mktime(std::tm* tm);

↩️ 返回值:difftime 返回秒数;mktime 返回 time_t,失败返回 -1。

C++
#include <ctime>
#include <iostream>
using namespace std;

int main() {
    time_t t0 = time(nullptr);
    tm then = {};
    then.tm_year = 2024 - 1900;
    then.tm_mon = 0;
    then.tm_mday = 1;
    then.tm_hour = 0;
    then.tm_min = 0;
    then.tm_sec = 0;
    time_t t1 = mktime(&then);
    double diff = difftime(t0, t1);
    cout << "seconds since 2024-01-01: " << diff << "\n";
    return 0;
}

std::clock

🎯 功能:返回程序自启动以来消耗的处理器时间,以 clock_t 为单位。

💡 比喻:像一台只记录 CPU 工作耗电量的电表,而不是墙上流逝的时间。

🔧 声明:std::clock_t std::clock();

↩️ 返回值:成功返回处理器时钟滴答数;失败返回 -1。

C++
#include <ctime>
#include <iostream>
using namespace std;

int main() {
    clock_t start = clock();
    long long sum = 0;
    for (int i = 0; i < 10000000; ++i) sum += i;
    clock_t end = clock();
    double cpu_ms = 1000.0 * (end - start) / CLOCKS_PER_SEC;
    cout << "cpu time: " << cpu_ms << " ms\n";
    return 0;
}
下一节:7.1 <memory> —— 智能指针(C++11 重点!)

▼7.1 <memory> —— 智能指针(C++11 重点!)C++

是什么

📚 介绍:<memory> 提供智能指针,用 RAII 机制自动管理动态内存,避免 delete 遗漏、异常跳过释放等问题。

💡 比喻:它像一张会自动还书的借书证——当最后一个使用者离开作用域,书籍自动归还图书馆,不用你自己去还。

⚠️ 注意:优先使用 make_unique / make_shared 创建智能指针;不要把普通指针和智能指针混用,避免重复释放。

std::unique_ptr

🎯 功能:独占所有权,同一时刻只有一个 unique_ptr 指向对象;不能复制,但可以移动。

💡 比喻:像一套独家代理权——合同只能有一份正本,你可以把它转交给下家,但不能复印共享。

🔧 声明:template<class T, class Deleter = std::default_delete<T>> class unique_ptr;

↩️ 返回值:operator* / operator-> 访问对象;get() 返回底层指针。

C++
#include <iostream>
#include <memory>
using namespace std;

int main() {
    unique_ptr<int> p(new int(42));
    cout << "value: " << *p << "\n";

    unique_ptr<int> q = move(p);
    if (!p) cout << "p is empty after move\n";
    cout << "q value: " << *q << "\n";
    return 0;
}

std::shared_ptr / std::make_shared

🎯 功能:共享所有权,多个 shared_ptr 可指向同一对象,通过引用计数自动释放。

💡 比喻:像一本多人传阅的电子书——每个借阅者都登记在册,最后一人归还后书才下架。

🔧 声明:template<class T> class shared_ptr; template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);

↩️ 返回值:shared_ptr 返回管理对象;use_count() 返回当前引用计数。

C++
#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> a = make_shared<int>(100);
    {
        shared_ptr<int> b = a;
        cout << "ref count: " << a.use_count() << "\n";
    }
    cout << "after inner block: " << a.use_count() << "\n";
    cout << "value: " << *a << "\n";
    return 0;
}

std::weak_ptr

🎯 功能:不拥有对象的弱引用,用于打破 shared_ptr 循环引用或安全观察对象是否存活。

💡 比喻:像隔着玻璃窗看展品——你能看见,但不算拥有;展品撤走后玻璃窗也不会把它留住。

🔧 声明:template<class T> class weak_ptr;

↩️ 返回值:lock() 返回 shared_ptr(对象存活时有效);expired() 判断对象是否已释放。

C++
#include <iostream>
#include <memory>
using namespace std;

int main() {
    shared_ptr<int> sp = make_shared<int>(7);
    weak_ptr<int> wp = sp;

    if (auto locked = wp.lock()) {
        cout << "weak sees: " << *locked << "\n";
    }

    sp.reset();
    if (wp.expired()) {
        cout << "original object destroyed\n";
    }
    return 0;
}

std::enable_shared_from_this

🎯 功能:让对象在已受 shared_ptr 管理时,安全地返回指向自身的 shared_ptr。

💡 比喻:像知道自己在图书馆系统里的借阅编号,可以主动给别人再办一张合法的借书证。

🔧 声明:template<class T> class enable_shared_from_this;

↩️ 返回值:shared_from_this() 返回 shared_ptr<T>;必须在对象已被 shared_ptr 管理后调用。

C++
#include <iostream>
#include <memory>
using namespace std;

struct Node : enable_shared_from_this<Node> {
    int value;
    explicit Node(int v) : value(v) {}
    shared_ptr<Node> get_shared() { return shared_from_this(); }
};

int main() {
    shared_ptr<Node> n = make_shared<Node>(42);
    shared_ptr<Node> m = n->get_shared();
    cout << "value: " << m->value << ", refs: " << n.use_count() << "\n";
    return 0;
}
下一节:7.2 <new> —— 内存分配原语

▼7.2 <new> —— 内存分配原语C++

是什么

📚 介绍:<new> 暴露 C++ 底层的内存分配原语,包括 operator new/delete、数组版本、nothrow 版本以及 bad_alloc 异常。

💡 比喻:new 表达式像租一套带家具的公寓;operator new 则只租一块空地,你要自己盖房、自己拆房、自己还地。

⚠️ 注意:除非实现容器或内存池,否则不要直接使用 operator new;placement new 构造的对象必须手动调用析构函数。

operator new / operator delete

🎯 功能:分配和释放原始、未初始化的内存块;new/delete 表达式内部会调用它们。

💡 比喻:像从仓库租来一块空场地,并在退租时把场地原样交还。

🔧 声明:void* operator new(std::size_t size); void operator delete(void* ptr) noexcept;

↩️ 返回值:operator new 返回指向分配内存的指针,失败抛 std::bad_alloc;operator delete 无返回值。

C++
#include <new>
#include <iostream>
using namespace std;

struct Widget {
    int v;
    Widget(int x) : v(x) {}
};

int main() {
    void* raw = operator new(sizeof(Widget));
    Widget* p = new (raw) Widget(123);
    cout << "value: " << p->v << "\n";
    p->~Widget();
    operator delete(raw);
    return 0;
}

operator new[] / operator delete[]

🎯 功能:为数组分配和释放原始内存;通常由 new[]/delete[] 表达式内部调用。

💡 比喻:像一次性租下一片连续的空白展厅,每个展位单独布置,最后统一清场。

🔧 声明:void* operator new[](std::size_t size); void operator delete[](void* ptr) noexcept;

↩️ 返回值:operator new[] 返回数组首地址,失败抛 std::bad_alloc;operator delete[] 无返回值。

C++
#include <new>
#include <iostream>
using namespace std;

int main() {
    const int n = 5;
    void* raw = operator new[](n * sizeof(int));
    int* p = static_cast<int*>(raw);
    for (int i = 0; i < n; ++i) {
        new (&p[i]) int(i + 1);
    }
    for (int i = 0; i < n; ++i) {
        cout << p[i] << " ";
        p[i].~int();
    }
    cout << "\n";
    operator delete[](raw);
    return 0;
}

nothrow new

🎯 功能:分配失败时不抛出异常,而是返回 nullptr,适合需要优雅降级内存分配失败的场景。

💡 比喻:像一位不说“没有”只说“现货已售罄”的供应商,让你能继续营业而不是当场倒闭。

🔧 声明:void* operator new(std::size_t size, const std::nothrow_t&) noexcept;

↩️ 返回值:成功返回内存指针;失败返回 nullptr。

C++
#include <new>
#include <iostream>
using namespace std;

int main() {
    int* p = new (nothrow) int[1000000];
    if (p) {
        cout << "allocated\n";
        delete[] p;
    } else {
        cout << "allocation failed\n";
    }
    return 0;
}

std::bad_alloc

🎯 功能:默认 new 分配失败时抛出的异常;捕获它可以在内存不足时执行回退或记录日志。

💡 比喻:像仓库管理员的通知单:你要的场地没有了,系统不会崩溃,但会正式发一份“缺货”公告。

🔧 声明:class bad_alloc; // 定义在 <new> 与 <exception>

↩️ 返回值:what() 返回 C 风格错误描述字符串。

C++
#include <new>
#include <iostream>
#include <limits>
using namespace std;

int main() {
    try {
        size_t huge = numeric_limits<size_t>::max() / 4;
        int* p = new int[huge];
        delete[] p;
    } catch (const bad_alloc& e) {
        cout << "bad_alloc: " << e.what() << "\n";
    }
    return 0;
}
下一节:8.1 <thread> —— 线程

▼8.1 <thread> —— 线程C++

是什么

📚 介绍:<thread> 是 C++11 并发编程的入口头文件,用于创建和管理线程,让程序同时执行多条独立控制流。

💡 比喻:它像一支交响乐队——每个乐手(线程)各拿一份谱子,在指挥的调度下同时演奏,最终汇成一首完整的乐曲。

⚠️ 注意:线程对象必须在析构前调用 join() 或 detach(),否则程序会调用 std::terminate 异常终止;共享数据务必配合互斥锁保护。

std::thread 构造函数

🎯 功能:创建一个新线程并启动执行,可传入函数、参数、lambda 或可调用对象。

💡 比喻:像给工厂新招一名工人,把任务清单和原材料一并交给他,他立刻去旁边的工作台开工。

🔧 声明:template<class Function, class... Args> explicit thread(Function&& f, Args&&... args);

↩️ 返回值:构造后新线程立即开始执行;thread 对象本身无返回值,通过 get_id() 获取线程标识。

C++
#include <thread>
#include <iostream>
using namespace std;

void worker(int id, int value) {
    cout << "thread " << id << " got " << value << "\n";
}

int main() {
    thread t1(worker, 1, 42);
    thread t2([]() {
        cout << "lambda thread running\n";
    });

    t1.join();
    t2.join();
    return 0;
}

join / detach

🎯 功能:join() 阻塞当前线程直到目标线程结束;detach() 把线程与 thread 对象分离,使其在后台独立运行。

💡 比喻:join 像目送客人离开并关上门;detach 则像把风筝线剪断,让它自己飘走,你不再等它回来。

🔧 声明:void join(); void detach();

↩️ 返回值:两者均无返回值;joinable() 可判断线程是否仍可被 join/detach。

C++
#include <thread>
#include <iostream>
#include <chrono>
using namespace std;

void task() {
    this_thread::sleep_for(chrono::milliseconds(100));
    cout << "task done\n";
}

int main() {
    thread t(task);
    if (t.joinable()) {
        t.join();
    }
    cout << "main continues\n";

    thread t2(task);
    t2.detach();
    this_thread::sleep_for(chrono::milliseconds(200));
    return 0;
}

get_id / hardware_concurrency / sleep_for

🎯 功能:get_id() 返回线程唯一标识;hardware_concurrency() 返回支持的并发线程数;sleep_for() 让当前线程休眠指定时长。

💡 比喻:像每个工人佩戴工牌、查询车间最多能站多少人,以及让当前工人原地休息一会儿再继续。

🔧 声明:thread::id get_id() const noexcept; unsigned int hardware_concurrency() noexcept; template<class Rep, class Period> void sleep_for(const chrono::duration<Rep, Period>& rel_time);

↩️ 返回值:get_id() 返回 thread::id;hardware_concurrency() 返回 unsigned int(可能为 0,表示未知);sleep_for 无返回值。

C++
#include <thread>
#include <iostream>
#include <chrono>
using namespace std;

int main() {
    cout << "main thread id: " << this_thread::get_id() << "\n";
    cout << "hardware concurrency: " << thread::hardware_concurrency() << "\n";

    auto start = chrono::steady_clock::now();
    this_thread::sleep_for(chrono::milliseconds(50));
    auto end = chrono::steady_clock::now();

    cout << "slept ms: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "\n";
    return 0;
}
下一节:8.2 <mutex> —— 互斥锁

▼8.2 <mutex> —— 互斥锁C++

是什么

📚 介绍:<mutex> 提供互斥锁原语,防止多个线程同时进入临界区,避免数据竞争和不一致。

💡 比喻:它像公共厕所的锁——进去的人反锁,其他人只能排队;出来开锁,下一个人才能进。

⚠️ 注意:永远用 RAII 锁包装(lock_guard / unique_lock)而非裸 lock/unlock,防止异常导致死锁;不要重复锁定普通 mutex。

mutex / lock / unlock

🎯 功能:mutex 是最基本的互斥量;lock() 获取锁,unlock() 释放锁,两者必须成对出现。

💡 比喻:像一把只能由一个人持有的普通门锁:进去时上锁,离开时开锁,忘记开锁就会把人永远关在外面。

🔧 声明:class mutex; void lock(); void unlock();

↩️ 返回值:lock() / unlock() 无返回值;lock() 阻塞直到获取锁,重复 lock 同一线程会导致死锁。

C++
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;

mutex mtx;
int counter = 0;

void worker() {
    for (int i = 0; i < 10000; ++i) {
        mtx.lock();
        ++counter;
        mtx.unlock();
    }
}

int main() {
    thread t1(worker);
    thread t2(worker);
    t1.join();
    t2.join();
    cout << "counter: " << counter << "\n";
    return 0;
}

std::lock_guard

🎯 功能:RAII 风格的互斥锁包装,构造时加锁,析构时自动解锁,异常安全且不会忘记 unlock。

💡 比喻:像自动门禁卡——刷卡进门即上锁,出门时卡槽自动收回并开门,绝不会忘记解锁。

🔧 声明:template<class Mutex> class lock_guard;

↩️ 返回值:无返回值;生命周期结束或作用域退出时自动释放锁。

C++
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;

mutex mtx;
int counter = 0;

void worker() {
    for (int i = 0; i < 10000; ++i) {
        lock_guard<mutex> lk(mtx);
        ++counter;
    }
}

int main() {
    thread t1(worker);
    thread t2(worker);
    t1.join();
    t2.join();
    cout << "counter: " << counter << "\n";
    return 0;
}

std::unique_lock

🎯 功能:比 lock_guard 更灵活的 RAII 锁,支持延迟加锁、提前解锁、尝试加锁以及条件变量配合。

💡 比喻:像一把带遥控的智能门锁——可以预约上锁、临时开锁、尝试开门,还能和报警系统联动。

🔧 声明:template<class Mutex> class unique_lock;

↩️ 返回值:无返回值;提供 lock()、unlock()、try_lock()、try_lock_for() 等方法,owns_lock() 查询是否持有锁。

C++
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;

mutex mtx;
int data = 0;

void producer() {
    unique_lock<mutex> lk(mtx);
    data = 42;
    lk.unlock();
    cout << "produced\n";
}

void consumer() {
    unique_lock<mutex> lk(mtx, defer_lock);
    if (lk.try_lock()) {
        cout << "consumed: " << data << "\n";
    } else {
        cout << "could not lock\n";
    }
}

int main() {
    thread t1(producer);
    thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

recursive_mutex / timed_mutex

🎯 功能:recursive_mutex 允许同一线程多次加锁;timed_mutex 支持限时等待 lock_for / lock_until。

💡 比喻:recursive_mutex 像可多次扣上的安全带扣,自己扣几次就要自己解几次;timed_mutex 像限时停车位,超时没停进去就离开。

🔧 声明:class recursive_mutex; class timed_mutex;

↩️ 返回值:recursive_mutex 的 lock/unlock 成对计数;timed_mutex 的 try_lock_for/try_lock_until 成功返回 true,失败返回 false。

C++
#include <mutex>
#include <thread>
#include <iostream>
#include <chrono>
using namespace std;

recursive_mutex rmtx;
timed_mutex tmtx;

void nested(int n) {
    if (n <= 0) return;
    lock_guard<recursive_mutex> lk(rmtx);
    cout << "nested level " << n << "\n";
    nested(n - 1);
}

void timed_worker() {
    if (tmtx.try_lock_for(chrono::milliseconds(50))) {
        cout << "got timed lock\n";
        this_thread::sleep_for(chrono::milliseconds(20));
        tmtx.unlock();
    } else {
        cout << "timed out\n";
    }
}

int main() {
    nested(3);

    tmtx.lock();
    thread t(timed_worker);
    t.join();
    tmtx.unlock();
    return 0;
}
下一节:8.3 <condition_variable> —— 条件变量

▼8.3 <condition_variable> —— 条件变量C++

是什么

📚 介绍:<condition_variable> 让线程在某个条件成立前阻塞等待,并在条件变化时被唤醒,避免忙等待浪费 CPU。

💡 比喻:它像餐厅的叫号系统——顾客拿到号后坐在一旁休息,厨师做好菜后按铃叫号,顾客才起身取餐。

⚠️ 注意:wait 必须配合 unique_lock 使用,并在 lambda 中检查条件,防止虚假唤醒;notify 应在修改完共享状态后调用。

std::condition_variable

🎯 功能:提供线程间的事件通知机制,一个或多个线程等待某个条件,另一个线程在条件满足时发出通知。

💡 比喻:像工地上的铃铛——工人们休息时等铃响,监工确认材料到位后摇铃,工人们才继续干活。

🔧 声明:class condition_variable;

↩️ 返回值:本身无返回值;通过 wait / notify 方法协调线程。

C++
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
using namespace std;

mutex mtx;
condition_variable cv;
bool ready = false;

void waiter() {
    unique_lock<mutex> lk(mtx);
    cv.wait(lk, [] { return ready; });
    cout << "waiter proceeding\n";
}

void notifier() {
    this_thread::sleep_for(chrono::milliseconds(50));
    {
        lock_guard<mutex> lk(mtx);
        ready = true;
    }
    cv.notify_one();
}

int main() {
    thread t1(waiter);
    thread t2(notifier);
    t1.join();
    t2.join();
    return 0;
}

wait / wait_for / wait_until

🎯 功能:wait() 无限阻塞直到被 notify 且条件成立;wait_for() 限时等待;wait_until() 等到指定时间点。

💡 比喻:wait 像一直睡到被叫醒;wait_for 像小睡 10 分钟;wait_until 像设了早上 7 点的闹钟。

🔧 声明:void wait(unique_lock<mutex>& lock, Predicate pred); template<class Rep, class Period, class Pred> bool wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Pred pred); template<class Clock, class Duration, class Pred> bool wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& timeout_time, Pred pred);

↩️ 返回值:带谓词的 wait_for / wait_until 在条件成立时返回 true,超时返回 false;无限 wait 无返回值。

C++
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <chrono>
using namespace std;

mutex mtx;
condition_variable cv;
bool ready = false;

void timed_waiter() {
    unique_lock<mutex> lk(mtx);
    bool ok = cv.wait_for(lk, chrono::milliseconds(100), [] { return ready; });
    cout << (ok ? "condition met\n" : "timeout\n");
}

int main() {
    thread t(timed_waiter);
    this_thread::sleep_for(chrono::milliseconds(50));
    {
        lock_guard<mutex> lk(mtx);
        ready = true;
    }
    cv.notify_one();
    t.join();
    return 0;
}

notify_one / notify_all

🎯 功能:notify_one() 唤醒一个等待线程;notify_all() 唤醒所有等待线程,通常用于广播状态变更。

💡 比喻:notify_one 像老师点一个举手的学生回答问题;notify_all 像下课铃响,所有学生一起离开教室。

🔧 声明:void notify_one() noexcept; void notify_all() noexcept;

↩️ 返回值:均无返回值;被唤醒的线程会重新获取锁并检查谓词条件。

C++
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

mutex mtx;
condition_variable cv;
bool start = false;
int count = 0;

void runner(int id) {
    unique_lock<mutex> lk(mtx);
    cv.wait(lk, [] { return start; });
    ++count;
    cout << "runner " << id << " started\n";
}

int main() {
    vector<thread> threads;
    for (int i = 0; i < 3; ++i) {
        threads.emplace_back(runner, i);
    }

    this_thread::sleep_for(chrono::milliseconds(50));
    {
        lock_guard<mutex> lk(mtx);
        start = true;
    }
    cv.notify_all();

    for (auto& t : threads) t.join();
    cout << "total started: " << count << "\n";
    return 0;
}
下一节:8.4 <future> —— 异步任务

▼8.4 <future> —— 异步任务C++

是什么

📚 介绍:<future> 提供异步任务框架,允许把计算放到后台执行,稍后再取结果,避免阻塞主线程。

💡 比喻:它像外卖平台——你下单后不会干等,商家和骑手并行准备,做好后通知你取餐。

⚠️ 注意:future::get() 只能调用一次,调用后结果会被移走;不要把 future 和 promise 的生命周期搞混,避免悬垂结果。

std::async

🎯 功能:启动一个异步任务,返回 future 对象,稍后可通过 future 获取函数返回值。

💡 比喻:像把一项任务外包给临时工,并拿到一张取货单,等需要时凭单取结果。

🔧 声明:template<class Function, class... Args> future<result_of_t<decay_t<Function>(decay_t<Args>...)>> async(Function&& f, Args&&... args);

↩️ 返回值:返回 future<T>,T 是被调用函数的返回类型;通过 .get() 阻塞并获取结果。

C++
#include <future>
#include <iostream>
#include <chrono>
using namespace std;

int compute(int x) {
    this_thread::sleep_for(chrono::milliseconds(50));
    return x * x;
}

int main() {
    future<int> f = async(launch::async, compute, 7);
    cout << "doing other work...\n";
    cout << "result: " << f.get() << "\n";
    return 0;
}

std::future / get

🎯 功能:future 代表尚未完成的异步结果;get() 阻塞等待任务完成并返回结果,只能调用一次。

💡 比喻:像一张未开奖彩票——号码已经买了,get() 就是开奖那一刻,只能开一次。

🔧 声明:template<class T> class future; T get();

↩️ 返回值:get() 返回任务结果(或 void);若任务抛出异常,异常会在 get() 处重新抛出。

C++
#include <future>
#include <iostream>
#include <stdexcept>
using namespace std;

int risky() {
    throw runtime_error("oops");
}

int main() {
    future<int> f = async(launch::async, risky);
    try {
        cout << f.get() << "\n";
    } catch (const exception& e) {
        cout << "caught: " << e.what() << "\n";
    }
    return 0;
}

std::promise

🎯 功能:promise 允许一个线程显式设置某个 future 的结果,实现线程间手动传递值或异常。

💡 比喻:像快递员在收件人地址栏里亲手填上具体门牌号,然后交给 future 去派送。

🔧 声明:template<class T> class promise; future<T> get_future(); void set_value(const T& value);

↩️ 返回值:get_future() 返回关联的 future;set_value() / set_exception() 无返回值。

C++
#include <future>
#include <thread>
#include <iostream>
using namespace std;

void producer(promise<int> p) {
    p.set_value(42);
}

int main() {
    promise<int> p;
    future<int> f = p.get_future();

    thread t(producer, move(p));
    cout << "got: " << f.get() << "\n";
    t.join();
    return 0;
}

std::packaged_task

🎯 功能:把可调用对象包装成异步任务,绑定到一个 future,可交给线程执行。

💡 比喻:像把一道菜谱和一口锅打包成礼盒,送给厨师后,你能通过礼盒上的小窗口看到成品。

🔧 声明:template<class> class packaged_task; // 特化形式如 packaged_task<int(int)>

↩️ 返回值:get_future() 返回关联 future;operator() 执行任务并设置结果。

C++
#include <future>
#include <thread>
#include <iostream>
using namespace std;

int add(int a, int b) {
    return a + b;
}

int main() {
    packaged_task<int(int, int)> task(add);
    future<int> f = task.get_future();

    thread t(move(task), 3, 4);
    cout << "3 + 4 = " << f.get() << "\n";
    t.join();
    return 0;
}
下一节:8.5 <atomic> —— 原子操作

▼8.5 <atomic> —— 原子操作C++

是什么

📚 介绍:<atomic> 提供无锁的原子类型和操作,保证多线程下对单个变量的读写不可分割,避免数据竞争。

💡 比喻:它像一台自动售票机——每次只处理一张钞票,出票和扣款是一个不可打断的整体,不会出现重复出票。

⚠️ 注意:原子操作只保护单个变量,不保护复合操作序列;复杂逻辑仍需配合 mutex;注意内存顺序参数对性能与正确性的影响。

std::atomic<T>

🎯 功能:对类型 T 提供原子操作的模板类,常用于计数器、标志位等共享状态。

💡 比喻:像银行柜台上的计数器显示屏,每位顾客看到的数字都是即时且一致的,不会读到“正在更新中”的半成品。

🔧 声明:template<class T> struct atomic;

↩️ 返回值:重载了常用算术/位运算符;构造函数只能从 T 值初始化,不能拷贝。

C++
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

atomic<int> counter{0};

void worker() {
    for (int i = 0; i < 10000; ++i) {
        ++counter;
    }
}

int main() {
    vector<thread> threads;
    for (int i = 0; i < 4; ++i) {
        threads.emplace_back(worker);
    }
    for (auto& t : threads) t.join();
    cout << "counter: " << counter << "\n";
    return 0;
}

load / store / exchange

🎯 功能:load() 原子读取值;store() 原子写入值;exchange() 原子替换并返回旧值。

💡 比喻:load 像从保险柜取钱看一眼;store 像把现金放进保险柜;exchange 像用新钞票换出旧钞票。

🔧 声明:T load(memory_order order = memory_order_seq_cst) const noexcept; void store(T desired, memory_order order = memory_order_seq_cst) noexcept; T exchange(T desired, memory_order order = memory_order_seq_cst) noexcept;

↩️ 返回值:load 返回当前值;store 无返回值;exchange 返回旧值。

C++
#include <atomic>
#include <iostream>
using namespace std;

int main() {
    atomic<int> value{10};

    int v = value.load();
    cout << "loaded: " << v << "\n";

    value.store(20);
    cout << "stored: " << value.load() << "\n";

    int old = value.exchange(30);
    cout << "old: " << old << ", new: " << value.load() << "\n";
    return 0;
}

fetch_add / fetch_sub / compare_exchange_strong

🎯 功能:fetch_add / fetch_sub 原子加减并返回旧值;compare_exchange_strong 实现 CAS 乐观锁。

💡 比喻:fetch_add 像自动计数器按一下加一并显示刚才的数;CAS 像核对指纹后才换锁,指纹不对就重来。

🔧 声明:T fetch_add(T arg, memory_order order = memory_order_seq_cst) noexcept; T fetch_sub(T arg, memory_order order = memory_order_seq_cst) noexcept; bool compare_exchange_strong(T& expected, T desired, memory_order order = memory_order_seq_cst) noexcept;

↩️ 返回值:fetch_add / fetch_sub 返回操作前的旧值;compare_exchange_strong 成功返回 true,失败时把当前值写回 expected。

C++
#include <atomic>
#include <iostream>
using namespace std;

int main() {
    atomic<int> a{5};
    int old = a.fetch_add(3);
    cout << "old: " << old << ", now: " << a.load() << "\n";

    int expected = 8;
    bool ok = a.compare_exchange_strong(expected, 10);
    cout << "cas ok: " << ok << ", value: " << a.load() << "\n";

    expected = 99;
    ok = a.compare_exchange_strong(expected, 20);
    cout << "cas ok: " << ok << ", expected after fail: " << expected << "\n";
    return 0;
}
下一节:8.6 <shared_mutex> —— 读写锁(C++14/17)

▼8.6 <shared_mutex> —— 读写锁(C++14/17)C++

是什么

📚 介绍:<shared_mutex> 提供读写锁:多个读线程可同时持有共享锁,写线程独占锁,适合读多写少的场景。

💡 比喻:它像图书馆的阅览室——很多读者可以同时进去看书,但整理书架时管理员会清场,等整理完再开放。

⚠️ 注意:C++14 引入 shared_timed_mutex,C++17 引入 shared_mutex;写锁与共享锁不能同时被不同线程持有,否则死锁。

std::shared_mutex

🎯 功能:读写互斥量,支持 shared_lock(共享读锁)和 unique_lock(独占写锁)。

💡 比喻:像一扇双模式门:日常许多人可以一起“刷卡只读”进入;打扫卫生时一人独占,其他人全部在外等候。

🔧 声明:class shared_mutex; // C++17

↩️ 返回值:lock() / unlock() 提供独占锁;lock_shared() / unlock_shared() 提供共享锁。

C++
#include <shared_mutex>
#include <mutex>
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

shared_mutex smtx;
int data = 0;

void reader(int id) {
    shared_lock<shared_mutex> lk(smtx);
    cout << "reader " << id << " sees " << data << "\n";
}

void writer(int value) {
    unique_lock<shared_mutex> lk(smtx);
    data = value;
    cout << "writer set " << data << "\n";
}

int main() {
    vector<thread> readers;
    for (int i = 0; i < 3; ++i) {
        readers.emplace_back(reader, i);
    }
    thread w(writer, 42);

    for (auto& t : readers) t.join();
    w.join();
    return 0;
}

std::shared_lock

🎯 功能:RAII 共享锁包装,与 shared_mutex 配合,允许多个线程同时获取读锁。

💡 比喻:像一张团体参观券——许多持券人可以同时入场参观,但 everyone 都得遵守只看不摸的规则。

🔧 声明:template<class Mutex> class shared_lock;

↩️ 返回值:无返回值;构造时获取共享锁,析构时释放。

C++
#include <shared_mutex>
#include <thread>
#include <iostream>
#include <vector>
using namespace std;

shared_mutex smtx;
int shared_data = 100;

void reader(int id) {
    shared_lock<shared_mutex> lk(smtx);
    cout << "reader " << id << ": " << shared_data << "\n";
}

int main() {
    vector<thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(reader, i);
    }
    for (auto& t : threads) t.join();
    return 0;
}

unique_lock(与 shared_mutex 配合)

🎯 功能:用 unique_lock 包裹 shared_mutex 可获得独占写锁,阻塞所有读线程直到写锁释放。

💡 比喻:像闭馆维护——管理员贴上“装修中”告示并锁门,所有读者被挡在外面,维护完再开门迎客。

🔧 声明:template<class Mutex> class unique_lock; // 与 shared_mutex 组合使用

↩️ 返回值:无返回值;独占锁释放后,等待的 shared_lock 线程才能继续获取共享锁。

C++
#include <shared_mutex>
#include <thread>
#include <iostream>
#include <vector>
#include <chrono>
using namespace std;

shared_mutex smtx;
int cache = 0;
bool updated = false;

void reader(int id) {
    shared_lock<shared_mutex> lk(smtx);
    cout << "reader " << id << " cache=" << cache << "\n";
}

void updater() {
    this_thread::sleep_for(chrono::milliseconds(50));
    unique_lock<shared_mutex> lk(smtx);
    cache = 42;
    updated = true;
    cout << "updater wrote " << cache << "\n";
}

int main() {
    vector<thread> readers;
    for (int i = 0; i < 3; ++i) {
        readers.emplace_back(reader, i);
    }
    thread u(updater);

    for (auto& t : readers) t.join();
    u.join();
    return 0;
}
下一节:9.1 <filesystem> —— 文件系统(C++17)

▼9.1 <functional> —— 函数包装与绑定C++

是什么

📚 介绍:<functional> 提供了一套 callable 对象的“通用接口”,包括函数包装器 std::function、参数绑定 std::bind、占位符以及引用包装器,让不同的可调用对象能够以统一方式传递和存储。

💡 比喻:它像一组国际通用的电源转接头——无论电器插头是美标、欧标还是英标,插上转接头后都能接到同一个插座上。

⚠️ 注意:std::function 内部有类型擦除开销,高频调用时应避免滥用;把 lambda 存入 function 时不要捕获局部变量的引用,否则对象析构后会产生悬垂引用。

std::function

🎯 功能:通用函数包装器,可以保存函数指针、lambda、bind 表达式、函数对象等任意可调用实体,并统一通过 operator() 调用。

💡 比喻:像一支万能笔筒——无论圆珠笔、钢笔还是马克笔,外形不同,但插进笔筒后都能按同一种方式取出来写字。

🔧 声明:template<class> class function; // 常用形式如 function<int(int,int)>

↩️ 返回值:本身无返回值;operator() 的返回类型与被包装函数相同。

C++
#include <functional>
#include <iostream>
using namespace std;

int add(int a, int b) { return a + b; }

struct Multiplier {
    int factor;
    explicit Multiplier(int f) : factor(f) {}
    int operator()(int x) const { return x * factor; }
};

int main() {
    function<int(int, int)> f1 = add;
    function<int(int)> f2 = Multiplier(3);
    function<int(int, int)> f3 = [](int x, int y) { return x - y; };

    cout << "add: " << f1(2, 3) << "\n";
    cout << "multiply: " << f2(4) << "\n";
    cout << "lambda: " << f3(10, 4) << "\n";
    return 0;
}

std::bind

🎯 功能:把函数与部分参数预先绑定,生成一个新的可调用对象,剩余参数稍后传入。

💡 比喻:像一台已经调好参数的咖啡机——你只需要把杯子放上去,它自动按预设配方出咖啡。

🔧 声明:template<class F, class... Args> /*unspecified*/ bind(F&& f, Args&&... args);

↩️ 返回值:返回一个未指定的函数对象(通常可存入 std::function),调用时执行原函数。

C++
#include <functional>
#include <iostream>
using namespace std;

int calc(int a, int b, int c) {
    return a * 100 + b * 10 + c;
}

int main() {
    auto f = bind(calc, 1, placeholders::_1, placeholders::_2);
    cout << "bound: " << f(2, 3) << "\n";

    auto g = bind(calc, placeholders::_2, 5, placeholders::_1);
    cout << "reordered: " << g(7, 9) << "\n";
    return 0;
}

lambda(与 functional 结合使用)

🎯 功能:lambda 表达式可以直接赋值给 std::function,作为轻量级匿名函数对象使用,捕获列表决定它能访问哪些外部变量。

💡 比喻:像一张即写即用的便利贴——不用专门写函数声明,随手写几行就能贴进代码里反复使用。

🔧 声明:[capture](params) -> return_type { body };

↩️ 返回值:lambda 的返回类型通常自动推导;存入 std::function 后,operator() 返回其定义的结果。

C++
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    int offset = 10;

    function<int(int)> add_offset = [offset](int x) { return x + offset; };
    for (int x : v) {
        cout << add_offset(x) << " ";
    }
    cout << "\n";

    int sum = 0;
    for_each(v.begin(), v.end(), [&sum](int x) { sum += x; });
    cout << "sum: " << sum << "\n";
    return 0;
}

placeholders

🎯 功能:std::placeholders 提供 _1、_2……等占位符,表示 bind 生成的新函数在调用时接收的第几个参数。

💡 比喻:像一份预留空格的申请表——_1、_2 就是留给你最后填写姓名的空行,其他栏目已经预先填好。

🔧 声明:namespace placeholders { /*unspecified*/ _1, _2, _3, ...; }

↩️ 返回值:占位符本身不返回值,只是用于构造 bind 表达式。

C++
#include <functional>
#include <iostream>
using namespace std;

int power(int base, int exp) {
    int r = 1;
    for (int i = 0; i < exp; ++i) r *= base;
    return r;
}

int main() {
    auto square = bind(power, placeholders::_1, 2);
    auto cube = bind(power, placeholders::_1, 3);

    cout << "square(4): " << square(4) << "\n";
    cout << "cube(2): " << cube(2) << "\n";
    return 0;
}

std::ref / std::cref

🎯 功能:引用包装器,用于把对象按引用传入 std::bind 或 std::function,避免不必要的拷贝,同时保持可修改或只读语义。

💡 比喻:像给文件袋贴上一张“请勿复印,原件请亲手交接”的标签——bind 看到后会传递原对象本身。

🔧 声明:template<class T> reference_wrapper<T> ref(T& t) noexcept; template<class T> reference_wrapper<const T> cref(const T& t) noexcept;

↩️ 返回值:返回 reference_wrapper,隐式转换为 T& 或 const T&;解引用用 .get()。

C++
#include <functional>
#include <iostream>
using namespace std;

void increment(int& x) { ++x; }

void print(const int& x) { cout << "value: " << x << "\n"; }

int main() {
    int n = 10;

    auto f = bind(increment, ref(n));
    f();
    cout << "after increment: " << n << "\n";

    auto g = bind(print, cref(n));
    g();
    return 0;
}
下一节:9.2 <type_traits> —— 类型特征(C++11)

▼9.2 <type_traits> —— 类型特征(C++11)C++

是什么

📚 介绍:<type_traits> 提供编译期类型查询和变换工具,让模板代码能够根据类型的属性(如是否为整数、是否可复制)做分支决策,实现更泛型、更安全的程序。

💡 比喻:它像工厂里的自动分拣扫描仪——货物还没上流水线,扫描仪就已经读出每件货物的材质、尺寸,从而决定用哪条传送带。

⚠️ 注意:type_traits 的结果都是编译期常量,不能替代运行时检查;滥用 enable_if / SFINAE 会让编译错误信息变得晦涩,必要时可用 if constexpr(C++17)简化。

is_integral / is_floating_point / is_arithmetic

🎯 功能:is_integral 判断类型是否为整数类型;is_floating_point 判断是否为浮点类型;is_arithmetic 综合判断是否为整数或浮点。

💡 比喻:像给每个数据贴一张“整数”“小数”“不是数”的分类标签,模板根据标签选择不同算法。

🔧 声明:template<class T> struct is_integral; template<class T> struct is_floating_point; template<class T> struct is_arithmetic;

↩️ 返回值:value 成员为 bool 类型常量,true 表示条件成立。

C++
#include <type_traits>
#include <iostream>
using namespace std;

template<class T>
void check_number() {
    cout << "int: " << is_integral<T>::value
         << ", float: " << is_floating_point<T>::value
         << ", arithmetic: " << is_arithmetic<T>::value << "\n";
}

int main() {
    check_number<int>();
    check_number<double>();
    check_number<void*>();
    return 0;
}

is_pointer / is_reference / is_array

🎯 功能:is_pointer 判断是否为指针类型;is_reference 判断是否为引用类型;is_array 判断是否为数组类型。

💡 比喻:像安全检查站根据形状识别包裹:细长的是指针、双头的是引用、成排的是数组。

🔧 声明:template<class T> struct is_pointer; template<class T> struct is_reference; template<class T> struct is_array;

↩️ 返回值:value 成员为 bool 类型常量。

C++
#include <type_traits>
#include <iostream>
using namespace std;

int main() {
    cout << boolalpha;
    cout << "int* is pointer: " << is_pointer<int*>::value << "\n";
    cout << "int& is reference: " << is_reference<int&>::value << "\n";
    cout << "int[] is array: " << is_array<int[]>::value << "\n";
    cout << "int is array: " << is_array<int>::value << "\n";
    return 0;
}

is_same / is_base_of / is_convertible

🎯 功能:is_same 判断两个类型是否相同;is_base_of 判断一个类型是否为另一个类型的基类;is_convertible 判断是否存在隐式转换。

💡 比喻:像户籍系统比对身份证号、查家谱、看能否把一种证件换领成另一种。

🔧 声明:template<class T, class U> struct is_same; template<class Base, class Derived> struct is_base_of; template<class From, class To> struct is_convertible;

↩️ 返回值:value 成员为 bool 类型常量。

C++
#include <type_traits>
#include <iostream>
using namespace std;

struct Base {};
struct Derived : Base {};

int main() {
    cout << boolalpha;
    cout << "int same as long: " << is_same<int, long>::value << "\n";
    cout << "Base of Derived: " << is_base_of<Base, Derived>::value << "\n";
    cout << "Derived* -> Base*: " << is_convertible<Derived*, Base*>::value << "\n";
    return 0;
}

enable_if / conditional

🎯 功能:enable_if 在条件为真时才产生有效类型,常用于 SFINAE 重载控制;conditional 根据布尔条件在两种类型之间二选一。

💡 比喻:enable_if 像一道自动闸门,条件不满足时模板候选就被挡在门外;conditional 像双刀开关,拨向左边选 A,拨向右边选 B。

🔧 声明:template<bool B, class T = void> struct enable_if; template<bool B, class T, class F> struct conditional;

↩️ 返回值:enable_if::type 为 T(B 为 true 时),否则无 type 成员;conditional::type 为 T 或 F。

C++
#include <type_traits>
#include <iostream>
using namespace std;

template<class T>
typename enable_if<is_integral<T>::value, T>::type
cube(T x) { return x * x * x; }

template<class T>
typename enable_if<is_floating_point<T>::value, T>::type
cube(T x) { return x * x * x; }

int main() {
    cout << "int cube: " << cube(3) << "\n";
    cout << "double cube: " << cube(2.5) << "\n";

    conditional<sizeof(int) >= 8, long long, int>::type chosen;
    cout << "chosen size: " << sizeof(chosen) << "\n";
    return 0;
}

remove_reference / decay / add_const

🎯 功能:remove_reference 去掉类型的引用修饰;decay 模拟按值传参时的类型退化(去引用、去 const、数组转指针、函数转指针);add_const 给类型加上 const。

💡 比喻:像一台类型“卸妆仪”——把引用、const、数组等化妆品一层层卸掉,露出最基础的类型。

🔧 声明:template<class T> struct remove_reference; template<class T> struct decay; template<class T> struct add_const;

↩️ 返回值:::type 成员为转换后的类型。

C++
#include <type_traits>
#include <iostream>
using namespace std;

int main() {
    cout << boolalpha;
    cout << "remove_reference<int&>: "
         << is_same<remove_reference<int&>::type, int>::value << "\n";
    cout << "decay<int const&>: "
         << is_same<decay<int const&>::type, int>::value << "\n";
    cout << "decay<int[3]>: "
         << is_same<decay<int[3]>::type, int*>::value << "\n";
    cout << "add_const<int>: "
         << is_same<add_const<int>::type, const int>::value << "\n";
    return 0;
}
下一节:9.3 <utility> —— 通用工具

▼9.3 <utility> —— 通用工具C++

是什么

📚 介绍:<utility> 是 C++ 标准库的“小工具箱”,提供 pair、make_pair、move、forward、swap、exchange 等基础组件,被容器、算法和元编程广泛依赖。

💡 比喻:它像厨房里那一抽屉零碎但离不开的小工具——削皮器、量勺、开瓶器,单独看都不起眼,但每道菜背后可能都在用。

⚠️ 注意:std::move 只是转换为右值引用,并不会真正移动对象;真正移动需要类型定义移动构造函数。forward 只能用于万能引用(T&&)参数,否则会导致意外拷贝或引用悬垂。

std::pair

🎯 功能:把两个不同类型的值组合成一个整体,first 和 second 分别访问第一个、第二个元素,常用于 map 的键值对。

💡 比喻:像一双分左右脚的袜子——虽然两只不同,但出厂时就是一对,缺一只就不好穿。

🔧 声明:template<class T1, class T2> struct pair;

↩️ 返回值:无返回值;通过 .first / .second 访问成员。

C++
#include <utility>
#include <iostream>
#include <string>
using namespace std;

int main() {
    pair<int, string> id_name{42, "alice"};
    cout << "id: " << id_name.first << ", name: " << id_name.second << "\n";

    id_name.first = 100;
    id_name.second = "bob";
    cout << "updated: " << id_name.first << ", " << id_name.second << "\n";
    return 0;
}

std::make_pair

🎯 功能:根据传入的实参自动推导 pair 的两个类型,简化 pair 对象的构造。

💡 比喻:像自动打包机——你把两样东西放进去,它自动识别尺寸并封好一对盒子。

🔧 声明:template<class T1, class T2> pair<V1, V2> make_pair(T1&& x, T2&& y);

↩️ 返回值:返回一个 pair,类型由模板实参推导决定。

C++
#include <utility>
#include <iostream>
#include <string>
using namespace std;

int main() {
    auto p = make_pair(3.14, string("pi"));
    cout << "first: " << p.first << ", second: " << p.second << "\n";

    int a = 7;
    auto q = make_pair(ref(a), 'x');
    q.first.get() = 99;
    cout << "a: " << a << "\n";
    return 0;
}

std::move / std::forward

🎯 功能:move 把左值转换为右值引用,提示编译器可以“窃取”资源;forward 在模板中按原始值类别完美转发参数,避免额外拷贝。

💡 比喻:move 像贴上一张“此物可回收,请拆走可用零件”的标签;forward 像一位严谨的快递中转员,来者是什么包装,就原样交给下一站。

🔧 声明:template<class T> constexpr remove_reference_t<T>&& move(T&& t) noexcept; template<class T> constexpr T&& forward(remove_reference_t<T>& t) noexcept;

↩️ 返回值:move 返回右值引用;forward 返回原值类别的引用。

C++
#include <utility>
#include <iostream>
#include <vector>
#include <string>
using namespace std;

template<class T>
void wrapper(T&& arg) {
    T local = forward<T>(arg);
    cout << "local size: " << local.size() << "\n";
}

int main() {
    vector<int> v{1, 2, 3, 4, 5};
    vector<int> moved = move(v);
    cout << "moved size: " << moved.size() << ", original size: " << v.size() << "\n";

    string s = "hello";
    wrapper(s);
    wrapper(move(s));
    return 0;
}

std::swap

🎯 功能:交换两个对象的值,通常调用类型的移动/拷贝构造函数和赋值运算符;标准容器也都提供 swap 成员函数。

💡 比喻:像两人互换座位——你把行李搬到对方位置,对方搬过来,过程不额外占用一整排新座位。

🔧 声明:template<class T> void swap(T& a, T& b) noexcept(/*...*/);

↩️ 返回值:无返回值;交换后 a 存放原 b 的值,b 存放原 a 的值。

C++
#include <utility>
#include <iostream>
#include <string>
using namespace std;

int main() {
    int a = 10, b = 20;
    swap(a, b);
    cout << "a: " << a << ", b: " << b << "\n";

    string x = "apple", y = "banana";
    swap(x, y);
    cout << "x: " << x << ", y: " << y << "\n";
    return 0;
}

std::exchange(C++14)

🎯 功能:把对象设置为新值,并返回它原来的旧值,常用于实现移动赋值或原子地更新状态。

💡 比喻:像以旧换新活动——你把旧手机交给店员,店员立刻给你一部新手机,并把你原来的旧手机拿走。

🔧 声明:template<class T, class U = T> constexpr T exchange(T& obj, U&& new_value);

↩️ 返回值:返回 obj 的旧值(按值移动)。

C++
#include <utility>
#include <iostream>
#include <string>
using namespace std;

int main() {
    int n = 5;
    int old_n = exchange(n, 100);
    cout << "old: " << old_n << ", new: " << n << "\n";

    string s = "old text";
    string prev = exchange(s, "new text");
    cout << "prev: " << prev << ", current: " << s << "\n";
    return 0;
}
下一节:9.4 <tuple> —— 元组(C++11)

▼9.4 <tuple> —— 元组(C++11)C++

是什么

📚 介绍:<tuple> 提供固定大小、元素类型可 heterogeneous(异构)的序列容器,可以把任意多个不同类型的值打包成一个对象,并通过编译期索引访问。

💡 比喻:它像一个带有编号抽屉的多功能工具箱——每个抽屉形状不同,但你知道第 0 号抽屉是扳手、第 1 号是螺丝刀,不用贴标签也能准确拿到。

⚠️ 注意:get<I> 的索引必须是编译期常量,越界会在编译期报错;C++17 起可用结构化绑定(auto [a,b,c] = t;)更直观地拆包。

tuple 构造函数

🎯 功能:tuple 是固定长度、元素类型各异的序列,可以用构造函数显式初始化,每个元素类型在模板参数列表中指定。

💡 比喻:像定制一个多格收纳盒——你在设计图上标明每一格的大小和材质,工厂按图做出一个整体盒子。

🔧 声明:template<class... Types> class tuple; // tuple<int, double, string> t(1, 2.5, "ok");

↩️ 返回值:构造后可通过 get<I>(t) 访问元素。

C++
#include <tuple>
#include <iostream>
#include <string>
using namespace std;

int main() {
    tuple<int, double, string> t(7, 3.14, "hello");
    cout << "get<0>: " << get<0>(t) << "\n";
    cout << "get<1>: " << get<1>(t) << "\n";
    cout << "get<2>: " << get<2>(t) << "\n";
    return 0;
}

std::make_tuple

🎯 功能:自动推导元素类型并构造 tuple,避免手写冗长的模板参数列表。

💡 比喻:像自助打包机——你把东西递过去,它自动识别每件物品的类型并封装好。

🔧 声明:template<class... Types> tuple<VTypes...> make_tuple(Types&&... args);

↩️ 返回值:返回一个元素类型已推导的 tuple。

C++
#include <tuple>
#include <iostream>
#include <string>
using namespace std;

int main() {
    auto t = make_tuple(42, 2.71, string("world"));
    cout << "size: " << tuple_size<decltype(t)>::value << "\n";
    cout << "items: " << get<0>(t) << ", " << get<1>(t) << ", " << get<2>(t) << "\n";
    return 0;
}

std::get

🎯 功能:通过编译期索引 I 访问 tuple 的第 I 个元素,返回对应类型的引用。

💡 比喻:像按编号取快递柜的格子——输入 0、1、2,柜门就会打开对应的那一格。

🔧 声明:template<size_t I, class... Types> constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>& t) noexcept;

↩️ 返回值:返回第 I 个元素的引用,类型由 tuple_element_t 决定。

C++
#include <tuple>
#include <iostream>
#include <string>
using namespace std;

int main() {
    tuple<int, double, string> t(1, 2.0, "three");
    get<0>(t) = 100;
    get<2>(t) = "updated";

    cout << get<0>(t) << " " << get<1>(t) << " " << get<2>(t) << "\n";
    return 0;
}

std::tuple_size / std::tuple_element

🎯 功能:tuple_size 获取 tuple 的元素个数;tuple_element 获取指定位置元素的类型。

💡 比喻:像读取工具箱的规格表——“共有 3 格”和“第 2 格专放螺丝刀”。

🔧 声明:template<class T> struct tuple_size; // tuple_size<tuple<...>>::value template<size_t I, class T> struct tuple_element;

↩️ 返回值:tuple_size::value 为 size_t 常量;tuple_element<I, T>::type 为第 I 个元素的类型。

C++
#include <tuple>
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;

int main() {
    using T = tuple<int, double, string>;
    cout << "tuple_size: " << tuple_size<T>::value << "\n";

    using Second = tuple_element<1, T>::type;
    cout << "second type is double: " << is_same<Second, double>::value << "\n";
    return 0;
}

std::tie / std::forward_as_tuple

🎯 功能:tie 用现有变量“ unpack ”tuple 内容;forward_as_tuple 按原值类别转发参数构造 tuple,常用于返回多个值或函数参数打包。

💡 比喻:tie 像把打包好的工具一件件摆回桌面;forward_as_tuple 像把桌上的东西原样装进袋子,继续传给下一个人。

🔧 声明:template<class... Types> tuple<Types&...> tie(Types&&... args) noexcept; template<class... Types> tuple<Types&&...> forward_as_tuple(Types&&... args) noexcept;

↩️ 返回值:tie 返回由参数引用组成的 tuple;forward_as_tuple 返回按引用类别转发的 tuple。

C++
#include <tuple>
#include <iostream>
#include <string>
using namespace std;

tuple<int, double, string> make_data() {
    return forward_as_tuple(1, 2.5, string("ok"));
}

int main() {
    int a;
    double b;
    string c;
    tie(a, b, c) = make_data();
    cout << a << " " << b << " " << c << "\n";

    int x = 10;
    auto t = tie(x);
    get<0>(t) = 20;
    cout << "x: " << x << "\n";
    return 0;
}
下一节:9.5 <variant> —— 类型安全联合体(C++17)

▼9.5 <variant> —— 类型安全联合体(C++17)C++

是什么

📚 介绍:<variant> 是 C++17 引入的类型安全联合体,允许一个变量在任意时刻保存若干预定类型中的一种,并记录当前保存的是哪一种,避免传统 union 的类型混淆和未定义行为。

💡 比喻:它像一把智能工具手柄——可以换上螺丝刀头、扳手头或钻头,手柄上的显示屏会实时告诉你当前装的是哪一头,绝不会让你用钻头去拧螺丝。

⚠️ 注意:访问 variant 前要用 holds_alternative 或 get_if 确认当前类型,否则 get 类型不匹配会抛 std::bad_variant_access;visit 必须覆盖所有替代类型。

variant 构造函数

🎯 功能:variant 在任一时刻保存 Types 中的一种,默认构造时使用第一个类型(必须可默认构造),也可以从任一允许的类型初始化。

💡 比喻:像一个多用途充电口——你可以插 USB-A、USB-C 或 Lightning,但同一时间只能插一个头。

🔧 声明:template<class... Types> class variant;

↩️ 返回值:构造后对象处于“持有某类型值”的状态;通过 emplace 或赋值可切换当前类型。

C++
#include <variant>
#include <iostream>
#include <string>
using namespace std;

int main() {
    variant<int, double, string> v;
    v = 3.14;
    cout << "holds double: " << holds_alternative<double>(v) << "\n";

    v = "hello variant";
    cout << "holds string: " << holds_alternative<string>(v) << "\n";

    variant<int, string> w(in_place_type<string>, "direct");
    cout << "w string: " << get<string>(w) << "\n";
    return 0;
}

std::get / std::get_if

🎯 功能:get / get 按类型或索引访问当前值,类型不匹配时抛异常;get_if 返回指针,类型不匹配时返回 nullptr,更安全。

💡 比喻:get 像直接掀开盖子拿东西,拿错格子会触发警报;get_if 像先用探测仪扫描,拿错了也只是指示灯变红。

🔧 声明:template<class T, class... Types> constexpr T& get(variant<Types...>& v); template<class T, class... Types> constexpr T* get_if(variant<Types...>* pv) noexcept;

↩️ 返回值:get 返回当前值的引用;get_if 成功返回指针,失败返回 nullptr。

C++
#include <variant>
#include <iostream>
#include <string>
using namespace std;

int main() {
    variant<int, double, string> v = 42;
    cout << "get<int>: " << get<int>(v) << "\n";

    v = string("text");
    if (auto p = get_if<string>(&v)) {
        cout << "get_if string: " << *p << "\n";
    }

    if (!get_if<double>(&v)) {
        cout << "not a double right now\n";
    }
    return 0;
}

std::holds_alternative

🎯 功能:检查 variant 当前是否持有某个指定类型,返回布尔值,常用于类型安全的分支判断。

💡 比喻:像检查工具手柄显示屏——先看当前装的是不是螺丝刀头,再决定往哪边拧。

🔧 声明:template<class T, class... Types> constexpr bool holds_alternative(const variant<Types...>& v) noexcept;

↩️ 返回值:true 表示当前持有类型 T,false 表示不是。

C++
#include <variant>
#include <iostream>
#include <string>
using namespace std;

int main() {
    variant<int, double, string> v = 3.14;

    if (holds_alternative<double>(v)) {
        cout << "v is double: " << get<double>(v) << "\n";
    }

    v = 100;
    if (holds_alternative<int>(v)) {
        cout << "v is int: " << get<int>(v) << "\n";
    }
    return 0;
}

std::visit

🎯 功能:对 variant 当前持有的值应用一个可调用对象,编译器会自动把所有可能的类型都生成一份调用分支,要求处理函数覆盖全部类型。

💡 比喻:像自动分拣机器人——它不管当前来的是螺丝刀头、扳手头还是钻头,都会调用对应的处理程序。

🔧 声明:template<class Visitor, class... Variants> constexpr /*see below*/ visit(Visitor&& vis, Variants&&... vars);

↩️ 返回值:返回 Visitor 的返回类型;所有 variant 的当前类型必须都有对应的重载。

C++
#include <variant>
#include <iostream>
#include <string>
using namespace std;

struct Printer {
    void operator()(int x) const { cout << "int: " << x << "\n"; }
    void operator()(double x) const { cout << "double: " << x << "\n"; }
    void operator()(const string& s) const { cout << "string: " << s << "\n"; }
};

int main() {
    variant<int, double, string> v = "hello";
    visit(Printer{}, v);

    v = 7;
    visit(Printer{}, v);
    return 0;
}
下一节:9.6 <any> —— 任意类型容器(C++17)

▼9.6 <any> —— 任意类型容器(C++17)C++

是什么

📚 介绍:<any> 提供一种类型擦除容器,可以保存任意单个对象,并在之后按原类型取出;它不要求类型列表在编译期确定,比 variant 更灵活但运行时开销更大。

💡 比喻:它像一个不透明的保险箱——你可以把任何贵重物品放进去,但取出来的时候必须说出正确密码(类型),否则打不开。

⚠️ 注意:any_cast 类型不匹配会抛 std::bad_any_cast;存放引用时要使用 std::ref 包装;若类型集已知,优先使用 std::variant 以获得更好性能和类型安全。

std::any 构造函数

🎯 功能:any 对象可以默认构造(为空),也可以用任意可复制类型的值构造或赋值。

💡 比喻:像一个空保鲜盒——你可以不放东西,也可以放苹果、鸡蛋或牛奶,但一次只能放一样东西。

🔧 声明:class any; any() noexcept; template<class ValueType> any(ValueType&& value);

↩️ 返回值:构造后 any 处于空状态或持有传入值的副本;赋值时会替换原有内容。

C++
#include <any>
#include <iostream>
#include <string>
using namespace std;

int main() {
    any a;
    cout << "empty: " << !a.has_value() << "\n";

    a = 42;
    cout << "holds int: " << a.has_value() << "\n";

    a = string("stored string");
    cout << "type: " << a.type().name() << "\n";
    return 0;
}

std::any_cast

🎯 功能:按类型从 any 中取出值,可以返回副本、引用或指针;类型不匹配时抛异常或返回 nullptr。

💡 比喻:像取保险箱里的东西——说对名字就拿走,说错名字保险箱报警。

🔧 声明:template<class T> T any_cast(const any& operand); template<class T> T* any_cast(any* operand) noexcept;

↩️ 返回值:按值版本返回副本;按指针版本成功返回指针,失败返回 nullptr;引用版本失败抛 std::bad_any_cast。

C++
#include <any>
#include <iostream>
#include <string>
using namespace std;

int main() {
    any a = 3.14;

    try {
        double d = any_cast<double>(a);
        cout << "double: " << d << "\n";
    } catch (const bad_any_cast& e) {
        cout << "cast failed\n";
    }

    a = string("hello");
    if (auto p = any_cast<string>(&a)) {
        cout << "string ptr: " << *p << "\n";
    }

    try {
        int x = any_cast<int>(a);
        cout << x << "\n";
    } catch (const bad_any_cast&) {
        cout << "not an int\n";
    }
    return 0;
}

std::any::reset / has_value / type

🎯 功能:reset 清空 any 的内容;has_value 判断是否持有对象;type 返回 type_info,可用于运行时类型检查。

💡 比喻:reset 像把保鲜盒清空;has_value 像摇晃盒子听有没有东西;type 像查看盒盖上贴的标签。

🔧 声明:void reset(); bool has_value() const noexcept; const type_info& type() const noexcept;

↩️ 返回值:reset 无返回值;has_value 返回 bool;type 返回 std::type_info 引用。

C++
#include <any>
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;

int main() {
    any a = 100;
    cout << "has value: " << a.has_value() << "\n";
    cout << "type: " << a.type().name() << "\n";

    a.reset();
    cout << "after reset: " << a.has_value() << "\n";

    a = string("abc");
    if (a.type() == typeid(string)) {
        cout << "it is a string\n";
    }
    return 0;
}
下一节:9.7 <optional> —— 可选值(C++17)

▼9.7 <optional> —— 可选值(C++17)C++

是什么

📚 介绍:<optional> 提供一种明确表达“值可能存在也可能不存在”的包装类型,避免了用特殊值(如 -1、nullptr)表示缺失时带来的歧义和错误。

💡 比喻:它像一个透明的“可能空”快递盒——盒子上明确写着“可能没货”,你不用拆开猜里面是 0 还是真的没有东西。

⚠️ 注意:在 optional 为空时调用 value() 会抛 std::bad_optional_access;应优先使用 has_value()、value_or() 或 if (opt) 进行安全检查。

optional 构造函数

🎯 功能:optional 可以构造为空(std::nullopt),也可以包装一个 T 类型的值;为空时不分配 T 对象。

💡 比喻:像一个可以贴“暂无内容”标签的相框——你可以放照片,也可以先空着,比塞一张空白纸更清楚。

🔧 声明:template<class T> class optional; optional() noexcept; optional(const T& value);

↩️ 返回值:构造后 optional 处于空状态或有值状态;可用 operator bool 或 has_value 检查。

C++
#include <optional>
#include <iostream>
#include <string>
using namespace std;

int main() {
    optional<int> empty_opt;
    optional<int> val_opt = 42;
    optional<string> msg = "hello";

    cout << "empty? " << !empty_opt << "\n";
    cout << "val? " << val_opt.has_value() << "\n";
    cout << "msg: " << *msg << "\n";
    return 0;
}

std::optional::has_value / value / value_or

🎯 功能:has_value 判断是否有值;value 返回引用并在空时抛异常;value_or 在空时返回默认值。

💡 比喻:has_value 像问“盒子里有东西吗”;value 像直接拿,空了会摔一跤;value_or 像自带备用方案,空了就拿默认值。

🔧 声明:constexpr bool has_value() const noexcept; constexpr T& value() &; constexpr T value_or(U&& default_value) const&;

↩️ 返回值:has_value 返回 bool;value 返回 T&;value_or 返回 T 的副本(或默认值)。

C++
#include <optional>
#include <iostream>
#include <string>
using namespace std;

optional<int> maybe_sqrt(int x) {
    if (x >= 0) return x * x;
    return nullopt;
}

int main() {
    auto a = maybe_sqrt(3);
    auto b = maybe_sqrt(-1);

    if (a.has_value()) {
        cout << "a value: " << a.value() << "\n";
    }

    cout << "b or 0: " << b.value_or(0) << "\n";

    try {
        cout << b.value() << "\n";
    } catch (const bad_optional_access&) {
        cout << "b has no value\n";
    }
    return 0;
}

std::optional::reset / emplace

🎯 功能:reset 销毁当前值并把 optional 置为空;emplace 原地构造一个新值,常用于延迟初始化或替换内容。

💡 比喻:reset 像把相框里的照片取出来并贴上“空”标签;emplace 像直接在现场打印一张新照片并装进相框。

🔧 声明:void reset() noexcept; template<class... Args> T& emplace(Args&&... args);

↩️ 返回值:reset 无返回值;emplace 返回新构造值的引用。

C++
#include <optional>
#include <iostream>
#include <string>
using namespace std;

struct Record {
    int id;
    string name;
    Record(int i, string n) : id(i), name(move(n)) {}
};

int main() {
    optional<Record> rec;
    rec.emplace(1, "alice");
    cout << "id: " << rec->id << ", name: " << rec->name << "\n";

    rec.reset();
    cout << "after reset: " << rec.has_value() << "\n";

    rec.emplace(2, "bob");
    cout << "id: " << rec->id << ", name: " << rec->name << "\n";
    return 0;
}
下一节:10.1 <exception> —— 异常基类

▼10.1 <exception> —— 异常基类C++

是什么

📚 介绍:<exception> 定义了 C++ 异常机制的根基——std::exception 是所有标准异常类的公共基类。它规定了异常对象的基本接口,使得代码可以用统一的 catch (const exception&) 捕获并处理各种错误。

💡 比喻:它像所有快递异常单的统一模板——无论是包裹丢失、地址错误还是拒收,都会有一张带“异常编号”和“原因说明”的单子,方便收件人按同一流程处理。

⚠️ 注意:抛异常时应优先抛标准异常或从 std::exception 派生的自定义异常;what() 返回的字符串生命周期与异常对象绑定,捕获后应尽快使用或复制,不要长期保存裸指针。

std::exception 基类

🎯 功能:所有标准异常类的公共基类,自定义异常也应继承它,从而可以被统一的 catch 分支捕获。

💡 比喻:像所有病毒的共同“祖先株”——无论变异成哪种亚型,检测仪都能先识别出它是这个家族的一员。

🔧 声明:class exception;

↩️ 返回值:本身不直接携带错误细节;派生类通过重写 what() 提供具体描述。

C++
#include <exception>
#include <iostream>
using namespace std;

class MyError : public exception {
public:
    const char* what() const noexcept override {
        return "MyError: something went wrong";
    }
};

int main() {
    try {
        throw MyError();
    } catch (const exception& e) {
        cout << "caught: " << e.what() << "\n";
    }
    return 0;
}

what()

🎯 功能:返回一个描述异常原因的 C 风格字符串,是异常对象对外暴露错误信息的标准方式。

💡 比喻:像异常单上的“原因栏”——虽然只是短短一行字,却告诉你到底哪里出了问题。

🔧 声明:virtual const char* what() const noexcept;

↩️ 返回值:返回 const char*,指向以空字符结尾的异常描述字符串;派生类可重写。

C++
#include <exception>
#include <iostream>
using namespace std;

class FileError : public exception {
    string msg;
public:
    explicit FileError(const string& name) : msg("cannot open file: " + name) {}
    const char* what() const noexcept override { return msg.c_str(); }
};

int main() {
    try {
        throw FileError("data.txt");
    } catch (const exception& e) {
        cout << e.what() << "\n";
    }
    return 0;
}

throw / try / catch 基本用法

🎯 功能:throw 用于抛出异常对象;try 块包裹可能抛异常的代码;catch 块按类型匹配并处理异常,支持多分支和按引用捕获。

💡 比喻:像医院急诊分诊——try 是候诊室,throw 是病人喊“我不舒服”,catch 是不同科室按症状把病人接走。

🔧 声明:try { /* ... */ } catch (const SomeException& e) { /* ... */ }

↩️ 返回值:try/catch 本身无返回值;异常被捕获后程序继续执行 catch 后的代码,否则调用 std::terminate。

C++
#include <iostream>
#include <stdexcept>
using namespace std;

int divide(int a, int b) {
    if (b == 0) throw runtime_error("division by zero");
    return a / b;
}

int main() {
    try {
        cout << divide(10, 2) << "\n";
        cout << divide(10, 0) << "\n";
    } catch (const runtime_error& e) {
        cout << "runtime error: " << e.what() << "\n";
    } catch (const exception& e) {
        cout << "other error: " << e.what() << "\n";
    }
    cout << "program continues\n";
    return 0;
}
下一节:10.2 <stdexcept> —— 标准异常类

▼10.2 <stdexcept> —— 标准异常类C++

是什么

📚 介绍:<stdexcept> 提供了一组开箱即用的标准异常类,分为 logic_error(逻辑错误,通常可在编码阶段避免)和 runtime_error(运行时错误,往往依赖外部条件)两大分支,让错误语义更加清晰。

💡 比喻:它们像医院里的专科科室——logic_error 是“可预防内科”,runtime_error 是“急诊外科”,每个派生类又对应更细分的诊室,方便快速定位病因。

⚠️ 注意:标准异常的 what() 信息可能因编译器而异,不要依赖具体文字做程序逻辑判断;优先按异常类型捕获,而不是统一 catch (...)。

logic_error / invalid_argument / domain_error / length_error / out_of_range

🎯 功能:logic_error 表示程序逻辑前提被破坏,派生类包括 invalid_argument(参数不合法)、domain_error(数学定义域错误)、length_error(长度超限)、out_of_range(索引越界)。

💡 比喻:像试卷上的“审题错误”——不是考场突发状况,而是本来就该在交卷前检查出来的问题。

🔧 声明:class logic_error : public exception; class invalid_argument : public logic_error; class domain_error : public logic_error; class length_error : public logic_error; class out_of_range : public logic_error;

↩️ 返回值:均继承自 exception,what() 返回构造时传入的描述字符串。

C++
#include <stdexcept>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    try {
        throw invalid_argument("negative value not allowed");
    } catch (const invalid_argument& e) {
        cout << "invalid_argument: " << e.what() << "\n";
    }

    vector<int> v = {1, 2, 3};
    try {
        cout << v.at(10) << "\n";
    } catch (const out_of_range& e) {
        cout << "out_of_range: " << e.what() << "\n";
    }
    return 0;
}

runtime_error / overflow_error / underflow_error / range_error

🎯 功能:runtime_error 表示程序运行期间才可能暴露的问题,派生类包括 overflow_error(上溢)、underflow_error(下溢)、range_error(范围错误)。

💡 比喻:像高速公路上突然出现的障碍物——不是设计图纸的问题,而是车开起来才会遇到。

🔧 声明:class runtime_error : public exception; class overflow_error : public runtime_error; class underflow_error : public runtime_error; class range_error : public runtime_error;

↩️ 返回值:均继承自 exception,what() 返回构造时传入的描述字符串。

C++
#include <stdexcept>
#include <iostream>
#include <limits>
using namespace std;

int main() {
    try {
        throw runtime_error("network disconnected");
    } catch (const runtime_error& e) {
        cout << "runtime_error: " << e.what() << "\n";
    }

    try {
        int max = numeric_limits<int>::max();
        if (max > 0 && max + 1 < 0) {
            throw overflow_error("integer overflow");
        }
    } catch (const overflow_error& e) {
        cout << "overflow_error: " << e.what() << "\n";
    }
    return 0;
}
下一节:10.3 <system_error> —— 系统错误(C++11)

▼10.3 <system_error> —— 系统错误(C++11)C++

是什么

📚 介绍:<system_error> 提供与操作系统或底层平台错误码交互的标准方式,包括 error_code(可移植错误码)、error_category(错误分类)、error_condition(跨平台条件比较)和 system_error(异常形式)。

💡 比喻:它像各国海关的统一报关系统——每个国家有自己的错误编号(error_code),但系统把它们映射到统一的违规类别(error_condition),方便跨国比对。

⚠️ 注意:error_code 是轻量级值类型,不抛异常;system_error 才是异常类。处理底层 API 错误时优先返回 error_code,只有真正无法继续时才抛 system_error。

std::error_code

🎯 功能:封装了一个错误码值和对应的 error_category,用于在不抛异常的情况下传递底层系统错误。

💡 比喻:像一个轻量化的“错误身份证”——只带编号和发证机构,不带情绪,不会打断程序流程。

🔧 声明:class error_code;

↩️ 返回值:value() 返回整数错误码;category() 返回 error_category 引用;message() 返回描述字符串;bool 转换表示是否有错误。

C++
#include <system_error>
#include <iostream>
using namespace std;

error_code simulate_open() {
    return error_code(ENOENT, generic_category());
}

int main() {
    error_code ec = simulate_open();
    if (ec) {
        cout << "error value: " << ec.value() << "\n";
        cout << "message: " << ec.message() << "\n";
        cout << "category: " << ec.category().name() << "\n";
    } else {
        cout << "ok\n";
    }
    return 0;
}

std::error_category

🎯 功能:error_category 是错误分类的抽象基类,负责把原始错误值解释为可读的 message,并判断两个错误码是否等价。

💡 比喻:像每个国家的领事馆——负责本国民众(错误码)的信息翻译和身份认证。

🔧 声明:class error_category;

↩️ 返回值:name() 返回分类名称;message(int) 返回错误描述;equivalent() 用于跨分类比较;通常使用 system_category() 或 generic_category() 获取内置实例。

C++
#include <system_error>
#include <iostream>
using namespace std;

int main() {
    const error_category& generic = generic_category();
    const error_category& system = system_category();

    cout << "generic name: " << generic.name() << "\n";
    cout << "system name: " << system.name() << "\n";

    error_code ec(ENOENT, generic_category());
    cout << "message: " << ec.category().message(ec.value()) << "\n";
    return 0;
}

std::error_condition

🎯 功能:与 error_code 类似,但独立于具体平台,用于表达“跨平台可比较的错误条件”,常与 errc 枚举搭配。

💡 比喻:像国际通用的“故障类型标签”——不管设备型号和产地,都能把错误归类为“无此文件”“权限不足”等统一条件。

🔧 声明:class error_condition;

↩️ 返回值:value() 返回条件值;category() 返回分类;与 error_code 可用 == / != 比较是否等价。

C++
#include <system_error>
#include <iostream>
using namespace std;

int main() {
    error_code ec(ENOENT, generic_category());
    error_condition cond(errc::no_such_file_or_directory);

    if (ec == cond) {
        cout << "matched: no such file or directory\n";
    }

    error_condition cond2 = ec.default_error_condition();
    cout << "default condition value: " << cond2.value() << "\n";
    return 0;
}

std::system_error

🎯 功能:继承自 runtime_error,把 error_code 包装成异常抛出,适合底层操作失败且调用方无法就地恢复的场景。

💡 比喻:像 error_code 的“升级形态”——当 quiet fail 已不够,必须中断当前流程向上报告时就变身成异常。

🔧 声明:class system_error : public runtime_error;

↩️ 返回值:code() 返回内部的 error_code;what() 包含错误描述;构造时可传入 error_code 和自定义信息。

C++
#include <system_error>
#include <iostream>
using namespace std;

void open_resource() {
    error_code ec(ENOENT, generic_category());
    if (ec) {
        throw system_error(ec, "open_resource failed");
    }
}

int main() {
    try {
        open_resource();
    } catch (const system_error& e) {
        cout << "caught system_error: " << e.what() << "\n";
        cout << "code: " << e.code().value() << "\n";
    }
    return 0;
}
下一节:11.1 <typeinfo> —— 运行时类型识别

▼11.1 <typeinfo> —— 运行时类型识别C++

是什么

📚 介绍:<typeinfo> 提供运行时类型信息(RTTI)机制,通过 typeid 运算符和 type_info 对象,程序可以在运行期查询对象或表达式的实际类型。

💡 比喻:它像给每个对象发了一张“身份证”——即使它被装在 void* 或基类引用里,也能查出真实类型。

⚠️ 注意:RTTI 有多态类型开销,且 type_info::name() 返回的格式由编译器决定,不要跨编译器比较名称字符串;对空指针用 typeid(*p) 会抛 bad_typeid。

typeid

🎯 功能:typeid 运算符返回一个 type_info 对象的引用,可用于在运行期识别表达式或类型的具体类型。

💡 比喻:像一台随身身份扫描仪——对准对象一扫,就能读出它的“户口类型”。

🔧 声明:const type_info& typeid(type); const type_info& typeid(expression);

↩️ 返回值:返回 const std::type_info&,可用于比较类型是否相同或获取类型名称。

C++
#include <typeinfo>
#include <iostream>
using namespace std;

int main() {
    int a = 0;
    double b = 1.0;

    cout << typeid(a).name() << "\n";
    cout << typeid(b).name() << "\n";
    cout << typeid(int).name() << "\n";

    if (typeid(a) == typeid(int)) {
        cout << "a is int\n";
    }
    return 0;
}

std::type_info / name()

🎯 功能:type_info 是编译器生成的类型描述对象,提供 name()、operator==、operator!= 等接口;typeid 返回的就是该对象的引用。

💡 比喻:像派出所里的常住人口登记表——记录了姓名、身份证号,还能比对两个人是不是同一人。

🔧 声明:class type_info;

↩️ 返回值:name() 返回实现相关的类型名称字符串;operator== / != 判断两个类型是否相同;before() 提供全序比较。

C++
#include <typeinfo>
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s = "hello";
    const char* raw = "world";

    cout << "string: " << typeid(s).name() << "\n";
    cout << "char*: " << typeid(raw).name() << "\n";

    if (typeid(s) != typeid(raw)) {
        cout << "different types\n";
    }
    return 0;
}

bad_cast / bad_typeid

🎯 功能:bad_cast 在 dynamic_cast 引用转换失败时抛出;bad_typeid 在 typeid(*nullptr) 等无效场景下抛出。

💡 比喻:像身份证读卡器的错误提示——读不出、证号异常时它会明确告诉你“无法识别”。

🔧 声明:class bad_cast : public exception; class bad_typeid : public exception;

↩️ 返回值:what() 返回错误描述;bad_cast 由失败的引用 dynamic_cast 抛出,bad_typeid 由非法 typeid 操作抛出。

C++
#include <typeinfo>
#include <iostream>
using namespace std;

struct Base { virtual ~Base() = default; };
struct Derived : Base {};

int main() {
    Base* b = new Base();
    try {
        Derived& d = dynamic_cast<Derived&>(*b);
        (void)d;
    } catch (const bad_cast& e) {
        cout << "bad_cast: " << e.what() << "\n";
    }
    delete b;

    Base* p = nullptr;
    try {
        const type_info& ti = typeid(*p);
        (void)ti;
    } catch (const bad_typeid& e) {
        cout << "bad_typeid: " << e.what() << "\n";
    }
    return 0;
}
下一节:12.1 <regex> —— 正则表达式

▼12.1 <regex> —— 正则表达式C++

是什么

📚 介绍:<regex> 把正则表达式引擎引入 C++,支持模式编译、全串匹配、子串搜索、替换以及匹配结果提取,是处理文本模式匹配的标准工具。

💡 比喻:它像一台精密筛子——你给定孔洞形状(正则模式),它就能从一堆沙子(文本)里筛出符合形状的颗粒。

⚠️ 注意:默认语法是 ECMAScript,特殊字符需要转义;复杂正则构造和匹配开销较大,避免在热路径中反复编译同一模式;std::regex 行为在不同库实现间可能存在差异。

std::regex 构造函数

🎯 功能:把字符串形式的正则表达式编译成可复用的 regex 对象,支持多种语法标志(如 icase 忽略大小写)。

💡 比喻:像把图纸交给工厂生产出模具——图纸只需设计一次,之后就能反复用来冲压零件。

🔧 声明:regex(const string& str, regex::flag_type flags = regex::ECMAScript);

↩️ 返回值:构造成功则对象可用;正则语法错误时抛 regex_error 异常。

C++
#include <regex>
#include <iostream>
#include <string>
using namespace std;

int main() {
    regex re1("[a-z]+");
    regex re2("HELLO", regex::icase);

    cout << "re1 valid: " << re1.mark_count() << " capturing groups\n";
    cout << "re2 valid: " << re2.mark_count() << " capturing groups\n";
    return 0;
}

std::regex_match

🎯 功能:检查整个字符串是否完整匹配正则表达式,只有目标从头到尾都符合模式时才返回 true。

💡 比喻:像入场安检——你必须从头到脚都符合着装要求,才能放行;露出一点不合规都不行。

🔧 声明:bool regex_match(const string& s, const regex& re); bool regex_match(const string& s, smatch& m, const regex& re);

↩️ 返回值:完全匹配返回 true,否则 false;带 smatch 的重载可提取捕获组。

C++
#include <regex>
#include <iostream>
#include <string>
using namespace std;

int main() {
    regex re("(\\d{4})-(\\d{2})-(\\d{2})");
    string s = "2024-05-18";
    smatch m;

    if (regex_match(s, m, re)) {
        cout << "full match: " << m[0] << "\n";
        cout << "year: " << m[1] << "\n";
        cout << "month: " << m[2] << "\n";
        cout << "day: " << m[3] << "\n";
    } else {
        cout << "not matched\n";
    }
    return 0;
}

std::regex_search

🎯 功能:在字符串中搜索第一个与正则表达式匹配的子串,不要求整个字符串完全匹配。

💡 比喻:像在大文章中搜索关键词——只要出现一次就算命中,不要求整篇文章都是这个词。

🔧 声明:bool regex_search(const string& s, const regex& re); bool regex_search(const string& s, smatch& m, const regex& re);

↩️ 返回值:找到匹配子串返回 true,否则 false;带 smatch 的重载可获取匹配位置与捕获组。

C++
#include <regex>
#include <iostream>
#include <string>
using namespace std;

int main() {
    regex re("\\b\\w+@\\w+\\.\\w+\\b");
    string text = "Contact me at alice@example.com or bob@test.org.";
    smatch m;

    string::const_iterator start = text.cbegin();
    while (regex_search(start, text.cend(), m, re)) {
        cout << "found: " << m[0] << " at " << m.position() << "\n";
        start = m.suffix().first;
    }
    return 0;
}

std::regex_replace

🎯 功能:把字符串中所有匹配正则表达式的子串替换为指定格式,支持反向引用捕获组。

💡 比喻:像批量查找替换功能——文档里所有“旧标签”自动换成“新标签”,还能保留原标签里的编号。

🔧 声明:string regex_replace(const string& s, const regex& re, const string& fmt);

↩️ 返回值:返回替换后的新字符串;原字符串不会被修改。

C++
#include <regex>
#include <iostream>
#include <string>
using namespace std;

int main() {
    regex re("(\\w+)@(\\w+\\.\\w+)");
    string text = "alice@example.com and bob@test.org";

    string out = regex_replace(text, re, "$1 [at] $2");
    cout << out << "\n";
    return 0;
}

smatch / cmatch / ssub_match

🎯 功能:smatch 保存 string 上的匹配结果,cmatch 保存 const char* 上的匹配结果,ssub_match 表示单个捕获组匹配的子串。

💡 比喻:像一次搜索后的“证据袋”——大袋子里分装着小袋子,每个小袋子标着第几号捕获组、内容是什么。

🔧 声明:typedef match_results<string::const_iterator> smatch; typedef match_results<const char*> cmatch; class sub_match<BiIter>;

↩️ 返回值:match_results 可通过 operator[] 访问第 i 个 sub_match;sub_match 可隐式转换为 string,也可调用 str()、length()、position()。

C++
#include <regex>
#include <iostream>
#include <string>
using namespace std;

int main() {
    regex re("(\\d+)-(\\d+)");
    string s = "score: 85-92";
    smatch m;

    if (regex_search(s, m, re)) {
        cout << "full: " << m[0] << "\n";
        for (size_t i = 1; i < m.size(); ++i) {
            cout << "group " << i << ": " << m[i]
                 << " (len=" << m[i].length() << ", pos=" << m[i].position() << ")\n";
        }
    }

    const char* text = "id=42";
    cmatch cm;
    if (regex_search(text, cm, regex("id=(\\d+)"))) {
        cout << "cm group 1: " << cm[1] << "\n";
    }
    return 0;
}
下一节:13.1 <filesystem> —— 文件系统操作

▼13.1 <filesystem> —— 文件系统操作C++

是什么

📚 介绍:<filesystem>(C++17)提供跨平台的文件系统操作接口,包括路径表示、文件属性查询、目录创建、文件复制/删除以及当前路径和文件大小查询。

💡 比喻:它像一位全能文件管理员——既能看懂各国文件柜的编号规则(path),也能帮你新建抽屉、复印档案、清理废纸。

⚠️ 注意:filesystem 需要 C++17 支持(编译时加 -std=c++17);所有修改操作都可能因权限或路径问题抛 filesystem_error,敏感操作前建议先检查 exists() 或捕获异常。

std::filesystem::path

🎯 功能:path 类封装文件系统路径,支持跨平台的路径拼接、分解、扩展名提取和规范化操作。

💡 比喻:像一支万能地址笔——不管路径是 Windows 的 “C:\” 还是 Linux 的 “/”,它都能按统一方式读写和拼接。

🔧 声明:class path;

↩️ 返回值:提供 string()、u8string()、filename()、stem()、extension()、parent_path() 等成员;重载了 /= 和 / 用于路径拼接。

C++
#include <filesystem>
#include <iostream>
using namespace std;
namespace fs = filesystem;

int main() {
    fs::path p = "data";
    p /= "reports";
    p /= "summary.txt";

    cout << "full: " << p << "\n";
    cout << "filename: " << p.filename() << "\n";
    cout << "stem: " << p.stem() << "\n";
    cout << "extension: " << p.extension() << "\n";
    cout << "parent: " << p.parent_path() << "\n";
    return 0;
}

exists / is_regular_file / is_directory

🎯 功能:查询路径是否存在、是否为普通文件、是否为目录,是文件操作前最常用的安全预检。

💡 比喻:像进仓库前先看门牌——确认仓库存在、是房间还是货架,再决定怎么搬东西。

🔧 声明:bool exists(const path& p); bool is_regular_file(const path& p); bool is_directory(const path& p);

↩️ 返回值:返回 bool;路径不存在或无法访问时返回 false(部分重载通过 error_code 输出错误)。

C++
#include <filesystem>
#include <iostream>
using namespace std;
namespace fs = filesystem;

int main() {
    fs::path p = "cpp.html";

    cout << boolalpha;
    cout << "exists: " << fs::exists(p) << "\n";
    cout << "is_regular_file: " << fs::is_regular_file(p) << "\n";
    cout << "is_directory: " << fs::is_directory(p) << "\n";

    fs::path dir = ".";
    cout << "dir exists: " << fs::exists(dir) << "\n";
    cout << "dir is_directory: " << fs::is_directory(dir) << "\n";
    return 0;
}

create_directory / create_directories

🎯 功能:create_directory 创建单级目录;create_directories 递归创建路径中所有不存在的目录。

💡 比喻:像装修队进场——create_directory 只铺一层地板,create_directories 会连地基到顶层一次性搭好。

🔧 声明:bool create_directory(const path& p); bool create_directories(const path& p);

↩️ 返回值:成功创建返回 true,目录已存在返回 false;失败时抛 filesystem_error(或设置 error_code)。

C++
#include <filesystem>
#include <iostream>
using namespace std;
namespace fs = filesystem;

int main() {
    fs::path d1 = "tmp_level1";
    fs::path d2 = "tmp_a/tmp_b/tmp_c";

    if (fs::create_directory(d1)) {
        cout << "created: " << d1 << "\n";
    }

    if (fs::create_directories(d2)) {
        cout << "created nested: " << d2 << "\n";
    }

    fs::remove_all(d1);
    fs::remove_all(d2);
    return 0;
}

copy / copy_file / remove / remove_all

🎯 功能:copy 复制文件或目录;copy_file 复制单个文件;remove 删除单个文件或空目录;remove_all 递归删除目录及其内容。

💡 比喻:像档案室的复印和碎纸流程—— copy 是复印,remove 是单张碎纸,remove_all 是整箱清空。

🔧 声明:void copy(const path& from, const path& to, copy_options opts = /*none*/); bool copy_file(...); bool remove(const path& p); uintmax_t remove_all(const path& p);

↩️ 返回值:remove 成功返回 true;remove_all 返回删除的项目数;copy/copy_file 失败时抛 filesystem_error。

C++
#include <filesystem>
#include <fstream>
#include <iostream>
using namespace std;
namespace fs = filesystem;

int main() {
    fs::path src = "demo_src.txt";
    fs::path dst = "demo_dst.txt";

    {
        ofstream(src) << "hello filesystem";
    }

    fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
    cout << "copied " << src << " to " << dst << "\n";

    fs::remove(src);
    fs::remove(dst);
    cout << "removed both files\n";
    return 0;
}

current_path / file_size

🎯 功能:current_path 获取或设置程序的当前工作目录;file_size 返回普通文件的字节大小。

💡 比喻:像看仓库总台账——current_path 告诉你现在站在哪个库房,file_size 告诉你某个箱子有多重。

🔧 声明:path current_path(); void current_path(const path& p); uintmax_t file_size(const path& p);

↩️ 返回值:current_path 返回或设置当前工作目录;file_size 返回文件字节数;对目录调用 file_size 会抛异常。

C++
#include <filesystem>
#include <fstream>
#include <iostream>
using namespace std;
namespace fs = filesystem;

int main() {
    cout << "current path: " << fs::current_path() << "\n";

    fs::path f = "size_demo.txt";
    {
        ofstream out(f);
        out << "1234567890";
    }

    cout << "file size: " << fs::file_size(f) << " bytes\n";
    fs::remove(f);
    return 0;
}
下一节:14.1 <format> —— 格式化(C++20)

▼14.1 <format> —— 格式化(C++20)C++

是什么

📚 介绍:<format>(C++20)提供类型安全、Python 风格的字符串格式化,替代 printf 的格式说明符和 iostream 的插入运算符链。核心函数 std::format 通过大括号占位符把值按指定格式写进字符串。

💡 比喻:它像一台智能标签打印机——你把值丢进模板槽位,它自动按你指定的宽度、精度、进制和对齐方式生成整齐的标签。

⚠️ 注意:C++20 的 <format> 需要较新的编译器(GCC 13+、Clang 17+、VS 2019 16.10+);在完全支持前可用 fmt 库作为过渡。占位符与参数数量必须匹配,否则编译期报错。

std::format

🎯 功能:按格式字符串和参数生成新的 std::string,是 <format> 最常用的入口。

💡 比喻:像一台自动排版机——把零散的数据按统一版式装订成一份完整文档。

🔧 声明:template<class... Args>
string format(format_string<Args...> fmt, Args&&... args);

↩️ 返回值:返回格式化后的 std::string;参数类型与格式说明符不匹配时编译失败。

C++
#include <format>
#include <iostream>
#include <string>
using namespace std;

int main() {
    string s1 = format("{} + {} = {}", 1, 2, 3);
    string s2 = format("{1} {0}", "world", "hello");
    string s3 = format("name={}, age={}", "Alice", 30);

    cout << s1 << "\n";
    cout << s2 << "\n";
    cout << s3 << "\n";
    return 0;
}

格式说明符({:d}、{:.2f}、{:>10} 等)

🎯 功能:在大括号内用冒号引出格式规则,可控制进制、精度、宽度、填充、对齐、符号等。

💡 比喻:像打印机的排版指令——同样是数字,你可以指定它显示成整数、两位小数、还是右对齐占 10 格。

🔧 声明:{:[fill][align][sign][#][0][width][.precision][type]}

↩️ 返回值:std::format 按说明符转换对应参数并嵌入结果字符串;非法说明符会编译报错或运行时抛 format_error。

C++
#include <format>
#include <iostream>
#include <string>
using namespace std;

int main() {
    int n = 42;
    double pi = 3.1415926;

    cout << format("decimal: {:d}\n", n);
    cout << format("hex: {:x}\n", n);
    cout << format("binary: {:b}\n", n);
    cout << format("two digits: {:.2f}\n", pi);
    cout << format("right align: [{:>10}]\n", n);
    cout << format("left align:  [{:<10}]\n", n);
    cout << format("zero pad:    [{:010}]\n", n);
    return 0;
}
下一节:14.2 <ranges> —— 范围库(C++20)

▼14.2 <ranges> —— 范围库(C++20)C++

是什么

📚 介绍:<ranges>(C++20)把容器、数组、I/O 流等可迭代对象抽象为“范围”,并提供惰性求值的视图适配器。它让过滤、转换、切片、生成等操作可以链式组合,避免不必要的中间容器。标准算法也提供 ranges 重载。

💡 比喻:它像一条自动化流水线——原料(容器)从传送带进入,经过筛选、包装、取样等工位,最终按需产出,而不是每个工位都先堆一个临时仓库。

⚠️ 注意:视图通常是惰性求值,只在迭代时才真正计算;修改底层容器可能使视图失效。组合多个视图时建议用括号明确优先级,复杂的管道可读性会下降。

views

🎯 功能:std::views 是范围视图适配器的命名空间,提供 filter、transform、take、drop、iota 等工厂函数,用于构建惰性计算的视图。

💡 比喻:像工厂的各个加工模块——每个模块本身不生产成品,而是把输入流按需转换成另一种形态。

🔧 声明:namespace std::views; // 包含 filter, transform, take, drop, iota, all 等

↩️ 返回值:返回一个视图对象,可与范围用管道符 | 组合,也可以直接迭代。

C++
#include <ranges>
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5, 6};

    auto view = v | views::filter([](int x) { return x % 2 == 0; })
                  | views::transform([](int x) { return x * x; });

    for (int x : view) {
        cout << x << " ";
    }
    cout << "\n";
    return 0;
}

filter / transform / take / drop

🎯 功能:filter 按谓词保留元素;transform 对每个元素做映射;take 取前 N 个;drop 跳过前 N 个。它们都是惰性视图。

💡 比喻:像数据清洗车间——filter 是质检筛,transform 是包装机,take 是只取前几个样品的机械臂,drop 是跳过开头瑕疵的拨叉。

🔧 声明:views::filter(pred) | views::transform(f) | views::take(n) | views::drop(n)

↩️ 返回值:返回经过筛选/映射/切片后的视图;不会修改原容器,也不会一次性分配新容器。

C++
#include <ranges>
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto pipe = v | views::drop(2)
                  | views::filter([](int x) { return x % 2 == 0; })
                  | views::transform([](int x) { return x * 10; })
                  | views::take(3);

    for (int x : pipe) {
        cout << x << " ";
    }
    cout << "\n";
    return 0;
}

iota / all

🎯 功能:views::iota 生成从起始值开始的无限或有限整数序列;views::all 把任意范围包装成视图而不复制数据。

💡 比喻:像无限卷纸机和复印机——iota 能源源不断地吐出编号,all 则把现有资料原样装进视图封套。

🔧 声明:views::iota(start); views::iota(start, stop); views::all(range);

↩️ 返回值:iota 返回一个惰性递增序列;all 返回底层范围的全视图;两者都不拥有元素。

C++
#include <ranges>
#include <iostream>
#include <vector>
using namespace std;

int main() {
    for (int x : views::iota(1, 6)) {
        cout << x << " ";
    }
    cout << "\n";

    vector<int> v = {10, 20, 30};
    auto all_view = views::all(v);
    for (int x : all_view) {
        cout << x << " ";
    }
    cout << "\n";
    return 0;
}
下一节:14.3 <span> —— 连续内存视图(C++20)

▼14.3 <span> —— 连续内存视图(C++20)C++

是什么

📚 介绍:<span>(C++20)提供一个轻量的、不拥有所有权的连续内存视图。它可以指向数组、std::vector、std::array 或原始指针,用来替代“裸指针 + 长度”这种容易出错的接口。

💡 比喻:它像一块透明玻璃——你能透过它看到背后的数据,甚至只框选其中一部分,但玻璃本身不储存数据,也不会把数据复制一份。

⚠️ 注意:span 不管理内存生命周期,底层对象必须在 span 使用期间保持有效。静态大小 span<T, N> 与动态大小 span<T> 的接口略有不同,传参时优先用 span<const T> 表示只读。

span 构造函数

🎯 功能:span 可以从数组、vector、array、原始指针对或另一个 span 构造,支持静态大小和动态大小两种形式。

💡 比喻:像给一段连续内存装上一个可调大小的取景框——框住多少由你决定,但内容来自外部。

🔧 声明:span<T> s(container); span<T> s(ptr, count); span<T, N> s(arr);

↩️ 返回值:构造一个指向外部数据的轻量视图;静态大小的 span 编译期知道长度,动态大小的 span 运行期知道长度。

C++
#include <span>
#include <vector>
#include <array>
#include <iostream>
using namespace std;

void print_span(span<const int> s) {
    for (int x : s) cout << x << " ";
    cout << "\n";
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> v = {6, 7, 8};
    array<int, 3> a = {9, 10, 11};

    span<int, 5> s1(arr);
    span<int> s2(v);
    span<int> s3(a);
    span<int> s4(arr + 1, 3);

    print_span(s1);
    print_span(s2);
    print_span(s3);
    print_span(s4);
    return 0;
}

operator[] / at / front / back / data

🎯 功能:span 提供与容器类似的元素访问接口:operator[] 不检查边界,at 做边界检查,front/back 访问首尾,data 返回原始指针。

💡 比喻:像展台上的样品架——operator[] 是直接拿,at 是柜台确认后再取,front/back 是取最左最右的样品,data 是拿到仓库地址。

🔧 声明:T& operator[](size_t i); T& at(size_t i); T& front(); T& back(); T* data();

↩️ 返回值:返回对应元素的引用或首元素指针;at 在越界时抛 out_of_range,operator[] 行为未定义。

C++
#include <span>
#include <iostream>
using namespace std;

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    span<int> s(arr);

    cout << "s[0]=" << s[0] << "\n";
    cout << "s.at(2)=" << s.at(2) << "\n";
    cout << "front=" << s.front() << ", back=" << s.back() << "\n";
    cout << "data pointer=" << s.data() << "\n";
    return 0;
}

size / size_bytes / subspan

🎯 功能:size 返回元素个数,size_bytes 返回总字节数,subspan 返回一个新的 span 指向原范围的一个连续子区间。

💡 比喻:像切蛋糕——size 数有几块,size_bytes 算整块多重,subspan 按你想要的起止位置切下一条。

🔧 声明:size_t size() const; size_t size_bytes() const;
span<T> subspan(size_t offset, size_t count = dynamic_extent);

↩️ 返回值:size / size_bytes 返回对应计数;subspan 返回指向子区间的视图,不复制数据。

C++
#include <span>
#include <iostream>
using namespace std;

int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8};
    span<int> s(arr);

    cout << "size=" << s.size() << ", bytes=" << s.size_bytes() << "\n";

    auto sub = s.subspan(2, 4);
    cout << "sub size=" << sub.size() << ": ";
    for (int x : sub) cout << x << " ";
    cout << "\n";
    return 0;
}
下一节:14.4 <concepts> —— 概念(C++20)

▼14.4 <concepts> —— 概念(C++20)C++

是什么

📚 介绍:<concepts>(C++20)引入“概念”机制,用来对模板参数施加编译期约束。它让模板错误更直观,并支持基于约束的重载。标准库已预定义大量概念,如 same_as、convertible_to、integral、ranges::range 等。

💡 比喻:它像招聘时的岗位 JD——“要求整数类型”“要求可比较”“要求是一个范围”,只有简历(模板实参)符合,才能进入面试(实例化)。

⚠️ 注意:概念只能用于约束模板参数,不能替代运行期检查。requires 子句中的表达式必须是编译期可判定的;误用会导致实例化失败或重载解析歧义。

requires 子句

🎯 功能:requires 子句紧跟在模板参数列表之后,用一组编译期布尔表达式对模板参数施加约束,不满足时模板不参与重载。

💡 比喻:像门口的安检闸机——只有满足条件的人才能进入,不符合的人会被礼貌地拦下并告知原因。

🔧 声明:template<typename T>
requires expression
void foo(T x);

↩️ 返回值:无返回值;约束失败时编译器给出清晰的“约束不满足”错误,而不是深层的模板展开错误。

C++
#include <concepts>
#include <iostream>
using namespace std;

template<typename T>
requires integral<T>
T add_int(T a, T b) {
    return a + b;
}

template<typename T>
requires (sizeof(T) >= 4)
T big_only(T x) {
    return x;
}

int main() {
    cout << add_int(3, 4) << "\n";
    cout << big_only(12345678L) << "\n";
    return 0;
}

标准概念:same_as、convertible_to、integral、ranges::range 等

🎯 功能:标准库预定义的概念覆盖类型关系、算术类型、可调用对象、迭代器、范围等,可直接用于约束模板参数。

💡 比喻:像标准件规格表——螺纹、电压、接口形状都已经定义好,你只需要按规格挑选。

🔧 声明:template<class T, class U> concept same_as = /*...*/;
template<class From, class To> concept convertible_to = /*...*/;
template<class T> concept integral = /*...*/;
template<class T> concept range = /*...*/;

↩️ 返回值:这些 concept 都是编译期谓词,值为 true 表示约束满足;可用于 requires 子句或简写模板语法。

C++
#include <concepts>
#include <vector>
#include <ranges>
#include <iostream>
using namespace std;

template<typename T>
requires same_as<T, int> || same_as<T, long>
T only_int_or_long(T x) { return x; }

template<convertible_to<double> T>
double to_double(T x) { return static_cast<double>(x); }

template<ranges::range R>
void print_range(const R& r) {
    for (auto x : r) cout << x << " ";
    cout << "\n";
}

int main() {
    cout << only_int_or_long(42) << "\n";
    cout << to_double(3.14f) << "\n";

    vector<int> v = {1, 2, 3};
    print_range(v);
    return 0;
}
下一节:14.5 <compare> —— 三路比较(C++20)

▼14.5 <compare> —— 三路比较(C++20)C++

是什么

📚 介绍:<compare>(C++20)引入三路比较运算符 <=>(太空船运算符),一次比较即可得到小于、等于或大于的结果。它还定义了 strong_ordering、weak_ordering、partial_ordering 三种比较类别,并提供了默认比较、比较辅助函数等基础设施。

💡 比喻:它像天平的一次称量——传统写法要分别称“左轻”“平衡”“右轻”,太空船运算符一次就给出三种状态之一。

⚠️ 注意:<=> 返回的是比较类别对象,不是整数,不能直接用 < 0 与 int 0 比较(应写 result < 0 在重载支持下可行,但推荐用 == 0、<=> 0 或 switch/case)。默认实现 = default 需要成员全部支持三路比较。

<=>(太空船运算符)

🎯 功能:三路比较运算符对左右操作数进行一次比较,返回 strong_ordering、weak_ordering 或 partial_ordering 之一。

💡 比喻:像一场比赛裁判的一次哨响——同时告诉你 A 胜、平局还是 B 胜,不用反复询问。

🔧 声明:auto operator<=>(const T&) const = default;

↩️ 返回值:返回比较类别对象;可与 0 用 ==、!=、<、>、<=、>= 比较,也可直接触发编译器生成的比较函数。

C++
#include <compare>
#include <iostream>
using namespace std;

int main() {
    int a = 3, b = 5;
    auto r = a <=> b;

    if (r < 0) cout << "a < b\n";
    else if (r == 0) cout << "a == b\n";
    else if (r > 0) cout << "a > b\n";
    return 0;
}

std::strong_ordering / weak_ordering / partial_ordering

🎯 功能:strong_ordering 表示等价且不可区分;weak_ordering 表示等价但可区分;partial_ordering 表示某些值之间不可比较(如 NaN)。

💡 比喻:像三种不同精度的裁判标准——强序连“长得像”都不允许模糊;弱序允许外貌相似;偏序允许某些选手根本不在同一赛道。

🔧 声明:strong_ordering::less / equal / greater;
weak_ordering::less / equivalent / greater;
partial_ordering::less / equivalent / greater / unordered;

↩️ 返回值:这些类别对象可用 ==、!=、<、> 等与 0 比较,也可用于 switch 风格的比较分发。

C++
#include <compare>
#include <iostream>
#include <cmath>
using namespace std;

int main() {
    auto s = 1 <=> 2;
    if (s == strong_ordering::less) cout << "strong less\n";

    double nan = numeric_limits<double>::quiet_NaN();
    auto p = nan <=> 1.0;
    if (p == partial_ordering::unordered) cout << "partial unordered\n";
    return 0;
}

compare_xxx 函数

🎯 功能:std::compare_xxx 系列(如 compare_three_way)提供可调用对象形式的比较策略,常用于标准算法或自定义比较器。

💡 比喻:像把裁判标准做成一把通用卡尺——算法需要“怎么比”时,把这套标准递进去即可。

🔧 声明:struct compare_three_way; struct ranges::less; struct ranges::greater;

↩️ 返回值:返回比较类别或 bool;可作为算法(如 sort)的比较器,也可在泛型代码中统一比较语义。

C++
#include <compare>
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> v = {3, 1, 4, 1, 5, 9};
    sort(v.begin(), v.end(), compare_three_way());

    for (int x : v) cout << x << " ";
    cout << "\n";
    return 0;
}
下一节:16.1 <cassert> —— 断言

▼16.1 <cassert> —— 断言C 兼容

是什么

📚 介绍:<cassert> 提供 assert 宏,用于在调试阶段检查“不应该发生”的前提条件。当表达式为 false 时,程序会输出错误信息并异常终止。定义 NDEBUG 宏后,所有 assert 在编译期被移除。

💡 比喻:它像施工现场的保险绳——平时不显眼,一旦你踩空(违反假设),立刻把你拉住并大喊“这里出问题了”。

⚠️ 注意:assert 只在 Debug builds 中有效,Release 中通常定义 NDEBUG 使其失效,因此不能用 assert 做运行期错误处理;副作用代码不要放进 assert 表达式,否则 Release 行为会改变。

assert

🎯 功能:assert(expr) 在 expr 为 false 时向标准错误输出诊断信息并调用 std::abort() 终止程序;expr 为 true 时不产生任何效果。

💡 比喻:像自动验票机——票有效就放行,票无效就亮红灯并把你拦下。

🔧 声明:void assert(int expression);

↩️ 返回值:无返回值;失败时程序终止并输出文件名、行号和失败表达式。

C++
#include <cassert>
#include <iostream>
using namespace std;

int divide(int a, int b) {
    assert(b != 0);
    return a / b;
}

int main() {
    cout << divide(10, 2) << "\n";
    return 0;
}

NDEBUG

🎯 功能:NDEBUG 是控制断言行为的宏。如果在包含 <cassert> 前定义了 NDEBUG,assert 宏会被替换为空,所有运行时检查都会被移除。

💡 比喻:像演出时的静音开关——NDEBUG 一打开,调试阶段的报警器全部静音,表演按正式流程走。

🔧 声明:#define NDEBUG // 必须在 #include 之前

↩️ 返回值:无返回值;定义后 assert 不再生成任何代码,也不会检查条件。

C++
#define NDEBUG
#include <cassert>
#include <iostream>
using namespace std;

int main() {
    assert(false);  // 不会触发,因为 NDEBUG 已定义
    cout << "release build: assert disabled\n";
    return 0;
}
下一节:16.2 <bitset> —— 位集

▼16.2 <bitset> —— 位集C++

是什么

📚 介绍:<bitset> 提供固定大小的位集合,封装了一组二进制位并提供直观的位操作接口。它比手动用 unsigned int 做位运算更安全,比 vector<bool> 更高效紧凑。

💡 比喻:它像一排带编号的小开关——每个位置只能开或关,你可以单独扳动、批量翻转、统计亮灯数量,还能把它转成字符串或整数。

⚠️ 注意:bitset 大小必须在编译期确定;operator[] 不检查越界,test 会检查。to_ulong / to_ullong 在位过多时会抛 overflow_error。

bitset 构造函数

🎯 功能:bitset 可用默认构造(全 0)、整数构造、字符串构造或拷贝构造,大小由模板参数在编译期指定。

💡 比喻:像一排新安装的开关——默认全部关闭,你也可以按初始状态一次性设定。

🔧 声明:bitset<N>(); bitset<N>(unsigned long long val); bitset<N>(const string& str);

↩️ 返回值:构造一个固定 N 位的位集;整数构造按二进制位填充,字符串构造按字符序列填充。

C++
#include <bitset>
#include <iostream>
#include <string>
using namespace std;

int main() {
    bitset<8> b1;
    bitset<8> b2(0b10110011);
    bitset<8> b3("11001100");

    cout << "b1: " << b1 << "\n";
    cout << "b2: " << b2 << "\n";
    cout << "b3: " << b3 << "\n";
    return 0;
}

operator[] / test / set / reset / flip

🎯 功能:operator[] 读写指定位(不检查越界);test 检查指定位;set 置 1;reset 置 0;flip 取反。

💡 比喻:像操作一排开关——operator[] 是直接扳,test 是查看状态灯,set/reset 是开和关,flip 是反转。

🔧 声明:reference operator[](size_t pos);
bool test(size_t pos) const;
bitset& set(size_t pos, bool val = true);
bitset& reset(size_t pos);
bitset& flip(size_t pos);

↩️ 返回值:operator[] / test 返回对应位的状态;set/reset/flip 返回 bitset 自身引用,支持链式调用。

C++
#include <bitset>
#include <iostream>
using namespace std;

int main() {
    bitset<8> b;
    b.set(2);
    b[5] = 1;
    b.flip(2);

    cout << "b: " << b << "\n";
    cout << "b[5]=" << b[5] << ", test(3)=" << b.test(3) << "\n";

    b.reset(5);
    cout << "after reset(5): " << b << "\n";
    return 0;
}

to_ulong / to_ullong / to_string / count

🎯 功能:to_ulong / to_ullong 把位集转换为整数;to_string 转换为 01 字符串;count 返回 1 的个数。

💡 比喻:像把开关状态翻译成不同语言——to_ulong 是数字语言,to_string 是文本语言,count 是统计亮灯数量。

🔧 声明:unsigned long to_ulong() const;
unsigned long long to_ullong() const;
string to_string() const;
size_t count() const;

↩️ 返回值:返回转换后的数值、字符串或 1 的位数;位数超过返回类型范围时 to_ulong/to_ullong 抛 overflow_error。

C++
#include <bitset>
#include <iostream>
#include <string>
using namespace std;

int main() {
    bitset<8> b("10110100");

    cout << "string: " << b.to_string() << "\n";
    cout << "ulong: " << b.to_ulong() << "\n";
    cout << "count of 1s: " << b.count() << "\n";
    cout << "all: " << b.all() << ", any: " << b.any() << ", none: " << b.none() << "\n";
    return 0;
}
下一节:16.3 <initializer_list> —— 初始化列表(C++11)

▼16.3 <initializer_list> —— 初始化列表(C++11)C++

是什么

📚 介绍:<initializer_list>(C++11)提供对花括号初始化列表 {a, b, c} 的标准访问方式。它让自定义类也能支持统一的初始化语法,并与标准容器的花括号构造保持风格一致。

💡 比喻:它像一份一次性购物清单——清单本身是只读的,列出你要放进对象里的所有元素,构造完成后清单就完成了使命。

⚠️ 注意:initializer_list 中的元素是 const,只能读取不能修改;其生命周期与初始化表达式绑定,不要长期保存。现代编译器通常会自动处理花括号列表,一般不需要显式包含此头文件。

initializer_list

🎯 功能:initializer_list<T> 是轻量的只读数组视图,由编译器在遇到花括号初始化列表时自动构造,常用于自定义构造函数。

💡 比喻:像一份即点即用的配料包——商家按菜单自动打包,你负责把它拆开装进碗里。

🔧 声明:template<class E> class initializer_list;

↩️ 返回值:不拥有元素,提供只读访问;大小在构造时确定,不可增长或修改。

C++
#include <initializer_list>
#include <vector>
#include <iostream>
using namespace std;

class IntBag {
    vector<int> data_;
public:
    IntBag(initializer_list<int> list) : data_(list) {}
    void print() const {
        for (int x : data_) cout << x << " ";
        cout << "\n";
    }
};

int main() {
    IntBag bag = {1, 2, 3, 4, 5};
    bag.print();
    return 0;
}

begin / end / size

🎯 功能:initializer_list 提供 begin 和 end 迭代器以及 size 成员函数,使其可以像普通容器一样遍历和获取长度。

💡 比喻:像清单上的页码——begin 是第一项,end 是末尾之后,size 告诉你一共有几条。

🔧 声明:const E* begin() const noexcept; const E* end() const noexcept; size_t size() const noexcept;

↩️ 返回值:begin/end 返回指向首元素和尾后元素的指针;size 返回元素个数,时间复杂度 O(1)。

C++
#include <initializer_list>
#include <iostream>
using namespace std;

int sum(initializer_list<int> list) {
    int total = 0;
    for (auto it = list.begin(); it != list.end(); ++it) {
        total += *it;
    }
    return total;
}

int main() {
    cout << "sum = " << sum({1, 2, 3, 4, 5}) << "\n";
    cout << "size = " << initializer_list<int>({10, 20, 30}).size() << "\n";
    return 0;
}
下一节:16.4 <charconv> —— 低开销数值转换(C++17)

▼16.4 <charconv> —— 低开销数值转换(C++17)C++

是什么

📚 介绍:<charconv>(C++17)提供 to_chars 和 from_chars,用于在数值与字符序列之间做低开销、无分配、无异常的转换。它是实现高性能日志、序列化和网络协议的首选。

💡 比喻:它像一台只认数字的极简打字机——不排版、不格式化、不抛纸团,只把整数或浮点数原样敲进字符缓冲区。

⚠️ 注意:to_chars 不在输出末尾写 '\0',需要手动终止字符串;浮点支持到 C++17 才完整,但早期实现可能不完整。from_chars 成功时返回的 ptr 指向未解析的下一个字符。

to_chars

🎯 功能:把整数或浮点数转换为字符序列写入给定缓冲区,不分配内存、不抛异常,返回写入结果的迭代器和错误码。

💡 比喻:像把数字刻在木板上——只写有效字符,末尾不自动加盖结束章,刻完你自己画终止线。

🔧 声明:to_chars_result to_chars(char* first, char* last, T value, int base = 10);

↩️ 返回值:返回 {ptr, ec};ptr 指向最后一个写入字符的下一个位置;ec 为 errc{} 表示成功。

C++
#include <charconv>
#include <iostream>
using namespace std;

int main() {
    char buf[32];
    auto [ptr, ec] = to_chars(buf, buf + sizeof(buf), 12345, 10);
    *ptr = '\0';

    if (ec == errc{}) {
        cout << "value = " << buf << "\n";
    } else {
        cout << "conversion failed\n";
    }

    auto [p2, e2] = to_chars(buf, buf + sizeof(buf), 255, 16);
    *p2 = '\0';
    cout << "hex = " << buf << "\n";
    return 0;
}

from_chars

🎯 功能:把字符序列解析为整数或浮点数,不分配内存、不抛异常,返回解析位置和错误码。

💡 比喻:像一台只读数字的扫描仪——从字符流里识别数值,读完指向没读完的部分,失败也平静地报告。

🔧 声明:from_chars_result from_chars(const char* first, const char* last, T& value, int base = 10);

↩️ 返回值:返回 {ptr, ec};ptr 指向第一个未解析字符;ec 为 errc{} 表示成功,result_out_of_range 表示溢出。

C++
#include <charconv>
#include <iostream>
using namespace std;

int main() {
    const char* text = "12345 extra";
    int value = 0;
    auto [ptr, ec] = from_chars(text, text + strlen(text), value);

    if (ec == errc{}) {
        cout << "parsed: " << value << "\n";
        cout << "remaining: " << ptr << "\n";
    } else {
        cout << "parse failed\n";
    }
    return 0;
}
下一节:16.5 <numbers> —— 数学常量(C++20)

▼16.5 <numbers> —— 数学常量(C++20)C++

是什么

📚 介绍:<numbers>(C++20)提供一组编译期数学常量,包括圆周率 pi、自然常数 e、sqrt2、phi 等。它们用变量模板实现,避免了手写 Magic Number 带来的精度问题和可读性损失。

💡 比喻:它像实验室里的标准试剂瓶——pi、e、sqrt2 都贴着精确标签,不用你每次自己称量。

⚠️ 注意:常量以变量模板形式提供,如 numbers::pi_v<double>,numbers::pi 是 double 的简写。需要 C++20 支持;低版本可用宏或自行定义。

numbers::pi

🎯 功能:圆周率 π 的编译期常量,可用于几何计算、三角函数参数等。

💡 比喻:像圆规说明书上的标准参数——无论画多大圆,周长与直径的比值都从这里取。

🔧 声明:template<class T> inline constexpr T pi_v = /*...*/;
inline constexpr double pi = pi_v<double>;

↩️ 返回值:返回指定类型的 π 近似值;double 精度约为 3.141592653589793。

C++
#include <numbers>
#include <iostream>
using namespace std;

int main() {
    double r = 2.0;
    double area = numbers::pi * r * r;
    cout << "radius = " << r << ", area = " << area << "\n";

    float pi_f = numbers::pi_v<float>;
    cout << "pi as float = " << pi_f << "\n";
    return 0;
}

numbers::e

🎯 功能:自然常数 e 的编译期常量,约等于 2.71828,常见于指数、对数和复利计算。

💡 比喻:像复利增长曲线上的基准斜率——描述自然增长过程时,它是最常用的底数。

🔧 声明:inline constexpr double e = e_v<double>;

↩️ 返回值:返回指定类型的 e 近似值;double 精度约为 2.718281828459045。

C++
#include <numbers>
#include <cmath>
#include <iostream>
using namespace std;

int main() {
    double x = 1.0;
    cout << "e^" << x << " = " << exp(x) << "\n";
    cout << "numbers::e = " << numbers::e << "\n";
    return 0;
}

numbers::sqrt2 / phi 等

🎯 功能:numbers 还提供 sqrt2(√2)、sqrt3、inv_sqrtpi、ln2、ln10、log2e、log10e、phi(黄金比例)等常量。

💡 比喻:像一套常用几何和对数尺——sqrt2 算正方形对角线,phi 用于美学比例,ln2 用于信息熵。

🔧 声明:inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double phi = phi_v<double>;

↩️ 返回值:返回对应数学常量的指定类型近似值,可直接参与表达式计算。

C++
#include <numbers>
#include <iostream>
using namespace std;

int main() {
    cout << "sqrt2 = " << numbers::sqrt2 << "\n";
    cout << "sqrt3 = " << numbers::sqrt3 << "\n";
    cout << "ln2   = " << numbers::ln2 << "\n";
    cout << "phi   = " << numbers::phi << "\n";
    return 0;
}
下一节:16.6 <source_location> —— 源码位置(C++20)

▼16.6 <source_location> —— 源码位置(C++20)C++

是什么

📚 介绍:<source_location>(C++20)提供一种类型安全的源码位置获取方式,替代传统的 __FILE__、__LINE__、__func__ 宏。它通过 source_location::current() 在调用点捕获文件名、函数名、行号和列号。

💡 比喻:它像自动填写的快递面单——不需要你手动写寄件地址,系统把当前网点、窗口、工位一次性打印出来。

⚠️ 注意:source_location::current() 必须在默认参数或模板参数中使用才能捕获调用点,否则捕获的是当前函数定义处。它是 C++20 特性,旧编译器不支持。

source_location::current

🎯 功能:source_location::current() 构造一个表示当前源码位置的对象,通常作为默认参数传递给日志或断言函数。

💡 比喻:像相机的自动定位水印——你一按快门,它就把拍摄地点、时间、设备信息一起烙印在照片上。

🔧 声明:static consteval source_location current() noexcept;

↩️ 返回值:返回一个 source_location 对象,记录调用处的文件、函数、行、列信息。

C++
#include <source_location>
#include <iostream>
using namespace std;

void log(auto msg, source_location loc = source_location::current()) {
    cout << loc.file_name() << ":" << loc.line()
         << " (" << loc.function_name() << ") "
         << msg << "\n";
}

int main() {
    log("program start");
    log("doing work");
    return 0;
}

file_name / function_name / line / column

🎯 功能:source_location 提供四个查询接口,分别返回文件路径、函数签名、行号和列号,用于日志、调试和错误定位。

💡 比喻:像定位导航的四维坐标——file_name 是城市,function_name 是街道,line 是门牌号,column 是房间号。

🔧 声明:const char* file_name() const noexcept;
const char* function_name() const noexcept;
uint_least32_t line() const noexcept;
uint_least32_t column() const noexcept;

↩️ 返回值:分别返回对应源码位置信息;function_name 的具体格式由实现定义,通常包含函数签名。

C++
#include <source_location>
#include <iostream>
using namespace std;

void print_loc(const source_location& loc = source_location::current()) {
    cout << "file: " << loc.file_name() << "\n";
    cout << "function: " << loc.function_name() << "\n";
    cout << "line: " << loc.line() << "\n";
    cout << "column: " << loc.column() << "\n";
}

int main() {
    print_loc();
    return 0;
}
第十七章 头文件使用要点

1. 包含方式

#include <iostream> 标准库用尖括号

#include "myheader.h" 自己写的用双引号

2. 防止重复包含

方式一(推荐):#pragma once

方式二(标准):

#ifndef MYHEADER_H

#define MYHEADER_H

// 内容

#endif

3. using namespace std

- 小程序/练习可以用,省事

- 大项目/头文件中不要用,避免命名冲突

- 头文件里绝对不要写 using namespace std

4. 常用最小包含

最基础:#include <iostream>

竞赛常用(GCC):#include <bits/stdc++.h>

(包含所有标准头,编译慢,不推荐工程用)

5. C++ 版本速查

版本 主要新增特性

C++98 基础STL(vector、map、string、algorithm)

C++11 auto、lambda、智能指针、thread、unordered_map、

array、tuple、chrono、regex

C++14 make_unique、泛型lambda、变量模板

C++17 filesystem、optional、variant、string_view、

结构化绑定、if constexpr

C++20 concepts、ranges、format、coroutine、span、

<=>、numbers、source_location

C++23 expected、print、generator、mdspan、flat_map

(文档结束)