C++ 教程

C++ 是 C 语言的超集,由 Bjarne Stroustrup 于 1985 年发布,在 C 的基础上增加了面向对象、泛型、STL 标准库等特性。它以高性能著称,是系统开发、游戏引擎、高频交易等领域的王者。

C++ 的核心特性

特性说明
面向对象类、继承、多态、封装
泛型编程模板(Template)
STL 标准库容器、算法、迭代器
内存控制指针、引用、智能指针
高性能接近硬件的执行效率

第一个 C++ 程序

#include <iostream>
using namespace std;

int main() {
    cout << "Hello, CodeNest!" << endl;
    return 0;
}

编译与运行

# g++ 编译
g++ hello.cpp -o hello
./hello

# 或 clang++
clang++ hello.cpp -o hello

C++ 的应用领域

  • 操作系统、驱动程序
  • 游戏引擎(Unreal、Unity 底层)
  • 浏览器内核(Chrome、Firefox)
  • 数据库(MySQL、MongoDB)
  • 高频交易、嵌入式系统
  • AI 框架底层(TensorFlow、PyTorch)

版本演进

版本年份亮点
C++981998首个标准
C++112011auto、智能指针、Lambda(划时代)
C++142014泛型 Lambda 等小改进
C++172017if constexpr、filesystem
C++202020概念、协程、范围
C++232023模块、打印库
💡 提示

C++ 是一门"能驾驭底层、也能写抽象"的语言。学习难度较高,但掌握后收益极大。

C++ 简介

C++ 简介:C++ 的历史、与 C 的关系、设计哲学。

C++ 的诞生

  • 1979 年 Bjarne Stroustrup 在贝尔实验室开始研发
  • 最初叫 "C with Classes"(带类的 C)
  • 1983 年正式改名 C++(++ 是 C 的自增运算符)
  • 1985 年发布首个商用版本

C++ vs C

对比项CC++
编程范式面向过程面向对象 + 泛型 + 过程
标准库C 标准库C 库 + STL
内存管理malloc/freenew/delete + RAII
类型安全较弱更强(模板、强类型)
适用嵌入式、内核复杂系统、应用

C++ 的设计哲学

  • 零成本抽象:高级特性不牺牲性能
  • 兼容 C:C 代码可直接编译
  • 多范式:不强制一种风格
  • 信任程序员:给你控制权,也让你负责

C++ 的编程范式

  1. 面向过程(函数)
  2. 面向对象(类与对象)
  3. 泛型编程(模板)
  4. 函数式(Lambda、算法)
  5. 元编程(模板元)

为什么学 C++

  • 深入理解计算机底层原理
  • 高性能应用开发的必备技能
  • 游戏、AI、数据库行业刚需
  • 面试算法题常用语言
💡 提示

C++ 难在"选择多"(同样任务多种写法)。学习时先掌握一种主流写法,再逐步探索其他范式。

C++ 环境设置

配置 C++ 开发环境:编译器、构建工具、IDE。

安装编译器

# macOS:安装 Xcode Command Line Tools(含 clang++)
xcode-select --install

# Ubuntu/Debian
sudo apt install g++ build-essential

# Windows:安装 MinGW-w64 或使用 Visual Studio

验证编译器

g++ --version
# 或
clang++ --version

编写并编译第一个程序

// hello.cpp
#include <iostream>

int main() {
    std::cout << "Hello C++" << std::endl;
    return 0;
}
g++ hello.cpp -o hello    # 编译
./hello                   # 运行

常用编译选项

g++ main.cpp -o app                    # 编译
g++ main.cpp -o app -std=c++17         # 指定标准
g++ main.cpp -o app -O2                # 优化等级
g++ main.cpp -o app -Wall -Wextra      # 开启警告
g++ main.cpp -o app -g                 # 调试信息
g++ a.cpp b.cpp -o app                 # 多文件编译

IDE 选择

工具特点
CLionJetBrains 出品,C++ 首选(付费)
Visual StudioWindows 最强
VS Code + 插件轻量免费(C/C++ 扩展)
Code::Blocks免费跨平台

构建工具

  • Make / CMake:跨平台构建
  • Bazel / Ninja:大型项目
  • vcpkg / Conan:依赖管理
💡 提示

入门建议:VS Code + g++ 即可;项目变大后用 CMake 管理构建,这是 C++ 业界标准。

C++ 基本语法

掌握 C++ 基本语法:程序结构、语句、标识符。

程序基本结构

#include <iostream>       // 预处理器指令:引入头文件

int main() {              // 主函数:程序入口
    // 语句
    std::cout << "Hello" << std::endl;
    return 0;             // 返回值:0 表示成功
}

语句与分号

int a = 10;      // 每条语句以分号结尾
int b = 20;
int sum = a + b;
// 分号遗漏是初学者最常见的编译错误

标识符规则

  • 字母、数字、下划线组成
  • 不能以数字开头
  • 区分大小写
  • 不能是关键字(int、class、if 等)

注释

// 单行注释

/*
 * 多行注释
 */

/**
 * 文档注释(配合 Doxygen)
 */

命名规范

场景规范示例
变量/函数小驼峰或下划线userName / user_name
类名大驼峰StudentManager
常量全大写MAX_SIZE

使用 std:: 命名空间

// 方式一:全限定名(推荐,清晰)
std::cout << "Hello" << std::endl;

// 方式二:using 声明
using std::cout;
using std::endl;
cout << "Hello" << endl;

// 方式三:整个命名空间(不推荐,污染)
using namespace std;
cout << "Hello" << endl;
💡 提示

小项目用 using namespace std; 图方便;大项目推荐 std:: 前缀,避免命名冲突。

C++ 注释

C++ 注释:注释的三种写法与最佳实践。

三种注释

// 单行注释:两个斜杠

/* 多行注释
   可以跨行 */

/**
 * Doxygen 文档注释
 * 可生成 API 文档
 */

Doxygen 注释

/**
 * 计算两个整数的和
 * @param a 第一个加数
 * @param b 第二个加数
 * @return a + b 的结果
 */
int add(int a, int b) {
    return a + b;
}

注释的最佳实践

  • 解释"为什么",而非"是什么"
  • 复杂算法写清思路
  • TODO 标记待办:// TODO: 优化此处
  • FIXME 标记问题:// FIXME: 边界情况未处理

生成文档

# 安装 doxygen 后生成文档
doxygen Doxyfile
💡 提示

好的注释是"写给未来的自己和其他开发者"的。命名清晰时少写废话注释;逻辑复杂时多写说明。

C++ 数据类型

掌握 C++ 数据类型:基本类型、大小与范围。

基本数据类型

类型大小(64位)范围
bool1 字节true/false
char1 字节-128 ~ 127
short2 字节-32768 ~ 32767
int4 字节±21 亿
long4/8 字节取决于平台
long long8 字节极大
float4 字节约 7 位精度
double8 字节约 15 位精度
wchar_t4 字节宽字符(Unicode)

修饰符

signed int      // 有符号(默认)
unsigned int    // 无符号(非负)
short int       // 短整型
long int        // 长整型
unsigned long long  // 组合

类型大小查询

#include <iostream>
using namespace std;

int main() {
    cout << "int: " << sizeof(int) << " 字节" << endl;
    cout << "double: " << sizeof(double) << " 字节" << endl;
    cout << "int 范围: " << INT_MIN << " ~ " << INT_MAX << endl;
    return 0;
}

auto 类型推导(C++11+)

auto x = 42;        // int
auto y = 3.14;      // double
auto name = "C++";  // const char*
auto v = {1, 2, 3}; // initializer_list

类型转换

// 隐式转换
int i = 3.99;         // 3(截断)

// 静态转换(推荐)
double d = 3.99;
int n = static_cast<int>(d);   // 3

// C 风格(不推荐)
int n2 = (int)d;
💡 提示

选择类型原则:够用就好。计数用 int,金额/精度用 double,大数用 long long,状态用 bool。

C++ 变量类型

掌握 C++ 变量类型:变量声明、初始化与作用域。

变量声明与初始化

// 声明
int age;
double price;
string name;

// 初始化(多种方式)
int a = 42;              // 拷贝初始化
int b(42);               // 直接初始化
int c{42};               // 列表初始化(推荐,防窄化)
int d = {42};            // 拷贝列表初始化

// 多条声明
int x = 1, y = 2, z = 3;

变量类型

// 局部变量:函数内
void func() {
    int local = 10;      // 局部变量
}

// 全局变量:所有函数外
int global = 100;

// 静态变量:static
static int counter = 0;  // 生命周期到程序结束

// 常量
const double PI = 3.14159;   // 不可修改
constexpr int MAX = 100;     // 编译期常量

auto 与 decltype

auto num = 42;              // int
auto pi = 3.14;             // double

decltype(pi) another = 2.5; // double(取类型)

// 返回值类型推导
auto add(int a, int b) -> int {  // 尾置返回
    return a + b;
}

变量作用域

#include <iostream>
using namespace std;

int global = 100;    // 全局作用域

int main() {
    int local = 10;  // 局部作用域

    {
        int block = 5;   // 块作用域
        cout << block << endl;
    }
    // cout << block;   // ❌ 块外不可访问

    cout << local << " " << global << endl;
    return 0;
}

extern 声明

// file1.cpp
int sharedValue = 42;

// file2.cpp
extern int sharedValue;   // 引用其他文件的变量
💡 提示

现代 C++ 推荐 列表初始化 {}:能防止窄化转换(如 double 转 int 会报错),更安全。

C++ 变量作用域

掌握 C++ 变量作用域:局部、全局、块级与命名空间作用域。

作用域类型

作用域位置生命周期
全局所有函数外程序运行期间
局部函数内函数调用期间
块级{ } 内代码块执行期间
命名空间namespace 内程序运行期间

示例

#include <iostream>
using namespace std;

int globalVar = 1;    // 全局变量

namespace Math {
    int globalVar = 2;   // 命名空间中的变量
}

int main() {
    int localVar = 3;    // 局部变量
    cout << globalVar << endl;      // 1(全局)
    cout << Math::globalVar << endl; // 2(命名空间)
    cout << localVar << endl;       // 3

    {
        int blockVar = 4;
        cout << blockVar << endl;
    }
    // cout << blockVar;   // ❌ 块作用域外不可见
    return 0;
}

名字遮蔽(Shadowing)

int value = 10;      // 全局

int main() {
    int value = 20;  // 局部遮蔽全局
    cout << value << endl;       // 20(局部优先)
    cout << ::value << endl;     // 10(:: 访问全局)
    return 0;
}

static 变量

void counter() {
    static int count = 0;   // 只初始化一次,函数调用间保留
    count++;
    cout << count << endl;
}

counter();  // 1
counter();  // 2
counter();  // 3

作用域最佳实践

  • 变量最小作用域:靠近使用处声明
  • 避免全局变量(难维护、线程不安全)
  • 避免名字遮蔽
  • 循环变量在循环内声明
💡 提示

变量声明越靠近使用处越好,作用域越小越好——这是代码可维护性的重要原则。

C++ 常量

掌握 C++ 常量:const、constexpr、字面量。

字面量常量

42          // int
3.14        // double
3.14f       // float
'A'         // char
"hello"     // const char*(字符串字面量)
true        // bool
10000000000LL  // long long

const 常量

const double PI = 3.14159;   // 运行时常量
const int MAX_ITEMS = 100;

// const 指针
const int* p1 = &x;    // 指向常量的指针:不能改 *p1
int* const p2 = &x;    // 常量指针:不能改 p2 指向
const int* const p3 = &x;  // 都不能改

constexpr(编译期常量,C++11+)

constexpr int MAX = 100;          // 编译期确定
constexpr double PI = 3.14159;
constexpr int square(int x) {     // constexpr 函数
    return x * x;
}

int arr[square(5)];   // 编译期数组大小 ✅

// 对比:
// const:运行时常量
// constexpr:编译期常量(可优化更好)

enum 枚举常量

enum Color { RED, GREEN, BLUE };    // 0,1,2
enum Status { OK = 200, NOT_FOUND = 404 };

// 强类型枚举(推荐,C++11+)
enum class Level { LOW, MEDIUM, HIGH };
Level l = Level::HIGH;    // 必须用作用域符

define 宏常量(避免)

// ❌ 旧风格(无类型检查)
#define MAX_SIZE 100

// ✅ 现代风格(类型安全)
constexpr int MAX_SIZE = 100;
💡 提示

现代 C++ 原则:用 const/constexpr 替代 #define,用 enum class 替代裸 enum——类型安全且可调试。

C++ 修饰符类型

掌握 C++ 修饰符类型:signed/unsigned/short/long 等类型修饰符。

类型修饰符

修饰符作用示例
signed有符号(可负)signed int
unsigned无符号(非负,范围加倍)unsigned int
short缩短short int
long加长long int、long long

使用示例

unsigned int u = 42;          // 0 ~ 4294967295
signed int s = -42;           // 有符号
unsigned long long big = 18446744073709551615ULL;
short small = 100;            // 省内存

⚠️ unsigned 的坑

unsigned int a = 1;
int b = -2;
if (a > b) {
    cout << "a > b" << endl;    // 不打印!
} else {
    cout << "a <= b" << endl;   // 实际执行这里!
}
// 原因:a 转成 unsigned,-2 变成巨大正数

// 数组大小、容器大小常用 size_t(unsigned)
vector<int> v = {1,2,3};
for (size_t i = 0; i < v.size(); i++) { }

const 修饰符

const int a = 10;        // 常量
const string s = "hi";   // 常量字符串

// 成员函数 const:承诺不修改对象
class Box {
public:
    int getSize() const { return size; }   // const 成员函数
private:
    int size = 0;
};

volatile 修饰符(少用)

volatile int flag = 0;   // 告诉编译器不要优化该变量
// 硬件寄存器、信号处理器场景使用
⚠️ 注意

unsigned 与 signed 混用比较会产生意外结果。循环计数、数组索引等非负场景用 unsigned/size_t 要格外小心。

C++ 存储类

掌握 C++ 存储类:auto、static、extern、register、thread_local。

存储类总览

存储类作用
auto自动存储(默认,C++11 起表示类型推导)
static静态存储(生命周期=程序)
extern声明外部变量/函数
register请求寄存器存储(已废弃)
thread_local线程局部存储(C++11+)
mutableconst 成员函数中可修改

static 的三种用法

// 1. 静态局部变量:函数调用间保留
void counter() {
    static int n = 0;
    n++;
    cout << n << endl;
}

// 2. 静态全局/成员:仅本文件/类共享
static int fileScope = 42;    // 仅本文件可见

class Config {
public:
    static int version;       // 静态成员声明
    static void print() {}    // 静态成员函数
};
int Config::version = 1;      // 静态成员定义

// 3. 静态类对象:初始化一次
class Logger {
public:
    static Logger& instance() {   // 单例模式
        static Logger logger;     // 首次调用时创建
        return logger;
    }
};

extern

// a.cpp
int global = 42;

// b.cpp
extern int global;    // 使用 a.cpp 的变量
void f() {
    cout << global << endl;
}

// 头文件中的 extern 声明(多文件项目)
// shared.h
extern int sharedCounter;

thread_local(C++11+)

thread_local int threadId;   // 每个线程独立副本

void worker() {
    threadId = ...;          // 各线程互不影响
}

mutable

class Cache {
public:
    int get(int key) const {
        mutableCache[key] = ...;   // const 函数中修改 mutable
        return mutableCache[key];
    }
private:
    mutable map<int,int> mutableCache;
};
💡 提示

static 是 C++ 高频关键词:静态局部变量(状态保持)、静态成员(类级数据)、静态函数(文件私有)三种用途要分清。

C++ 运算符

掌握 C++ 运算符:算术、关系、逻辑、位运算、赋值。

算术运算符

int a = 10, b = 3;
a + b    // 13
a - b    // 7
a * b    // 30
a / b    // 3(整数除法)
a % b    // 1(取余)
++a      // 前置自增
a++      // 后置自增

关系与逻辑

a > b    a < b    a >= b    a <= b
a == b   a != b
a && b   // 逻辑与
a || b   // 逻辑或
!a       // 逻辑非

位运算符

int x = 6;   // 110
x & 3        // 2 (010) 与
x | 3        // 7 (111) 或
x ^ 3        // 5 (101) 异或
~x           // 取反
x << 1       // 12 左移(乘2)
x >> 1       // 3  右移(除2)

赋值运算符

int n = 10;
n += 5;    // n = 15
n -= 3;    // 12
n *= 2;    // 24
n /= 4;    // 6
n %= 5;    // 1
n <<= 1;   // 左移赋值

三目运算符

int age = 20;
string s = (age >= 18) ? "成年" : "未成年";

sizeof 与类型转换

sizeof(int)              // 4
sizeof(arr) / sizeof(arr[0])  // 数组长度

// 静态转换(推荐)
double d = 3.7;
int n = static_cast<int>(d);   // 3

运算符优先级

() 最高 → 一元(++ -- !) → 算术(* / %) → 算术(+ -)
→ 关系(< >) → 相等(== !=) → 逻辑与(&&) → 逻辑或(||)
→ 赋值(=) 最低
💡 提示

优先级记不住没关系——多用括号明确顺序,可读性远比"少写括号"重要。

C++ 循环

掌握 C++ 循环:for、while、do-while 与范围 for。

for 循环

for (int i = 0; i < 5; i++) {
    cout << i << " ";
}
// 0 1 2 3 4

// 递减
for (int i = 5; i > 0; i--) {
    cout << i << " ";
}
// 5 4 3 2 1

while 循环

int n = 0;
while (n < 5) {
    cout << n << " ";
    n++;
}
// 0 1 2 3 4

do-while(至少执行一次)

int n = 10;
do {
    cout << n << " ";    // 即使条件不满足也执行一次
    n++;
} while (n < 5);
// 10

范围 for(C++11+,推荐)

vector<int> v = {1, 2, 3, 4};
for (int x : v) {
    cout << x << " ";
}

// 引用遍历(可修改 + 高效)
for (int& x : v) {
    x *= 2;    // 修改原值
}

// const 引用(只读 + 高效)
for (const auto& x : v) {
    cout << x << " ";
}

// 遍历字符串
for (char c : "hello") {
    if (c == '') break;
    cout << c;
}

break / continue

for (int i = 0; i < 10; i++) {
    if (i % 2 == 0) continue;   // 跳过偶数
    if (i > 7) break;           // 超过 7 停止
    cout << i << " ";           // 1 3 5 7
}

嵌套循环

for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= i; j++) {
        cout << "*";
    }
    cout << endl;
}
// *
// **
// ***
💡 提示

遍历容器优先范围 for;需要索引时用传统 for;循环条件不明确用 while。

C++ 判断

掌握 C++ 判断语句:if、if-else、switch 与三目。

if 语句

int score = 85;
if (score >= 60) {
    cout << "及格" << endl;
}

if-else

if (score >= 60) {
    cout << "及格";
} else {
    cout << "不及格";
}

if-else if 链

if (score >= 90)      cout << "优秀";
else if (score >= 80) cout << "良好";
else if (score >= 60) cout << "及格";
else                  cout << "不及格";

switch 语句

int day = 3;
switch (day) {
    case 1: cout << "周一"; break;
    case 2: cout << "周二"; break;
    case 3: cout << "周三"; break;
    default: cout << "其他";
}
// break 不可省略,否则会"穿透"

三目运算符

int age = 20;
string type = (age >= 18) ? "成年" : "未成年";

// 嵌套三目(可读性差,少用)
int a = 10, b = 20, c = 30;
int max = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c);

逻辑组合

if (age >= 18 && age <= 60) { /* 工作年龄 */ }
if (city == "北京" || city == "上海") { /* 一线 */ }
if (!isDeleted) { /* 未删除 */ }

初始化语句(C++17+)

// if 初始化语句:变量作用域限在 if 内
if (auto it = m.find("key"); it != m.end()) {
    cout << "找到: " << it->second;
} else {
    cout << "未找到";
}
💡 提示

条件多分支用 switch(编译优化好);简单二选一用三目;复杂判断用 if-else if 保证可读性。

C++ 函数

掌握 C++ 函数:定义、参数、返回值、重载。

函数定义与调用

// 返回类型 函数名(参数列表) { 函数体 }
int add(int a, int b) {
    return a + b;
}

int main() {
    int result = add(3, 4);   // 调用
    cout << result << endl;   // 7
    return 0;
}

函数声明(原型)

// 先声明,后定义(用于多文件/互相调用)
int multiply(int a, int b);    // 声明

int main() {
    cout << multiply(3, 4);    // 可用
    return 0;
}

int multiply(int a, int b) {   // 定义
    return a * b;
}

参数传递

// 值传递(副本,不影响原值)
void byValue(int x) { x = 100; }

// 引用传递(直接操作原值,推荐)
void byRef(int& x) { x = 100; }

// 指针传递
void byPtr(int* x) { *x = 100; }

int main() {
    int n = 0;
    byValue(n);   // n 仍为 0
    byRef(n);     // n 变为 100
    byPtr(&n);    // n 变为 100
    return 0;
}

默认参数

void greet(string name, string greeting = "你好") {
    cout << greeting << "," << name << endl;
}

greet("张三");              // 你好,张三
greet("李四", "Hello");     // Hello,李四
// 默认参数只能从右往左连续设置

函数重载

int max(int a, int b) { return a > b ? a : b; }
double max(double a, double b) { return a > b ? a : b; }

max(1, 2);        // int 版本
max(1.5, 2.5);    // double 版本

内联函数

// inline:建议编译器内联展开(小函数)
inline int square(int x) {
    return x * x;
}

Lambda(C++11+)

// [捕获](参数) -> 返回类型 { 函数体 }
auto add = [](int a, int b) -> int {
    return a + b;
};
cout << add(2, 3) << endl;   // 5

// 捕获外部变量
int base = 10;
auto addBase = [base](int x) { return base + x; };
cout << addBase(5) << endl;  // 15
💡 提示

传递大型对象(string/vector/自定义类)用 const 引用;需要修改用引用;只有小基本类型才值传递。

C++ 数字

掌握 C++ 数字处理:数值类型、数学函数、随机数。

数字类型

int a = 42;          // 整数
double b = 3.14;     // 双精度
float c = 2.5f;      // 单精度
long long d = 10000000000LL;
unsigned int e = 100;

// 十六进制/八进制/二进制
int hex = 0xFF;      // 255
int oct = 017;       // 15
int bin = 0b1010;    // 10 (C++14+)

数学函数(<cmath>)

#include <cmath>

abs(-10);          // 10 绝对值
fabs(-3.5);        // 3.5 浮点绝对值
sqrt(16);          // 4.0 平方根
pow(2, 10);        // 1024.0 幂
ceil(3.2);         // 4.0 向上取整
floor(3.8);        // 3.0 向下取整
round(3.5);        // 4.0 四舍五入
fmod(10, 3);       // 1.0 浮点取余
log(2.71828);      // 1.0 自然对数
log10(100);        // 2.0
sin(M_PI / 2);     // 1.0
cos(0);            // 1.0
tan(0);            // 0.0

随机数(<random>,C++11+)

#include <random>

// 现代方式
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dist(1, 100);   // 1-100 均匀分布
int randomNum = dist(gen);

// 浮点随机
uniform_real_distribution<double> fdist(0.0, 1.0);
double r = fdist(gen);

// 旧方式(rand,了解即可)
srand(time(nullptr));
int old = rand() % 100 + 1;    // 1-100

数值转换

#include <string>

// 字符串 → 数字
int n = stoi("42");          // 42
double d = stod("3.14");     // 3.14
long l = stol("1000000");

// 数字 → 字符串
string s1 = to_string(42);       // "42"
string s2 = to_string(3.14);     // "3.140000"

精度问题

// 浮点比较不要用 ==
double x = 0.1 + 0.2;    // 0.30000000000000004

// 用误差范围比较
const double EPS = 1e-9;
if (fabs(x - 0.3) < EPS) {
    cout << "相等" << endl;
}
💡 提示

金融计算考虑定点数或整数分;随机数用 <random> 库(rand 有偏且弱);浮点比较用误差。

C++ 数组

掌握 C++ 数组:一维、二维、数组与指针关系。

一维数组

// 声明与初始化
int scores[5];                    // 未初始化(随机值)
int scores[5] = {90, 85, 95};     // 前 3 个初始化,其余 0
int nums[] = {1, 2, 3, 4};        // 自动推导大小 4
int zeros[5] = {};                // 全 0

// 访问
scores[0] = 100;                  // 修改
cout << scores[0];                // 读取
int size = sizeof(scores) / sizeof(scores[0]);   // 元素个数

// 遍历
for (int i = 0; i < size; i++) {
    cout << scores[i] << " ";
}
for (int x : scores) {            // 范围 for
    cout << x << " ";
}

二维数组

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

matrix[1][2];    // 7

// 遍历
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

字符数组(C 风格字符串)

char name[] = "CodeNest";       // 自动加 ''
char name2[20] = "Hello";

// 需要 <cstring>
#include <cstring>
strlen(name);        // 长度(不含 )
strcpy(dest, src);   // 复制
strcmp(a, b);        // 比较(0 相等)

// 现代 C++ 用 std::string(推荐)
string s = "CodeNest";

数组与指针

int arr[5] = {1, 2, 3, 4, 5};
int* p = arr;         // 数组名即首地址
p[0];                 // 1(指针可下标访问)
*(p + 2);             // 3(指针算术)
p++;                  // 指向下一个元素

// 数组作参数退化为指针
void printArr(int arr[], int size) { }   // 等价 int* arr

std::array(C++11+,推荐)

#include <array>

array<int, 5> a = {1, 2, 3, 4, 5};
a.size();          // 5(安全)
a.at(0);           // 带边界检查
a.front();         // 1
a.back();          // 5
// 大小固定、连续内存,比裸数组安全
💡 提示

固定大小用 std::array,动态大小用 std::vector,裸 C 数组仅用于性能敏感或与 C 交互。

C++ 字符串

掌握 C++ 字符串:std::string 的全面用法。

std::string 基础

#include <string>

string s1;                 // 空字符串
string s2 = "hello";       // 初始化
string s3("world");
string s4(5, 'a');         // "aaaaa"

// 拼接
string full = s2 + " " + s3;   // "hello world"
s2 += "!";                     // "hello!"

// 长度
s2.size();     // 6
s2.length();   // 6(等价)
s2.empty();    // 是否为空

访问与遍历

string s = "CodeNest";

s[0];            // 'C'(不检查边界)
s.at(1);         // 'o'(越界抛异常)
s.front();       // 'C'
s.back();        // 't'

for (char c : s) {
    cout << c;
}
for (size_t i = 0; i < s.size(); i++) {
    cout << s[i];
}

查找与截取

string s = "Hello CodeNest";

s.find("Code");       // 6(找不到返回 string::npos)
s.find("X");          // string::npos(很大的数)
s.rfind("l");         // 从后找
s.substr(6);          // "CodeNest"
s.substr(0, 5);       // "Hello"

if (s.find("Code") != string::npos) {
    cout << "找到" << endl;
}

修改与替换

string s = "hello";

s.insert(0, "say ");       // "say hello"
s.replace(4, 5, "bye");    // "say bye"
s.erase(0, 4);             // "bye"
s.push_back('!');          // "bye!"
s.pop_back();              // "bye"
s.clear();                 // 清空

// 大小写(需要 algorithm 或手写)
transform(s.begin(), s.end(), s.begin(), ::toupper);

C 风格字符串转换

// string → const char*
string s = "hello";
const char* cstr = s.c_str();
const char* data = s.data();

// const char* → string
string back = cstr;

string vs char*

对比项std::stringchar*
自动管理✅(自动扩容/释放)❌(手动管理)
操作丰富(拼接/查找/截取)少(cstring 函数)
安全越界检查(at)易溢出
推荐✅ 现代 C++C 兼容场景
💡 提示

现代 C++ 一律用 std::string;仅与 C 库交互时转 c_str()。

C++ 指针

掌握 C++ 指针:内存地址与间接访问的核心概念。

指针基础

int value = 42;
int* ptr = &value;    // & 取地址,ptr 存 value 的地址

cout << ptr;          // 地址(如 0x7ffe...)
cout << *ptr;         // 42(* 解引用,取指向的值)

*ptr = 100;           // 通过指针修改
cout << value;        // 100

空指针与野指针

// 空指针:不指向任何对象
int* p = nullptr;         // 现代 C++(推荐)
// int* p = NULL;         // C 风格
// int* p = 0;            // 旧式

if (p != nullptr) {
    cout << *p;
}

// 野指针:未初始化的指针(危险!)
// int* bad;              // 未初始化,指向未知内存
// *bad = 10;             // ❌ 未定义行为!

指针运算

int arr[] = {10, 20, 30};
int* p = arr;         // 指向 arr[0]

p++;                  // 指向 arr[1]
*p;                   // 20
*(p + 1);             // 30
p - arr;              // 元素偏移量

指针与数组

int arr[3] = {1, 2, 3};
int* p = arr;         // 数组名即首元素地址

// 三种等价访问
arr[1];    // 2
*(arr+1);  // 2
p[1];      // 2

指针与函数

// 指针参数:修改调用方的变量
void swap(int* a, int* b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int x = 1, y = 2;
swap(&x, &y);     // x=2, y=1

// 函数指针
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = add;
funcPtr(3, 4);    // 7

指针 vs 引用

对比项指针引用
可空✅(nullptr)❌(必须绑定)
可改绑✅❌
语法* 和 &自动
安全需小心更安全
💡 提示

能用引用用引用;需要"可空/可重定向"才用指针。C++11+ 用 unique_ptr/shared_ptr 管理动态内存。

C++ 引用

掌握 C++ 引用:变量的别名。

引用基础

int value = 42;
int& ref = value;    // ref 是 value 的别名

ref = 100;           // 修改 ref 即修改 value
cout << value;       // 100

// 引用必须初始化,不能改绑
// int& bad;          // ❌ 必须初始化

引用作函数参数(最常用)

// 值传递:拷贝(慢,不修改原值)
void byValue(string s) { s += "!"; }

// 引用传递:直接操作(快,可修改)
void byRef(string& s) { s += "!"; }

// const 引用:快且只读(推荐)
void printStr(const string& s) {
    cout << s;    // 只读,不拷贝
}

string msg = "hello";
byValue(msg);   // msg 不变
byRef(msg);     // msg = "hello!"
printStr(msg);  // 高效只读

引用返回值

// 返回引用:避免拷贝,可链式调用
class Counter {
    int count = 0;
public:
    Counter& add() {   // 返回自身引用
        count++;
        return *this;
    }
    int get() const { return count; }
};

Counter c;
c.add().add().add();    // 链式调用
c.get();                // 3

右值引用(C++11+)

// 左值:有名字、可取地址
int x = 10;      // x 是左值

// 右值:临时对象
// 10 是右值
// f() 返回值可能是右值

// 右值引用 &&:移动语义
string getTemp() { return "temp"; }

string a = getTemp();   // 移动构造(C++11 自动)

// 显式移动
string b = "hello";
string c = std::move(b);   // b 被移动(内容转移)

引用 vs 指针速查

场景推荐
函数参数(大型对象)const 引用
需要修改调用方变量引用
可空/可选参数指针
动态内存智能指针
💡 提示

引用是 C++ 的高频特性:const 引用传参 + 引用返回是写高效 C++ 的基本功。

C++ 日期 & 时间

掌握 C++ 日期与时间:<ctime> 与 <chrono>。

ctime(C 风格)

#include <ctime>
#include <iostream>
using namespace std;

int main() {
    // 当前时间戳(秒)
    time_t now = time(nullptr);
    cout << now << endl;   // 秒数

    // 转本地时间
    tm* local = localtime(&now);
    cout << local->tm_year + 1900 << "年"  // 年
         << local->tm_mon + 1 << "月"        // 月(0 起)
         << local->tm_mday << "日" << endl;

    // 格式化输出
    char buf[80];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
    cout << buf << endl;   // 2026-08-11 14:30:00
    return 0;
}

chrono(现代 C++,推荐)

#include <chrono>
using namespace std::chrono;

// 当前时间点
auto now = system_clock::now();

// 转时间戳
auto ts = system_clock::to_time_t(now);

// 转成可读时间
std::tm* t = std::localtime(&ts);

// 计时(高频场景)
auto start = high_resolution_clock::now();
// ... 执行代码 ...
auto end = high_resolution_clock::now();
auto duration = duration_cast<milliseconds>(end - start);
cout << "耗时: " << duration.count() << " ms" << endl;

时间单位

using namespace std::chrono;

nanoseconds    // 纳秒
microseconds   // 微秒
milliseconds   // 毫秒
seconds        // 秒
minutes        // 分
hours          // 时

// 单位转换
auto ms = duration_cast<milliseconds>(seconds(1));
// 1000ms

计时实用工具

// 简单计时器
class Timer {
    steady_clock::time_point start;
public:
    Timer() : start(steady_clock::now()) {}

    double elapsed_ms() {
        auto end = steady_clock::now();
        return duration_cast<milliseconds>(end - start).count();
    }
};

Timer t;
// 执行代码...
cout << t.elapsed_ms() << " ms" << endl;
💡 提示

计时/时间操作首选 <chrono>(类型安全、精度高、跨平台);<ctime> 用于格式化展示。

C++ 基本的输入输出

掌握 C++ 输入输出:cin、cout、cerr、文件流。

标准输出 cout

#include <iostream>
using namespace std;

cout << "Hello";             // 输出字符串
cout << 42;                  // 输出数字
cout << "值: " << 3.14;      // 链式输出
cout << endl;                // 换行(刷新缓冲区)
cout << "a" << "
";         // 换行(更快)

标准输入 cin

int age;
string name;

cout << "输入姓名: ";
cin >> name;         // 读取一个词(遇空白停止)

cout << "输入年龄: ";
cin >> age;

// 读取整行(含空格)
string line;
getline(cin, line);

// 注意:cin >> 后接 getline 需先清空缓冲区
cin.ignore(numeric_limits<streamsize>::max(), '
');

输入校验

int num;
cout << "输入数字: ";
if (cin >> num) {
    cout << "收到: " << num;
} else {
    cout << "输入无效" << endl;
    cin.clear();              // 清除错误状态
    cin.ignore(10000, '
');  // 清空缓冲
}

cerr / clog

cerr << "错误信息" << endl;   // 错误输出(不缓冲)
clog << "日志信息" << endl;    // 日志输出(缓冲)

格式化输出

#include <iomanip>

double pi = 3.14159265;

cout << fixed << setprecision(2) << pi;   // 3.14
cout << setw(10) << setfill('0') << 42;   // 0000000042
cout << hex << 255;                       // ff(十六进制)
cout << boolalpha << true;                // true

文件输入输出

#include <fstream>

// 写文件
ofstream out("data.txt");
out << "第一行" << endl;
out << 42 << endl;
out.close();          // 关闭

// 读文件
ifstream in("data.txt");
string line;
while (getline(in, line)) {
    cout << line << endl;
}
in.close();
💡 提示

endl 会刷新缓冲区(慢),大批量输出用 " ";文件读写用 RAII 方式(对象析构自动关闭)。

C++ 结构体(struct)

掌握 C++ 结构体(struct):组合不同类型的数据。

定义与使用

#include <iostream>
using namespace std;

// 结构体定义
struct Student {
    string name;
    int age;
    double score;
};

int main() {
    // 初始化
    Student s1;
    s1.name = "张三";
    s1.age = 20;
    s1.score = 88.5;

    // 聚合初始化
    Student s2 = {"李四", 22, 95.0};
    Student s3{"王五", 21, 76.5};   // 列表初始化

    // 访问
    cout << s2.name << " " << s2.score << endl;
    return 0;
}

结构体数组

Student students[3] = {
    {"张三", 20, 88},
    {"李四", 22, 95},
    {"王五", 21, 76}
};

// 遍历
for (const auto& s : students) {
    cout << s.name << ": " << s.score << endl;
}

结构体与函数

// 传引用(高效)
void printStudent(const Student& s) {
    cout << s.name << "," << s.age << "岁";
}

// 返回结构体
Student makeStudent(string name, int age) {
    return Student{name, age, 0};
}

// 结构体数组排序
sort(students, students + 3, [](const Student& a, const Student& b) {
    return a.score > b.score;    // 按分数降序
});

结构体 vs 类

对比项structclass
默认访问publicprivate
用途简单数据聚合完整面向对象
方法可有核心

结构体中的方法(C++ 可混合)

struct Point {
    double x, y;

    // 成员函数
    double distance() {
        return sqrt(x * x + y * y);
    }
};

Point p{3, 4};
p.distance();    // 5
💡 提示

简单数据组合用 struct(默认 public);需要封装/继承用 class。C++ 中两者本质相同,仅默认访问权限不同。

C++ vector 容器

掌握 C++ vector 容器:动态数组,最常用的 STL 容器。

创建与基本操作

#include <vector>
using namespace std;

vector<int> v1;                 // 空 vector
vector<int> v2(5);              // 5 个 0
vector<int> v3(5, 10);          // 5 个 10
vector<int> v4 = {1, 2, 3};     // 初始化列表
vector<int> v5(v4);             // 拷贝

// 添加元素
v4.push_back(4);        // 尾部添加
v4.emplace_back(5);     // 尾部构造(高效)

// 访问
v4[0];                  // 不检查边界
v4.at(1);               // 越界抛异常
v4.front();             // 第一个
v4.back();              // 最后一个

// 大小
v4.size();              // 元素个数
v4.empty();             // 是否为空
v4.capacity();          // 容量

修改与删除

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

v.pop_back();            // 删除尾部 → {1,2,3,4}
v.insert(v.begin() + 1, 99);  // 插入 → {1,99,2,3,4}
v.erase(v.begin() + 2);       // 删除 → {1,99,3,4}
v.clear();               // 清空
v.resize(10);            // 调整大小

// 删除所有 3
v.erase(remove(v.begin(), v.end(), 3), v.end());

遍历

vector<int> v = {1, 2, 3};

// 范围 for(推荐)
for (int x : v) { cout << x; }

// 引用遍历(修改)
for (int& x : v) { x *= 2; }

// 索引遍历
for (size_t i = 0; i < v.size(); i++) { }

// 迭代器
for (auto it = v.begin(); it != v.end(); ++it) { }

算法配合

#include <algorithm>

sort(v.begin(), v.end());            // 排序
reverse(v.begin(), v.end());         // 反转
find(v.begin(), v.end(), 3);         // 查找
count(v.begin(), v.end(), 3);        // 计数
max_element(v.begin(), v.end());     // 最大值位置
accumulate(v.begin(), v.end(), 0);   // 求和

// 二维 vector(矩阵)
vector<vector<int>> matrix(3, vector<int>(4, 0));

vector vs 数组

对比项vector数组
大小动态固定
自动管理✅❌
性能略低(极少)最高
推荐✅ 默认选择性能敏感
💡 提示

vector 是 C++ 最常用的容器:动态扩容、内存连续(缓存友好)、与算法库完美配合。

C++ 数据结构

掌握 C++ 数据结构:常见数据结构的 STL 实现。

数据结构总览

结构STL 容器特点
动态数组vector随机访问 O(1)
链表list / forward_list插入删除 O(1)
栈stackLIFO
队列queueFIFO
优先队列priority_queue自动排序取最大
哈希表unordered_map/set查找 O(1)
平衡树map/set有序,O(log n)

栈 stack

#include <stack>

stack<int> st;
st.push(1);        // 入栈
st.push(2);
st.top();          // 2(栈顶)
st.pop();          // 出栈
st.empty();        // 是否空
st.size();         // 大小

队列 queue

#include <queue>

queue<int> q;
q.push(1);         // 入队
q.push(2);
q.front();         // 1(队头)
q.back();          // 2(队尾)
q.pop();           // 出队

优先队列(堆)

#include <queue>

// 最大堆(默认)
priority_queue<int> pq;
pq.push(3); pq.push(1); pq.push(5);
pq.top();          // 5(最大)

// 最小堆
priority_queue<int, vector<int>, greater<int>> minPq;
minPq.top();       // 最小

map(有序键值)

#include <map>

map<string, int> scores;
scores["张三"] = 90;
scores["李四"] = 85;

scores["张三"];            // 90
scores.count("张三");      // 1(是否存在)
scores.find("李四");       // 迭代器
scores.erase("李四");      // 删除

// 遍历(按键升序)
for (const auto& [k, v] : scores) {
    cout << k << ": " << v << endl;
}

unordered_map(哈希,更快)

#include <unordered_map>

unordered_map<string, int> um;
um["apple"] = 5;
um["banana"] = 3;
um["apple"];       // 5(O(1) 查找)

// 需要有序遍历时用 map,只要快速查找用 unordered_map

数据结构选择

  • 要排序/范围查询 → map / set
  • 只要快速查找 → unordered_map / unordered_set
  • 取最大/最小 → priority_queue
  • FIFO → queue;LIFO → stack
  • 动态数组 → vector(默认)
💡 提示

STL 容器是 C++ 面试和实战的基石:会选容器 + 会用算法,事半功倍。

C++ 类 & 对象

掌握 C++ 类与对象:面向对象编程的核心。

定义类

#include <iostream>
using namespace std;

class Student {
private:              // 私有:外部不可直接访问
    string name;
    int age;

public:               // 公有:对外接口
    // 构造方法
    Student(string n, int a) : name(n), age(a) {}

    // 成员函数
    void introduce() {
        cout << "我是 " << name << "," << age << "岁" << endl;
    }

    // getter/setter
    string getName() { return name; }
    void setName(string n) { name = n; }
};

int main() {
    Student s("张三", 20);   // 创建对象(调用构造)
    s.introduce();           // 我是 张三,20岁
    s.setName("李四");
    cout << s.getName();     // 李四
    return 0;
}

构造与析构

class Resource {
public:
    // 构造:初始化
    Resource() {
        cout << "构造" << endl;
        data = new int[100];   // 申请资源
    }

    // 析构:清理(RAII)
    ~Resource() {
        cout << "析构" << endl;
        delete[] data;         // 释放资源
    }

private:
    int* data;
};

初始化列表

class Point {
    double x, y;
public:
    // 初始化列表(推荐,比赋值高效)
    Point(double a, double b) : x(a), y(b) {}
};

this 指针

class Box {
    int size;
public:
    void setSize(int size) {
        this->size = size;   // this->size 是成员
    }
};

访问控制

关键字访问范围
public任何地方
protected类内 + 子类
private仅类内

static 成员

class Counter {
public:
    static int total;          // 静态成员声明
    Counter() { total++; }
};

int Counter::total = 0;        // 静态成员定义(类外)

Counter a, b, c;
Counter::total;    // 3(所有对象共享)
💡 提示

C++ 类的核心思想:封装数据 + RAII 管理资源(构造申请、析构释放),这是 C++ 区别于其他语言的关键。

C++ 继承

掌握 C++ 继承:代码复用与多态的基础。

基本继承

#include <iostream>
using namespace std;

// 基类
class Animal {
protected:
    string name;
public:
    Animal(string n) : name(n) {}

    void eat() {
        cout << name << " 吃东西" << endl;
    }
};

// 派生类:public 继承
class Dog : public Animal {
public:
    Dog(string n) : Animal(n) {}    // 调用基类构造

    void bark() {
        cout << name << " 汪汪叫" << endl;
    }
};

int main() {
    Dog dog("旺财");
    dog.eat();    // 继承的方法
    dog.bark();   // 自己的方法
    return 0;
}

继承方式

继承方式基类 public基类 protected基类 private
public 继承publicprotected不可见
protected 继承protectedprotected不可见
private 继承privateprivate不可见

方法覆盖(多态前提)

class Animal {
public:
    // virtual:虚函数,支持多态
    virtual void speak() {
        cout << "动物叫" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {     // override:覆盖
        cout << "喵喵" << endl;
    }
};

// 多态调用
Animal* a = new Cat();
a->speak();    // 喵喵(虚函数动态绑定)

// 纯虚函数(抽象类)
class Shape {
public:
    virtual double area() = 0;   // = 0:纯虚函数
    virtual ~Shape() {}
};

构造与析构顺序

// 构造:先基类后派生
// 析构:先派生后基类

class Base {
public:
    Base() { cout << "Base 构造" << endl; }
    virtual ~Base() { cout << "Base 析构" << endl; }
    // 注意:基类析构必须是 virtual!否则 delete 派生对象不会调用派生析构
};

class Derived : public Base {
public:
    Derived() { cout << "Derived 构造" << endl; }
    ~Derived() { cout << "Derived 析构" << endl; }
};

继承类型

类型说明
单继承一个子类一个父类(最常见)
多继承一个子类多个父类(谨慎使用)
菱形继承多继承+共享祖先(用 virtual 继承解决)
💡 提示

C++ 继承要点:基类析构要 virtual、覆盖方法加 override、有虚函数就有虚析构——这三条能避免 90% 的坑。

C++ 重载运算符和重载函数

掌握 C++ 重载运算符与重载函数。

函数重载

int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
int add(int a, int b, int c) { return a + b + c; }

add(1, 2);        // int 版本
add(1.5, 2.5);    // double 版本
add(1, 2, 3);     // 3 参数版本

运算符重载

#include <iostream>
using namespace std;

class Vector {
    double x, y;
public:
    Vector(double a, double b) : x(a), y(b) {}

    // 成员函数重载 +
    Vector operator+(const Vector& other) {
        return Vector(x + other.x, y + other.y);
    }

    // 成员函数重载 ==
    bool operator==(const Vector& other) {
        return x == other.x && y == other.y;
    }

    // 友元函数重载 <<(输出)
    friend ostream& operator<<(ostream& os, const Vector& v) {
        os << "(" << v.x << ", " << v.y << ")";
        return os;
    }
};

int main() {
    Vector v1(1, 2), v2(3, 4);
    Vector sum = v1 + v2;    // 调用 operator+
    cout << sum << endl;     // (4, 6)
    return 0;
}

常用可重载运算符

运算符典型用途
+ - * /数学对象(复数、向量)
== != < >比较(排序需要 <)
<< >>流输入输出
[]下标访问
()函数对象(仿函数)
++ --迭代器

重载注意

  • 不能发明新运算符
  • 不改变优先级和结合性
  • 至少一个操作数是自定义类型
  • 保持语义一致(+ 就是加法)
💡 提示

重载 < 即可用 sort/map/set;重载 << 方便调试输出。但要克制——过度重载反而难读。

C++ 多态

掌握 C++ 多态:虚函数与动态绑定。

多态的基础

#include <iostream>
using namespace std;

class Shape {
public:
    // 虚函数:运行时决定调用哪个版本
    virtual double area() {
        return 0;
    }

    // 虚析构(重要!)
    virtual ~Shape() {}
};

class Circle : public Shape {
    double r;
public:
    Circle(double radius) : r(radius) {}

    double area() override {    // 覆盖
        return 3.14159 * r * r;
    }
};

class Rect : public Shape {
    double w, h;
public:
    Rect(double a, double b) : w(a), h(b) {}

    double area() override {
        return w * h;
    }
};

int main() {
    // 基类指针指向派生类对象
    Shape* shapes[] = {new Circle(5), new Rect(4, 6)};

    for (Shape* s : shapes) {
        cout << s->area() << endl;   // 多态:各调各的
    }
    // 78.5398
    // 24

    for (Shape* s : shapes) delete s;   // 虚析构确保正确清理
    return 0;
}

纯虚函数与抽象类

// 抽象类:含纯虚函数,不能实例化
class Animal {
public:
    virtual void speak() = 0;    // 纯虚函数
    virtual ~Animal() {}
};

class Dog : public Animal {
public:
    void speak() override { cout << "汪汪"; }
};

// Animal a;   // ❌ 抽象类不能实例化
Animal* a = new Dog();
a->speak();    // 汪汪

override 与 final

class Base {
public:
    virtual void f() {}
};

class Derived : public Base {
public:
    void f() override {}   // override:明确覆盖(编译器检查)
    // void g() override {} // ❌ 基类无 g(),报错
};

// final:禁止继续覆盖
class Final : public Base {
public:
    void f() override final {}   // 子类不能再覆盖
};

虚函数表(vtable)

有虚函数的类会有虚函数表:调用虚函数时通过 vtable 间接跳转(动态绑定),多一次间接寻址,但换来多态能力。构造函数不能是虚函数。

多态的好处

  • 统一接口,不同实现
  • 代码扩展性(新增类不改调用代码)
  • 实现设计模式的基础
💡 提示

多态三件套:虚函数 + 基类指针/引用 + 派生类覆盖。基类析构务必 virtual,否则内存泄漏。

C++ 数据抽象

掌握 C++ 数据抽象:只暴露接口,隐藏实现细节。

什么是数据抽象

把"做什么"(接口)和"怎么做"(实现)分离:用户只关心接口,不关心内部实现。

示例:栈的抽象

#include <iostream>
#include <vector>
using namespace std;

// 对外只暴露 push/pop/top/isEmpty
class Stack {
private:
    vector<int> data;    // 实现细节(隐藏)

public:
    void push(int x) { data.push_back(x); }

    int pop() {
        int top = data.back();
        data.pop_back();
        return top;
    }

    int top() const { return data.back(); }

    bool isEmpty() const { return data.empty(); }
};

int main() {
    Stack s;
    s.push(1);
    s.push(2);
    cout << s.pop() << endl;    // 2
    // 用户不需要知道内部是 vector 还是数组
    return 0;
}

抽象的好处

好处说明
简化使用接口清晰,无需理解内部
便于修改内部实现可换,接口不变
安全性防止误操作内部状态
可测试通过接口测试

抽象 vs 封装

对比项数据抽象封装
侧重点接口与实现分离数据隐藏
手段public 接口 + private 实现private 成员
关系封装是抽象的实现手段—
💡 提示

抽象层次设计得好,调用方"只看接口不读源码"就能用——这也是接口文档重要的原因。

C++ 数据封装

掌握 C++ 数据封装:信息隐藏与访问控制。

封装的核心

  • private 隐藏数据
  • public 提供受控访问(getter/setter)
  • 保护数据完整性

封装示例

#include <iostream>
using namespace std;

class BankAccount {
private:
    string accountNo;
    double balance;          // 私有:外部不可直接改

public:
    BankAccount(string no, double b) : accountNo(no), balance(b) {}

    // 受控读取
    double getBalance() const { return balance; }

    // 受控写入(带校验)
    bool deposit(double amount) {
        if (amount <= 0) return false;   // 校验
        balance += amount;
        return true;
    }

    bool withdraw(double amount) {
        if (amount > balance) return false;   // 余额不足
        balance -= amount;
        return true;
    }
};

int main() {
    BankAccount acc("12345", 1000);
    // acc.balance = -100;   // ❌ 不能直接改
    acc.deposit(500);        // ✅ 通过方法
    cout << acc.getBalance() << endl;   // 1500
    return 0;
}

三种访问级别

关键字本类子类外部
public✅✅✅
protected✅✅❌
private✅❌❌

封装最佳实践

  • 成员变量默认 private
  • 提供 getter/setter 时加校验
  • 不变数据可以只读(getter,无 setter)
  • 公开接口要稳定,内部可随意改
💡 提示

封装让对象"自治":数据 + 操作数据的规则都封装在类里,外部只能通过接口安全交互。

C++ 接口(抽象类)

掌握 C++ 接口(抽象类):用纯虚类定义契约。

什么是接口

C++ 没有 interface 关键字,用纯虚类(全部方法为纯虚函数)模拟接口:只定义"能做什么",不实现。

接口示例

#include <iostream>
using namespace std;

// 接口:支付能力
class IPayment {
public:
    virtual void pay(double amount) = 0;      // 纯虚
    virtual ~IPayment() {}                     // 虚析构
};

// 实现接口
class WechatPay : public IPayment {
public:
    void pay(double amount) override {
        cout << "微信支付 " << amount << " 元" << endl;
    }
};

class Alipay : public IPayment {
public:
    void pay(double amount) override {
        cout << "支付宝支付 " << amount << " 元" << endl;
    }
};

// 面向接口编程
void checkout(IPayment& payment, double amount) {
    payment.pay(amount);   // 传入什么实现就怎么支付
}

int main() {
    WechatPay w;
    Alipay a;
    checkout(w, 100);   // 微信支付
    checkout(a, 200);   // 支付宝
    return 0;
}

多接口实现

// 一个类实现多个接口
class IDrawable {
public:
    virtual void draw() = 0;
    virtual ~IDrawable() {}
};

class ISerializable {
public:
    virtual string serialize() = 0;
    virtual ~ISerializable() {}
};

class Shape : public IDrawable, public ISerializable {
public:
    void draw() override { /* 画 */ }
    string serialize() override { return "shape"; }
};

接口的意义

  • 解耦:调用方依赖接口而非实现
  • 可替换:换实现不影响调用代码
  • 可测试:测试时可注入 mock 实现
  • 多态:同一接口多种行为
💡 提示

接口命名习惯:I 前缀(IPayment)或 able 后缀(Drawable)。接口是 C++ 架构设计的基石。

C++ 文件和流

掌握 C++ 文件和流:fstream 读写文件。

文件流类

类用途
ifstream读文件
ofstream写文件
fstream读写文件

写文件

#include <fstream>
using namespace std;

// 方式一:构造时打开
ofstream out("data.txt");
if (!out) {
    cerr << "打开失败" << endl;
    return;
}

out << "Hello" << endl;
out << 42 << endl;
out.close();    // 显式关闭

// 追加模式
ofstream append("data.txt", ios::app);
append << "追加内容" << endl;
append.close();

读文件

#include <fstream>
#include <string>
using namespace std;

ifstream in("data.txt");
string line;

// 逐行读取
while (getline(in, line)) {
    cout << line << endl;
}

// 逐词读取
string word;
while (in >> word) {
    cout << word << " ";
}

in.close();

二进制文件

// 写二进制
ofstream out("data.bin", ios::binary);
int num = 42;
out.write(reinterpret_cast<char*>(&num), sizeof(num));
out.close();

// 读二进制
ifstream in("data.bin", ios::binary);
int val;
in.read(reinterpret_cast<char*>(&val), sizeof(val));
in.close();

随机访问

fstream file("data.txt", ios::in | ios::out);

file.seekg(0, ios::end);      // 移到文件末尾(读位置)
streampos size = file.tellg();// 获取文件大小
file.seekg(0, ios::beg);      // 回到开头
file.seekg(5);                // 移到第 5 字节

状态检查

if (in.good())  // 正常
if (in.fail())  // 失败(格式错)
if (in.eof())   // 到达文件尾
if (in.bad())   // 严重错误
💡 提示

文本文件用 << / getline;二进制用 read/write。RAII:文件流对象析构自动关闭。

C++ 异常处理

掌握 C++ 异常处理:try、catch、throw。

基本用法

#include <iostream>
#include <stdexcept>
using namespace std;

double divide(double a, double b) {
    if (b == 0) {
        throw runtime_error("除数不能为 0");   // 抛出异常
    }
    return a / b;
}

int main() {
    try {
        double result = divide(10, 0);
        cout << result;
    } catch (const exception& e) {
        cout << "捕获异常: " << e.what() << endl;
    }
    return 0;
}

标准异常类

#include <stdexcept>

invalid_argument("参数无效")
out_of_range("越界")
length_error("长度错误")
overflow_error("溢出")
runtime_error("运行时错误")
logic_error("逻辑错误")

// 多 catch(从上到下匹配)
try {
    // ...
} catch (const invalid_argument& e) {
    // 具体异常先捕获
} catch (const exception& e) {
    // 通用异常
} catch (...) {
    // 捕获所有(包括非标准异常)
}

自定义异常

class ApiException : public runtime_error {
    int statusCode;
public:
    ApiException(int code, const string& msg)
        : runtime_error(msg), statusCode(code) {}

    int getCode() const { return statusCode; }
};

void fetchData() {
    throw ApiException(404, "资源不存在");
}

try {
    fetchData();
} catch (const ApiException& e) {
    cout << "HTTP " << e.getCode() << ": " << e.what();
}

异常安全与 RAII

// 利用 RAII 自动清理(异常发生时也安全)
void func() {
    vector<int> v(100);       // 异常时自动释放
    // 传统 new/delete 需要手动清理,异常可能泄漏
}

// 标准库容器和智能指针都是异常安全的

异常使用建议

  • 只用异常处理"异常情况"(不是正常流程)
  • 按引用捕获(const exception&)避免切片
  • 构造函数失败抛异常(不能返回错误码)
  • 避免在析构函数抛异常(会导致 terminate)
💡 提示

现代 C++ 哲学:异常 + RAII 替代手动错误检查。但性能敏感热路径(如高频循环)可能用错误码。

C++ 动态内存

掌握 C++ 动态内存:new/delete 与智能指针。

new / delete

// 分配单个对象
int* p = new int(42);     // 分配并初始化
delete p;                 // 释放

// 分配数组
int* arr = new int[10];
delete[] arr;             // 数组用 delete[]

// 分配对象
Student* s = new Student("张三", 20);
s->introduce();
delete s;

⚠️ 内存管理风险

// 内存泄漏:忘记 delete
void leak() {
    int* p = new int(42);
    // 没有 delete → 泄漏!
}

// 双重释放:delete 两次
int* p = new int(1);
delete p;
delete p;    // ❌ 未定义行为!

// 悬垂指针:delete 后继续用
int* p = new int(1);
delete p;
*p = 10;     // ❌ 访问已释放内存!

// 解决方案:智能指针(现代 C++ 推荐)

unique_ptr(独占所有权)

#include <memory>

// 独占:同一时间只有一个 owner
unique_ptr<int> p = make_unique<int>(42);
*p;    // 42

// 不能拷贝,只能移动
// unique_ptr<int> q = p;      // ❌
unique_ptr<int> q = move(p);   // ✅ 转移所有权

// 离开作用域自动释放(RAII)
void f() {
    auto s = make_unique<Student>("张三", 20);
    s->introduce();
    // 自动 delete
}

shared_ptr(共享所有权)

// 多个指针共享同一对象(引用计数)
shared_ptr<int> p1 = make_shared<int>(42);
shared_ptr<int> p2 = p1;      // ✅ 可拷贝

p1.use_count();    // 2(引用计数)

// 最后一个 shared_ptr 销毁时释放对象
p1.reset();
p2.reset();        // 此时对象被释放

weak_ptr(弱引用,防循环)

// 不增加引用计数,观察但不拥有
weak_ptr<int> wp;

{
    auto sp = make_shared<int>(42);
    wp = sp;                    // 不增加计数
    // sp 销毁后对象释放
}

if (auto sp = wp.lock()) {      // lock 获取 shared_ptr
    cout << *sp;
} else {
    cout << "对象已释放";
}

智能指针选择

场景选择
独占资源unique_ptr(默认首选)
多方共享shared_ptr
观察/防循环weak_ptr
裸指针仅在不拥有资源时用
💡 提示

现代 C++ 内存管理:优先栈对象,其次 unique_ptr,少用 new/delete。裸 new/delete 是 90% 内存 bug 的来源。

C++ 命名空间

掌握 C++ 命名空间:组织代码、避免命名冲突。

基本命名空间

#include <iostream>
using namespace std;

namespace Math {
    const double PI = 3.14159;

    int square(int x) {
        return x * x;
    }
}

namespace String {
    int length(const char* s) {
        return strlen(s);
    }
}

int main() {
    cout << Math::PI << endl;        // 作用域符访问
    cout << Math::square(5) << endl; // 25
    cout << String::length("hi");     // 2
    return 0;
}

嵌套与别名

// 嵌套
namespace Outer {
    namespace Inner {
        int value = 42;
    }
}
Outer::Inner::value;

// C++17 嵌套简写
namespace Outer::Inner2 {
    int v = 1;
}

// 命名空间别名
namespace M = Math;
M::square(3);    // 9

using 声明与指令

// using 声明:只引入某个名字
using Math::square;
square(4);       // 直接用

// using 指令:引入整个命名空间
using namespace Math;
PI;              // 可用(但可能冲突)

// 建议:小作用域内使用
void f() {
    using namespace std;   // 函数内 using
    cout << "hi";
}

匿名命名空间

// 匿名命名空间:相当于 static(仅本文件可见)
namespace {
    int filePrivateVar = 10;    // 文件私有

    void helper() { /* 仅本文件可用 */ }
}

std 命名空间

// C++ 标准库都在 std 中
std::cout
std::vector
std::string
std::make_unique

// 建议:用 std:: 前缀而非 using namespace std
//(避免与全局命名冲突)
💡 提示

大型项目规范:自己的代码放独立命名空间(如 com::codenest::utils),避免与第三方库冲突。

C++ 模板

掌握 C++ 模板:泛型编程的核心。

函数模板

#include <iostream>
using namespace std;

// 函数模板:T 是类型参数
template <typename T>
T maxValue(T a, T b) {
    return a > b ? a : b;
}

int main() {
    cout << maxValue(3, 7) << endl;          // 7(int)
    cout << maxValue(2.5, 1.5) << endl;      // 2.5(double)
    cout << maxValue('a', 'z') << endl;      // z(char)
    return 0;
}

类模板

template <typename T>
class Box {
private:
    T value;
public:
    Box(T v) : value(v) {}

    T get() const { return value; }
    void set(T v) { value = v; }
};

Box<int> intBox(42);
Box<string> strBox("hello");
cout << intBox.get() << " " << strBox.get();

多个类型参数

template <typename T, typename U>
pair<T, U> makePair(T a, U b) {
    return {a, b};
}

auto p = makePair(1, "one");    // pair<int, const char*>

模板特化

// 通用版本
template <typename T>
string typeName() { return "未知"; }

// 特化:int 版本
template <>
string typeName<int>() { return "int"; }

// 部分特化(类模板)
template <typename T>
class Box<T*> {    // 指针特化
    // 特殊处理指针
};

非类型模板参数

template <typename T, int N>
class FixedArray {
    T data[N];    // 编译期确定大小
public:
    int size() const { return N; }
};

FixedArray<int, 10> arr;
arr.size();    // 10

STL 就是模板

vector<int>          // vector 是类模板
list<string>
map<string, int>
sort(v.begin(), v.end())   // sort 是函数模板
💡 提示

模板是 C++"零成本抽象"的体现:编译期生成具体类型代码,无运行时开销。STL 是学习模板的最佳范例。

C++ 预处理器

掌握 C++ 预处理器:编译前处理的指令。

预处理器指令

指令作用
#include包含头文件
#define定义宏
#ifdef / #ifndef条件编译
#if / #else / #endif条件编译
#pragma编译器指令
#undef取消宏定义

头文件保护(最重要)

// myclass.h
#ifndef MYCLASS_H       // 如果没有定义
#define MYCLASS_H       // 定义(防止重复包含)

class MyClass {
    // ...
};

#endif                  // 结束

宏定义

// 对象宏
#define MAX_SIZE 100
#define PI 3.14159

// 函数宏(慎用,有副作用风险)
#define SQUARE(x) ((x) * (x))

// 现代 C++ 用 constexpr 替代
constexpr int MAX_SIZE = 100;

条件编译

#define DEBUG

#ifdef DEBUG
    cout << "调试信息" << endl;
#endif

#ifndef RELEASE
    cout << "开发模式" << endl;
#endif

// 平台判断
#ifdef _WIN32
    // Windows 代码
#elif defined(__APPLE__)
    // macOS 代码
#elif defined(__linux__)
    // Linux 代码
#endif

预定义宏

__FILE__    // 当前文件名
__LINE__    // 当前行号
__DATE__    // 编译日期
__TIME__    // 编译时间
__cplusplus // C++ 版本

// 调试打印
#define LOG(msg) cout << __FILE__ << ":" << __LINE__                  << " " << msg << endl;

LOG("开始执行");
💡 提示

头文件保护(#ifndef)必不可少;宏尽量少用,用 constexpr/inline 替代。编译时可用 -D DEBUG 定义宏。

C++ 信号处理

掌握 C++ 信号处理:<csignal> 处理程序信号。

常见信号

#include <csignal>

SIGINT    // Ctrl+C 中断
SIGTERM   // 终止请求
SIGSEGV   // 段错误(非法内存访问)
SIGABRT   // 异常终止
SIGFPE    // 除零等算术错误

注册信号处理器

#include <iostream>
#include <csignal>
using namespace std;

volatile sig_atomic_t g_flag = 0;

void signalHandler(int signum) {
    cout << "收到信号: " << signum << endl;
    g_flag = 1;    // 设置标志
    // 清理资源...
    exit(signum);
}

int main() {
    // 注册 SIGINT(Ctrl+C)处理器
    signal(SIGINT, signalHandler);

    cout << "按 Ctrl+C 退出..." << endl;
    while (true) {
        // 主循环
    }
    return 0;
}

raise 发送信号

#include <csignal>

// 向当前进程发送信号
raise(SIGINT);

// 向指定进程发送
// kill(pid, SIGTERM);  // POSIX

注意

  • 信号处理器中只能调用"异步安全"的函数
  • 避免在处理器中做复杂操作(malloc、cout 等可能不安全)
  • 用 volatile sig_atomic_t 做标志通信
  • 现代 C++ 更推荐信号量/原子变量管理
💡 提示

生产代码中,优雅关闭(SIGTERM/SIGINT 时保存状态)是常见需求;但处理器逻辑务必简单。

C++ 多线程

掌握 C++ 多线程:<thread> 与并发编程。

创建线程

#include <iostream>
#include <thread>
using namespace std;

void worker(int id) {
    cout << "线程 " << id << " 运行中" << endl;
}

int main() {
    thread t1(worker, 1);   // 创建并启动线程
    thread t2(worker, 2);

    t1.join();   // 等待线程结束
    t2.join();
    cout << "主线程继续" << endl;
    return 0;
}

join 与 detach

thread t(worker, 1);

t.join();      // 阻塞等待线程完成
// t.detach(); // 分离:后台运行(不等待)

// 判断是否可 join
if (t.joinable()) {
    t.join();
}

数据竞争问题

#include <mutex>

int counter = 0;
mutex mtx;    // 互斥锁

void increment() {
    for (int i = 0; i < 100000; i++) {
        lock_guard<mutex> lock(mtx);  // RAII 锁
        counter++;                    // 临界区
    }   // 自动解锁
}

原子操作

#include <atomic>

atomic<int> counter{0};   // 原子变量

void increment() {
    for (int i = 0; i < 100000; i++) {
        counter++;        // 原子操作,无需锁
    }
}

// 无锁且安全,性能好

条件变量

#include <condition_variable>

mutex mtx;
condition_variable cv;
bool ready = false;

void waiter() {
    unique_lock<mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });   // 等待条件
    cout << "条件满足,继续执行" << endl;
}

void notifier() {
    {
        lock_guard<mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one();    // 通知一个等待线程
}

future / async(推荐)

#include <future>

// 异步执行并获取结果
int compute(int x) { return x * x; }

future<int> result = async(launch::async, compute, 6);
cout << result.get() << endl;    // 36(阻塞等待结果)

多线程注意事项

  • 共享数据必须同步(锁/原子)
  • 避免死锁(锁顺序一致、用 lock_guard)
  • 优先 async/future(比手写线程简单)
  • 线程数不要超过硬件并发数
💡 提示

C++ 并发三件套:thread + mutex/atomic + future。能用 async 就别手写线程,能原子就别用锁。

C++ Web 编程

掌握 C++ Web 编程:C++ 与 Web 开发的结合方式。

C++ 在 Web 中的角色

角色说明
Web 框架Crow、Drogon、cpprestsdk
底层服务Nginx 模块、高并发网关
HTTP 客户端libcurl、cpprestsdk
WebSocketwebsocketpp
JSON 处理nlohmann/json、RapidJSON

HTTP 客户端(libcurl)

#include <curl/curl.h>
#include <iostream>
#include <string>
using namespace std;

size_t writeCallback(void* data, size_t size, size_t nmemb, string* out) {
    out->append(static_cast<char*>(data), size * nmemb);
    return size * nmemb;
}

int main() {
    CURL* curl = curl_easy_init();
    if (!curl) return 1;

    string response;
    curl_easy_setopt(curl, CURLOPT_URL, "https://api.example.com/users");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

    CURLcode res = curl_easy_perform(curl);
    if (res == CURLE_OK) {
        cout << response << endl;
    }
    curl_easy_cleanup(curl);
    return 0;
}

JSON 处理(nlohmann/json)

#include <nlohmann/json.hpp>
using json = nlohmann::json;

// 解析
json data = json::parse("{"name":"张三","age":25}");
string name = data["name"];
int age = data["age"];

// 构造
json obj;
obj["name"] = "李四";
obj["age"] = 30;
obj["tags"] = {"cpp", "web"};

string str = obj.dump();   // 序列化

Web 框架(Drogon 示例)

#include <drogon/drogon.h>
using namespace drogon;

int main() {
    app().registerHandler("/hello",
        [](const HttpRequestPtr&, std::function<void(const HttpResponsePtr&)>&& callback) {
            auto resp = HttpResponse::newHttpResponse();
            resp->setBody("Hello from C++!");
            callback(resp);
        },
        {Get});

    app().addListener("0.0.0.0", 8080).run();
    return 0;
}
💡 提示

多数 Web 场景用 Python/Node/Go 更高效;C++ 用于性能瓶颈模块(网关、推荐引擎、图像处理服务)。

C++ STL 教程

掌握 C++ STL 教程:标准模板库全景。

STL 组成

组件说明示例
容器存储数据的结构vector、list、map、set
算法通用操作sort、find、copy
迭代器访问容器的接口begin()、end()
函数对象可调用对象greater、less
适配器接口转换stack、queue

容器分类

序列容器:vector / list / deque / array / forward_list
关联容器:set / map / multiset / multimap
无序容器:unordered_set / unordered_map
容器适配器:stack / queue / priority_queue

算法示例

#include <algorithm>
#include <numeric>

vector<int> v = {5, 2, 8, 1, 9};

sort(v.begin(), v.end());          // 排序 [1,2,5,8,9]
reverse(v.begin(), v.end());       // 反转
auto it = find(v.begin(), v.end(), 5);  // 查找
int cnt = count(v.begin(), v.end(), 2); // 计数
int sum = accumulate(v.begin(), v.end(), 0); // 求和
auto mx = max_element(v.begin(), v.end());   // 最大值

// 复制
vector<int> copy;
copy.resize(v.size());
copy(v.begin(), v.end(), copy.begin());

// Lambda 算法
sort(v.begin(), v.end(), [](int a, int b) { return a > b; });  // 降序

STL 的优势

  • 通用:一套代码适配所有容器
  • 高效:经过工业级优化
  • 标准:跨平台一致行为
  • 类型安全:模板编译期检查
💡 提示

STL 是 C++ 的"标准零件库":先学容器,再学算法,最后理解迭代器如何连接两者。

C++ 导入标准库

掌握 C++ 导入标准库:include 与 import(C++20 模块)。

传统 #include

#include <iostream>    // 尖括号:标准库
#include "myheader.h"  // 引号:本地文件

// 常用标准库头文件
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include <memory>

C++20 模块(import)

// 现代模块化方式(C++20+)
import std;              // 导入标准库模块

int main() {
    std::vector<int> v = {1, 2, 3};
    return 0;
}

// 模块优势:编译更快、封装更好、无头文件
// 编译器支持:MSVC / GCC 14+ / Clang 17+

头文件最佳实践

  • 头文件加防重复包含保护
  • 头文件尽量少 include(前向声明)
  • 头文件只放声明,实现放 .cpp
  • 用 include guards 或 #pragma once
// 前向声明(减少依赖)
class MyClass;   // 只声明,不 include

void usePointer(MyClass* p);   // 指针可以前向声明

// 需要完整定义时再 include
💡 提示

理解 #include 的"文本替换"本质:预处理时把头文件内容粘贴进来。C++20 模块是未来方向,但 #include 仍为主流。

C++ 标准库

掌握 C++ 标准库:常用头文件与功能速查。

核心头文件

头文件内容
<iostream>输入输出(cin/cout)
<string>std::string
<vector>动态数组
<map> / <unordered_map>键值容器
<algorithm>通用算法(sort/find)
<memory>智能指针
<thread> / <mutex>多线程
<fstream>文件流
<cmath>数学函数
<chrono>时间

std::string 常用

string s = "Hello";
s.size();  s.substr(0, 2);  s.find("l");
s += "!";  s.replace(0, 1, "J");

智能指针

auto u = make_unique<int>(42);   // 独占
auto s = make_shared<int>(42);   // 共享
weak_ptr<int> w = s;             // 弱引用

算法速查

sort / reverse / find / count
min / max / min_element / max_element
accumulate / copy / remove / replace
💡 提示

标准库就是"轮子仓库":写代码前先想想标准库有没有现成的,避免重复造轮子。

C++ 有用的资源

C++ 有用的资源:学习资料与工具推荐。

官方文档

资源链接/说明
cppreferencecppreference.com(最权威)
isocppisocpp.org(标准与 FAQ)
cppinsightscppinsights.io(模板展开可视化)

书籍推荐

  • C++ Primer:入门经典(第 5 版)
  • Effective Modern C++:最佳实践
  • Effective C++:55 条建议
  • The C++ Programming Language:作者原著

在线练习

  • LeetCode:算法题(C++ 提交)
  • Codeforces:竞赛
  • Compiler Explorer(godbolt.org):在线编译

工具链

  • 编译器:GCC、Clang、MSVC
  • 构建:CMake(事实标准)
  • 依赖:vcpkg、Conan
  • 分析:Valgrind(内存)、perf(性能)
  • 格式化:clang-format、clang-tidy
💡 提示

学习路线建议:语法基础 → STL → 面向对象 → 模板/现代特性 → 项目实战。配合 cppreference 随时查证。

C++ 实例

C++ 实例:常见编程实例练习。

1. 判断素数

bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

2. 斐波那契

long long fib(int n) {
    if (n <= 1) return n;
    long long a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        long long t = a + b;
        a = b;
        b = t;
    }
    return b;
}

3. 字符串反转

string reverse(string s) {
    reverse(s.begin(), s.end());   // STL 一行
    return s;
}

4. 统计字符频率

map<char, int> countChars(const string& s) {
    map<char, int> freq;
    for (char c : s) freq[c]++;
    return freq;
}

5. vector 排序去重

vector<int> uniqueSort(vector<int> v) {
    sort(v.begin(), v.end());
    v.erase(unique(v.begin(), v.end()), v.end());
    return v;
}

6. 二分查找

int binarySearch(vector<int>& arr, int target) {
    int left = 0, right = arr.size() - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) return mid;
        if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

7. 两数之和(哈希)

vector<int> twoSum(vector<int>& nums, int target) {
    unordered_map<int, int> seen;
    for (int i = 0; i < nums.size(); i++) {
        int need = target - nums[i];
        if (seen.count(need)) return {seen[need], i};
        seen[nums[i]] = i;
    }
    return {};
}
💡 提示

动手敲这些实例,体会 STL 容器与算法的配合——它们是算法面试的高频题型。

C++ 测验

本章测验检验你对 C++ 的掌握。先独立作答,再看解析。

选择题

1. C++ 程序执行的入口函数是?

  • A. start()
  • B. main() ✅
  • C. run()
  • D. init()

2. 哪个是引用传递参数的写法?

  • A. void f(int x)
  • B. void f(int* x)
  • C. void f(int& x) ✅
  • D. void f(const x)

3. 多态依赖的关键字是?

  • A. static
  • B. virtual ✅
  • C. extern
  • D. register

4. 动态数组容器是?

  • A. array
  • B. vector ✅
  • C. list
  • D. map

5. 释放 new[] 分配的内存用?

  • A. delete
  • B. delete[] ✅
  • C. free
  • D. clear

6. 独占所有权的智能指针是?

  • A. shared_ptr
  • B. unique_ptr ✅
  • C. weak_ptr
  • D. auto_ptr

判断题

7. C++ 中 struct 与 class 默认访问权限相同。

❌ 错误。struct 默认 public,class 默认 private。

8. 基类析构函数应为 virtual。

✅ 正确。否则 delete 派生对象不会调用派生类析构,造成泄漏。

简答题

9. 写出 vector 排序的代码。

#include <algorithm>
sort(v.begin(), v.end());

10. 什么是 RAII?

RAII(Resource Acquisition Is Initialization):资源在构造时获取,析构时释放(如 lock_guard、unique_ptr)。异常安全、自动清理。

计分

答对水平
9-10优秀,可投入实战
6-8良好,查漏补缺
<6建议重学基础章节
💡 提示

C++ 要"多用多写":编译错误是最好的老师,多看编译器报错信息能快速进步。

C++ <iostream>

掌握 C++ <iostream>:标准输入输出流。

核心对象

对象用途
std::cin标准输入(键盘)
std::cout标准输出(屏幕)
std::cerr错误输出(不缓冲)
std::clog日志输出(缓冲)

输出

#include <iostream>
using namespace std;

cout << "Hello" << endl;        // 换行并刷新
cout << 42 << " " << 3.14;      // 多值输出
cout << "值: " << x << "\n";    // 快速换行

// 格式化
#include <iomanip>
cout << fixed << setprecision(2) << 3.14159;  // 3.14
cout << setw(5) << setfill('0') << 42;       // 00042

输入

int age;
string name;

cin >> age;              // 读数字
cin >> name;             // 读一个词
getline(cin, name);      // 读整行

// 循环读取直到 EOF
int n;
while (cin >> n) {
    cout << n * 2 << endl;
}

流状态

if (cin)          // 流正常
if (cin.fail())   // 读取失败
if (cin.eof())    // 文件末尾

// 清除错误并继续
cin.clear();
cin.ignore(10000, '
');
💡 提示

endl 会刷新缓冲区(慢),大量输出用 "\n"。调试用 cerr(不缓冲,实时显示)。

C++ <fstream>

掌握 C++ <fstream>:文件流。

文件流类

#include <fstream>
using namespace std;

ifstream in;    // 读
ofstream out;   // 写
fstream f;      // 读写

写文件

ofstream out("data.txt");       // 打开(覆盖)
if (!out) { cerr << "打开失败"; return; }

out << "第一行" << endl;
out << 42 << " " << 3.14 << endl;
out.close();                     // 关闭

// 追加模式
ofstream app("log.txt", ios::app);
app << "新日志" << endl;

读文件

ifstream in("data.txt");
string line;
while (getline(in, line)) {
    cout << line << endl;      // 逐行
}

int a, b;
while (in >> a >> b) {         // 按格式读
    cout << a + b << endl;
}

二进制

ofstream out("bin.dat", ios::binary);
int n = 100;
out.write(reinterpret_cast<char*>(&n), sizeof(n));
out.close();

ifstream in("bin.dat", ios::binary);
in.read(reinterpret_cast<char*>(&n), sizeof(n));

随机访问

fstream f("data.txt", ios::in | ios::out);
f.seekg(0, ios::end);        // 移到末尾
streampos size = f.tellg();  // 文件大小
f.seekg(0, ios::beg);        // 回到开头
💡 提示

文件流对象离开作用域自动关闭(RAII);但显式 close() 更清晰,能及时释放文件句柄。

C++ <sstream>

掌握 C++ <sstream>:字符串流。

stringstream 基础

#include <sstream>
using namespace std;

// 拼接
stringstream ss;
ss << "Hello" << " " << 42 << " " << 3.14;
string result = ss.str();    // "Hello 42 3.14"

// 清空复用
ss.str("");                  // 重置内容
ss.clear();                  // 清除状态

解析(字符串 → 数字)

string data = "123 456 789";
stringstream ss(data);

int a, b, c;
ss >> a >> b >> c;    // 依次提取
// a=123, b=456, c=789

// 按分隔符解析
string line = "1,2,3,4";
stringstream ls(line);
string token;
while (getline(ls, token, ',')) {   // 逗号分割
    cout << token << " ";    // 1 2 3 4
}

数字转字符串

// 方式一:to_string
string s1 = to_string(42);       // "42"

// 方式二:stringstream(可控制格式)
stringstream ss;
ss << fixed << setprecision(2) << 3.14159;
string s2 = ss.str();            // "3.14"

应用:解析 CSV

vector<vector<string>> parseCSV(const string& content) {
    vector<vector<string>> rows;
    stringstream ss(content);
    string row;
    while (getline(ss, row, '
')) {
        vector<string> cols;
        stringstream rs(row);
        string col;
        while (getline(rs, col, ',')) {
            cols.push_back(col);
        }
        rows.push_back(cols);
    }
    return rows;
}
💡 提示

stringstream 是"字符串与数字互转、按分隔符解析"的利器,比手写字符串处理简洁得多。

C++ <iomanip>

掌握 C++ <iomanip>:输入输出格式化。

常用操作符

操作符作用
setw(n)设置宽度
setfill(c)填充字符
setprecision(n)小数位数
fixed固定小数表示
scientific科学计数法
left / right左/右对齐
hex / oct / dec进制
boolalphatrue/false 输出
showpos显示正号

示例

#include <iostream>
#include <iomanip>
using namespace std;

double pi = 3.14159265;

// 精度
cout << setprecision(3) << pi;        // 3.14
cout << fixed << setprecision(2) << pi;  // 3.14
cout << scientific << pi;             // 3.141593e+00

// 宽度与填充
cout << setw(10) << 42 << endl;       // "        42"
cout << setw(10) << setfill('0') << 42;  // "0000000042"
cout << left << setw(10) << "Name" << "|" << endl;

// 进制
cout << hex << 255 << endl;   // ff
cout << oct << 8 << endl;     // 10
cout << dec << 10 << endl;    // 10

// 布尔
cout << boolalpha << true;   // true

表格对齐输出

cout << left
     << setw(15) << "姓名"
     << setw(10) << "年龄"
     << setw(10) << "分数" << endl;
cout << left
     << setw(15) << "张三"
     << setw(10) << 20
     << setw(10) << 88.5 << endl;
💡 提示

setw 只对下一次输出有效;setfill/fixed/setprecision 持续有效(直到改变)。

C++<array>

掌握 C++ <array>:固定大小数组容器。

基本用法

#include <array>
using namespace std;

array<int, 5> a = {1, 2, 3, 4, 5};   // 固定大小 5

a.size();          // 5
a[0];              // 1(不检查边界)
a.at(0);           // 1(越界抛异常)
a.front();         // 1
a.back();          // 5
a.fill(0);         // 全部填 0
a.empty();         // 是否为空(固定大小总是 false)

遍历

for (int x : a) { cout << x; }
for (auto it = a.begin(); it != a.end(); ++it) { }
for (size_t i = 0; i < a.size(); i++) { cout << a[i]; }

array vs 裸数组 vs vector

对比项std::array裸数组vector
大小固定固定动态
安全at() 检查无at() 检查
STL 兼容✅❌✅
推荐✅ 固定大小C 兼容动态大小

算法配合

#include <algorithm>

array<int, 4> a = {3, 1, 4, 2};
sort(a.begin(), a.end());     // 排序
reverse(a.begin(), a.end());  // 反转

// 与裸数组互转
int* raw = a.data();          // 获取底层指针
💡 提示

固定大小且需要 STL 功能时用 std::array;它没有裸数组的"退化为指针"问题,更安全。

C++ <vector>

掌握 C++ <vector>:动态数组容器(STL 最常用)。

创建

#include <vector>
using namespace std;

vector<int> v1;                  // 空
vector<int> v2(5);               // 5 个 0
vector<int> v3(5, 10);           // 5 个 10
vector<int> v4 = {1, 2, 3};      // 列表初始化
vector<int> v5(v4.begin(), v4.end());  // 范围拷贝

操作

vector<int> v = {1, 2, 3};

v.push_back(4);          // 尾部添加
v.emplace_back(5);       // 尾部构造添加
v.pop_back();            // 尾部删除
v.insert(v.begin(), 0);  // 头部插入
v.erase(v.begin());      // 头部删除
v.clear();               // 清空

v.size();                // 元素个数
v.capacity();            // 容量(已分配)
v.empty();               // 是否空
v.reserve(100);          // 预分配容量(优化)

v[0];  v.at(0);  v.front();  v.back();

扩容机制

// 容量不足时自动扩容(通常翻倍)
// 频繁添加时预先 reserve 避免反复扩容

vector<int> v;
v.reserve(1000);       // 预分配 1000 容量
for (int i = 0; i < 1000; i++) {
    v.push_back(i);    // 不会触发扩容
}

二维 vector

// 3 行 4 列,全 0
vector<vector<int>> matrix(3, vector<int>(4, 0));

matrix[1][2] = 9;

for (const auto& row : matrix) {
    for (int x : row) cout << x << " ";
    cout << endl;
}

算法配合

#include <algorithm>
#include <numeric>

sort(v.begin(), v.end());
reverse(v.begin(), v.end());
find(v.begin(), v.end(), 3);
accumulate(v.begin(), v.end(), 0);   // 求和
💡 提示

vector 是 C++ 的"默认容器":内存连续、随机访问快、自动扩容。95% 的动态数组场景用它。

C++ <list>

掌握 C++ <list>:双向链表容器。

基本操作

#include <list>
using namespace std;

list<int> l = {1, 2, 3};

l.push_back(4);         // 尾部添加
l.push_front(0);        // 头部添加
l.pop_back();           // 尾部删除
l.pop_front();          // 头部删除

l.size();  l.empty();
l.front();  l.back();

l.insert(l.begin(), 99);   // 指定位置插入
l.erase(l.begin());        // 指定位置删除
l.clear();

list 特有操作

list<int> a = {1, 2, 3};
list<int> b = {4, 5};

a.splice(a.end(), b);      // 拼接(O(1),转移节点)
a.sort();                  // 排序(list 自己的 sort)
a.reverse();               // 反转
a.unique();                // 去重(相邻相同)
a.remove(2);               // 删除所有值=2
a.merge(b);                // 合并(需都已排序)

list vs vector

对比项listvector
内存分散节点连续
随机访问O(n) 慢O(1) 快
头部插入O(1) 快O(n) 慢
缓存友好差好
适用频繁中段增删随机访问为主

注意:list 无随机访问

// list 不支持 l[3](无 operator[])
// 需要遍历到目标位置
auto it = l.begin();
advance(it, 3);    // 前进 3 步
💡 提示

list 的节点分散导致缓存不友好,性能常不如 vector。默认用 vector;只有大量"中间插入/删除"才考虑 list。

C++ <forward_list>

掌握 C++ <forward_list>:单向链表(C++11+)。

基本操作

#include <forward_list>
using namespace std;

forward_list<int> fl = {1, 2, 3};

fl.push_front(0);       // 头部添加(只能头部!)
fl.pop_front();         // 头部删除
fl.front();             // 第一个元素

fl.insert_after(fl.begin(), 99);  // 在指定位置后插入
fl.erase_after(fl.begin());       // 删除指定位置后的元素

// 没有 size()!需要遍历统计
int n = distance(fl.begin(), fl.end());

forward_list vs list

对比项forward_listlist
方向单向双向
内存更小(少一个指针)较大
反向遍历❌✅
size()❌✅ O(1)
适用内存敏感的单向链表通用双向链表

遍历

for (int x : fl) { cout << x; }

// 前驱访问(单向链表的特色)
auto prev = fl.before_begin();   // 第一个元素之前的位置
auto cur = fl.begin();
while (cur != fl.end()) {
    if (*cur == 99) {
        fl.erase_after(prev);    // 通过前驱删除
        break;
    }
    prev = cur;
    ++cur;
}
💡 提示

forward_list 是最省内存的链表,但功能最少。日常开发 list 或 vector 更常用;它主要出现在内存极度敏感的场景。

C++ <deque>

掌握 C++ <deque>:双端队列容器。

基本操作

#include <deque>
using namespace std;

deque<int> d = {1, 2, 3};

d.push_back(4);        // 尾部添加
d.push_front(0);       // 头部添加
d.pop_back();          // 尾部删除
d.pop_front();         // 头部删除

d[0];                  // 随机访问(O(1))
d.at(0);
d.front();  d.back();
d.size();   d.empty();

d.insert(d.begin() + 1, 99);
d.erase(d.begin() + 1);

deque vs vector vs list

对比项dequevectorlist
头尾操作O(1) 都快头慢尾快都快
随机访问O(1)O(1)O(n)
内存分块连续连续分散
适用头尾都要增删尾部为主中段增删

遍历

for (int x : d) { cout << x; }
for (size_t i = 0; i < d.size(); i++) { cout << d[i]; }
for (auto it = d.begin(); it != d.end(); ++it) { }

典型应用

// 滑动窗口(头尾都操作)
deque<int> window;

// 双端队列实现单调队列(滑动窗口最值)
while (!d.empty() && d.back() < nums[i]) {
    d.pop_back();       // 保持单调递减
}
d.push_back(i);
💡 提示

需要"头尾都能高效增删"时用 deque(如滑动窗口、双端操作)。它比 vector 的头部插入高效得多。

C++ <stack>

掌握 C++ <stack>:栈容器适配器(LIFO)。

基本操作

#include <stack>
using namespace std;

stack<int> st;

st.push(1);       // 入栈
st.push(2);
st.push(3);

st.top();         // 3(栈顶,不移除)
st.pop();         // 出栈(移除栈顶)
st.size();        // 元素个数
st.empty();       // 是否为空

经典应用

// 括号匹配
bool isValid(string s) {
    stack<char> st;
    for (char c : s) {
        if (c == '(' || c == '[' || c == '{') {
            st.push(c);
        } else {
            if (st.empty()) return false;
            char top = st.top();
            if ((c == ')' && top != '(') ||
                (c == ']' && top != '[') ||
                (c == '}' && top != '{')) {
                return false;
            }
            st.pop();
        }
    }
    return st.empty();
}
// 逆波兰表达式求值、函数调用栈模拟
// 浏览器的前进后退(两个栈)

底层容器

// 默认 deque,可指定底层
stack<int> s1;                              // deque 实现
stack<int, vector<int>> s2;                 // vector 实现
stack<int, list<int>> s3;                   // list 实现

注意

  • 不能遍历 stack(只能访问栈顶)
  • pop() 不返回值(先 top 再 pop)
  • 访问空栈的 top 是未定义行为
💡 提示

栈的思想:后进先出。括号匹配、递归转循环、撤销操作都是栈的典型应用。

C++ <queue>

掌握 C++ <queue>:队列容器适配器(FIFO)。

基本操作

#include <queue>
using namespace std;

queue<int> q;

q.push(1);        // 入队(尾部)
q.push(2);
q.push(3);

q.front();        // 1(队头)
q.back();         // 3(队尾)
q.pop();          // 出队(移除队头)
q.size();         // 大小
q.empty();        // 是否为空

经典应用:BFS

// 广度优先搜索
void bfs(vector<vector<int>>& graph, int start) {
    queue<int> q;
    vector<bool> visited(graph.size(), false);

    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int node = q.front();
        q.pop();
        cout << node << " ";

        for (int next : graph[node]) {
            if (!visited[next]) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
}

其他应用

  • 消息队列(生产者-消费者)
  • 任务调度(FIFO)
  • 树的层序遍历
  • 打印缓冲

底层容器

queue<int> q1;                  // 默认 deque
queue<int, list<int>> q2;       // list 实现
💡 提示

队列"先进先出":BFS、排队处理、流式任务都是它的主场。记住 front/pop 配合使用(pop 不返回值)。

C++ <priority_queue>

掌握 C++ <priority_queue>:优先队列(堆)。

基本操作

#include <queue>
using namespace std;

// 最大堆(默认):top 是最大值
priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(5);

pq.top();         // 5(最大值)
pq.pop();         // 移除最大值
pq.size();  pq.empty();

最小堆

// 最小堆:top 是最小值
priority_queue<int, vector<int>, greater<int>> minPq;
minPq.push(3);
minPq.push(1);
minPq.top();      // 1

自定义比较

struct Task {
    int priority;
    string name;
};

// 自定义优先级:priority 大的先出
struct TaskCmp {
    bool operator()(const Task& a, const Task& b) {
        return a.priority < b.priority;   // 大顶堆
    }
};

priority_queue<Task, vector<Task>, TaskCmp> taskQueue;

经典应用

// 前 K 大元素
vector<int> topK(vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> minHeap;
    for (int n : nums) {
        minHeap.push(n);
        if (minHeap.size() > k) minHeap.pop();  // 保持 K 个
    }
    vector<int> result;
    while (!minHeap.empty()) {
        result.push_back(minHeap.top());
        minHeap.pop();
    }
    return result;
}
  • Top K 问题
  • 合并 K 个有序链表
  • 任务调度(按优先级)
  • Dijkstra 最短路
💡 提示

priority_queue 默认最大堆;要最小堆加 greater<int>;自定义类型重载 < 或提供比较器。

C++ <set>

掌握 C++ <set>:有序集合(红黑树实现)。

基本操作

#include <set>
using namespace std;

set<int> s;
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(1);        // 重复被忽略

s.size();           // 3
s.empty();
s.count(2);         // 1(存在性判断)
s.find(2);          // 迭代器(end() 表示未找到)
s.erase(2);         // 删除
s.clear();

有序遍历

// 自动升序
for (int x : s) { cout << x << " "; }   // 1 2 3

// 降序
for (auto it = s.rbegin(); it != s.rend(); ++it) { }

区间操作

// lower_bound:第一个 >= val 的位置
auto it = s.lower_bound(2);

// upper_bound:第一个 > val 的位置
auto it2 = s.upper_bound(2);

// 范围元素个数
int cnt = distance(s.lower_bound(2), s.upper_bound(5));

set vs unordered_set

对比项set(有序)unordered_set(哈希)
查找O(log n)O(1) 平均
顺序有序无序
实现红黑树哈希表
适用需要有序/范围只要去重查找

应用

// 去重
vector<int> v = {1, 2, 2, 3, 3, 3};
set<int> uniqueSet(v.begin(), v.end());
// 1 2 3

// 有序去重后转回 vector
vector<int> result(uniqueSet.begin(), uniqueSet.end());
💡 提示

set 自动排序 + 去重;multiset 允许重复;需要 O(1) 查找且不在乎顺序用 unordered_set。

C++ <unordered_set>

掌握 C++ <unordered_set>:哈希集合(C++11+)。

基本操作

#include <unordered_set>
using namespace std;

unordered_set<int> us;
us.insert(3);
us.insert(1);
us.insert(3);       // 重复忽略

us.count(3);        // 1(O(1) 查找)
us.find(1);         // 迭代器
us.erase(1);
us.size();  us.empty();
us.clear();

无序遍历

// 顺序不确定(哈希)
for (int x : us) { cout << x << " "; }

// 遍历同时删除
for (auto it = us.begin(); it != us.end();) {
    if (*it % 2 == 0) it = us.erase(it);   // C++11 返回下一个
    else ++it;
}

性能

// 平均 O(1) 查找/插入/删除
// 最坏 O(n)(哈希冲突)
// 内存比 set 大(哈希表开销)

// 自定义哈希(自定义类型)
struct Person {
    string name;
    bool operator==(const Person& o) const {
        return name == o.name;
    }
};

struct PersonHash {
    size_t operator()(const Person& p) const {
        return hash<string>()(p.name);
    }
};

unordered_set<Person, PersonHash> people;

应用场景

  • 去重(不在乎顺序)
  • 快速存在性检查(O(1))
  • 集合运算(交集/并集/差集)
💡 提示

只做"去重 + 快速查找"用 unordered_set(O(1));需要有序输出或范围查询用 set(O(log n))。

C++ <map>

掌握 C++ <map>:有序键值对容器(红黑树)。

基本操作

#include <map>
using namespace std;

map<string, int> scores;

// 插入/更新
scores["张三"] = 90;        // 插入
scores["李四"] = 85;
scores["张三"] = 95;        // 更新

// 或 insert(已存在不覆盖)
scores.insert({"王五", 70});

// 查找
scores.count("张三");       // 1
auto it = scores.find("李四");
if (it != scores.end()) {
    cout << it->second;     // 85
}

// 删除
scores.erase("王五");
scores.size();  scores.empty();

有序遍历(按键升序)

for (const auto& [key, value] : scores) {   // C++17 结构化绑定
    cout << key << ": " << value << endl;
}

// 传统迭代器
for (auto it = scores.begin(); it != scores.end(); ++it) {
    cout << it->first << "=" << it->second;
}

经典应用:计数

// 统计词频
map<string, int> freq;
for (const string& word : words) {
    freq[word]++;          // 不存在则初始化为 0 再 ++
}

// 输出按词频排序的结果
vector<pair<string,int>> vec(freq.begin(), freq.end());
sort(vec.begin(), vec.end(), [](auto& a, auto& b) {
    return a.second > b.second;    // 按频率降序
});

map vs unordered_map

对比项mapunordered_map
查找O(log n)O(1) 平均
有序✅ 按键排序❌ 无序
内存较小较大
适用要顺序/范围查询只要快速查找

区间查询

// [lower, upper) 之间的键
auto lo = scores.lower_bound("A");
auto hi = scores.upper_bound("M");
for (auto it = lo; it != hi; ++it) {
    cout << it->first << " ";
}
💡 提示

map 按键自动排序;operator[] 访问不存在的键会自动插入(注意副作用),用 count/find 先判断。

C++ <unordered_map>

掌握 C++ <unordered_map>:哈希映射(C++11+)。

基本操作

#include <unordered_map>
using namespace std;

unordered_map<string, int> um;

um["apple"] = 5;          // 插入
um["banana"] = 3;
um["apple"] = 8;          // 更新

um.count("apple");        // 1(O(1) 存在性)
auto it = um.find("banana");
if (it != um.end()) {
    cout << it->second;
}

um.erase("apple");
um.size();  um.empty();

遍历(无序)

for (const auto& [k, v] : um) {
    cout << k << "=" << v << endl;
}

// 遍历删除
for (auto it = um.begin(); it != um.end();) {
    if (it->second < 5) it = um.erase(it);
    else ++it;
}

经典应用:两数之和

vector<int> twoSum(vector<int>& nums, int target) {
    unordered_map<int, int> seen;   // 值 → 索引
    for (int i = 0; i < nums.size(); i++) {
        int need = target - nums[i];
        if (seen.count(need)) {
            return {seen[need], i};
        }
        seen[nums[i]] = i;
    }
    return {};
}

应用场景

  • 高频键值查找(O(1))
  • 缓存(LRU 的查找部分)
  • 去重 + 计数
  • 图(邻接表)

性能注意

// 预分配 bucket 减少 rehash
unordered_map<string, int> um;
um.reserve(10000);

// 自定义哈希(同 unordered_set 类似)
💡 提示

需要 O(1) 键值查找选 unordered_map;需要按键有序遍历选 map。多数"字典"场景 unordered_map 是默认。

C++ <bitset>

掌握 C++ <bitset>:位集容器。

基本用法

#include <bitset>
using namespace std;

bitset<8> b1;              // 8 位,全 0
bitset<8> b2(42);          // 用整数初始化:00101010
bitset<8> b3("10101010");  // 用二进制字符串

// 访问与设置
b1[0] = 1;                 // 设置第 0 位
b1.set(1);                 // 设置第 1 位
b1.set();                  // 全部置 1
b1.reset();                // 全部置 0
b1.flip();                 // 全部取反

b1.test(0);                // 检查第 0 位
b1.any();                  // 是否有 1
b1.all();                  // 是否全 1
b1.none();                 // 是否全 0
b1.count();                // 1 的个数
b1.size();                 // 位数

运算

bitset<8> a("1100");
bitset<8> b("1010");

a & b;    // 与
a | b;    // 或
a ^ b;    // 异或
~a;       // 取反

// 转换
string s = a.to_string();   // "00001100"
unsigned long v = a.to_ulong();  // 12

经典应用

// 状态标记(多个开关)
bitset<8> flags;
flags.set(0);    // 标志 0 开
flags.set(3);    // 标志 3 开
if (flags.test(0)) { /* 处理 */ }

// 集合表示(集合运算用位运算)
// 求两个集合交集是否非空
bitset<100> setA, setB;
if ((setA & setB).any()) { /* 有交集 */ }

// 布隆过滤器思想(位数组标记)
💡 提示

bitset 固定大小(编译期确定)、内存紧凑、位运算极快,适合状态标记、集合运算、位图场景。

C++ <algorithm>

掌握 C++ <algorithm>:通用算法库(STL 核心)。

排序

#include <algorithm>
using namespace std;

vector<int> v = {5, 2, 8, 1, 9};

sort(v.begin(), v.end());                // 升序
sort(v.begin(), v.end(), greater<int>()); // 降序
sort(v.begin(), v.end(),
     [](int a, int b) { return a > b; }); // Lambda 降序

stable_sort(v.begin(), v.end());         // 稳定排序

partial_sort(v.begin(), v.begin()+3, v.end());  // 前 3 最小

查找

auto it = find(v.begin(), v.end(), 8);       // 线性查找
if (it != v.end()) { cout << *it; }

bool found = binary_search(v.begin(), v.end(), 8);  // 二分(需有序)

auto lo = lower_bound(v.begin(), v.end(), 5);  // 第一个 >= 5
auto hi = upper_bound(v.begin(), v.end(), 5);  // 第一个 > 5

修改

reverse(v.begin(), v.end());          // 反转
rotate(v.begin(), v.begin()+2, v.end());  // 旋转
fill(v.begin(), v.end(), 0);          // 填充
replace(v.begin(), v.end(), 8, 88);   // 替换
remove(v.begin(), v.end(), 5);        // 移除(配合 erase)

// 移除 + 删除(erase-remove 惯用法)
v.erase(remove(v.begin(), v.end(), 5), v.end());

查询与聚合

int cnt = count(v.begin(), v.end(), 3);      // 计数
auto mx = max_element(v.begin(), v.end());   // 最大值
auto mn = min_element(v.begin(), v.end());   // 最小值

bool allPos = all_of(v.begin(), v.end(), [](int x){ return x > 0; });
bool anyEven = any_of(v.begin(), v.end(), [](int x){ return x % 2 == 0; });
bool noneZero = none_of(v.begin(), v.end(), [](int x){ return x == 0; });

// 相邻重复
auto it = adjacent_find(v.begin(), v.end());
// 去重(相邻)
v.erase(unique(v.begin(), v.end()), v.end());

复制与变换

vector<int> dest(v.size());
copy(v.begin(), v.end(), dest.begin());

// 映射变换
vector<int> squared(v.size());
transform(v.begin(), v.end(), squared.begin(),
          [](int x) { return x * x; });

// 合并两个有序序列
vector<int> merged;
merge(v1.begin(), v1.end(), v2.begin(), v2.end(),
      back_inserter(merged));
💡 提示

<algorithm> 是 STL 的灵魂:90% 的容器操作都有现成算法。先查算法再手写,是高效 C++ 的秘诀。

C++ <iterator>

掌握 C++ <iterator>:迭代器库。

迭代器类型

类型能力容器
输入迭代器只读单向istream
输出迭代器只写单向ostream
前向迭代器读写单向forward_list
双向迭代器读写双向list、set、map
随机访问任意跳跃vector、deque、array

迭代器操作

#include <iterator>
using namespace std;

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

auto it = v.begin();     // 指向第一个
auto end = v.end();      // 指向最后一个之后

it++;                    // 前进
it--;                    // 后退(双向)
it + 2;                  // 跳 2 步(随机访问)
it - v.begin();          // 距离(索引)

*it;                     // 解引用取值
it->member;              // 成员访问

// 通用工具
advance(it, 3);          // 前进 3 步(任意迭代器)
auto dist = distance(v.begin(), v.end());  // 距离
iter_swap(it, it+1);     // 交换两个迭代器指向的值

// 反向迭代器
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) { }

插入迭代器

#include <iterator>

// back_inserter:自动 push_back
vector<int> dest;
copy(v.begin(), v.end(), back_inserter(dest));

// 与算法配合
transform(v.begin(), v.end(),
          back_inserter(squared),
          [](int x){ return x * x; });

// front_inserter / inserter
list<int> l;
copy(v.begin(), v.end(), front_inserter(l));   // 头部插入

流迭代器

// 从输入流读入
istream_iterator<int> in(cin), eof;
vector<int> nums(in, eof);      // 读全部数字

// 输出到流
ostream_iterator<int> out(cout, " ");
copy(v.begin(), v.end(), out);  // 1 2 3 4 5
💡 提示

迭代器是"容器与算法之间的桥梁":算法只依赖迭代器接口,所以一套算法适用于所有容器。

C++ <functional>

掌握 C++ <functional>:函数对象与绑定器。

函数对象(仿函数)

#include <functional>
using namespace std;

// 内置函数对象
plus<int>()(1, 2);         // 3
minus<int>()(5, 2);        // 3
multiplies<int>()(3, 4);    // 12
greater<int>()(5, 3);      // true
less<int>()(3, 5);         // true

// 配合算法
sort(v.begin(), v.end(), greater<int>());   // 降序
sort(v.begin(), v.end(), less<int>());      // 升序

自定义函数对象

struct Square {
    int operator()(int x) const {   // 重载 ()
        return x * x;
    }
};

Square sq;
sq(5);        // 25(像函数一样调用)

// 有状态函数对象
struct Counter {
    int count = 0;
    int operator()() {
        return ++count;
    }
};
Counter c;
c(); c(); c();    // 1 2 3

std::function(通用函数包装器)

function<int(int, int)> f;

f = [](int a, int b) { return a + b; };   // Lambda
f(1, 2);    // 3

f = std::plus<int>();                     // 函数对象
f(3, 4);    // 7

f = [](int a, int b) { return a * b; };   // 可重新赋值

// 函数指针
int add(int a, int b) { return a + b; }
f = add;
f(5, 6);    // 11

// 存储回调
function<void(const string&)> callback;
callback = [](const string& s) { cout << s; };

bind(绑定参数)

using namespace std::placeholders;

// 绑定部分参数
auto add5 = bind(plus<int>(), _1, 5);   // 第一个参数 + 5
add5(10);    // 15

// 参数重排
auto subReversed = bind(minus<int>(), _2, _1);
subReversed(10, 3);    // 3 - 10 = -7

std::ref / std::cref

// 按引用传递(否则拷贝)
int x = 42;
auto f = [y = std::ref(x)]() { y.get() = 100; };
f();
// x == 100
💡 提示

std::function 是"类型安全的函数指针":存 Lambda、函数指针、函数对象皆可,回调设计的利器。

C++ <numeric>

掌握 C++ <numeric>:数值算法库。

数值算法

#include <numeric>
using namespace std;

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

// 求和
int sum = accumulate(v.begin(), v.end(), 0);        // 15
int sum10 = accumulate(v.begin(), v.end(), 10);     // 25(初始值 10)

// 乘积(用乘法函数对象)
int product = accumulate(v.begin(), v.end(), 1,
                         multiplies<int>());        // 120

// 点积(内积)
vector<int> u = {1, 2, 3};
vector<int> w = {4, 5, 6};
int dot = inner_product(u.begin(), u.end(), w.begin(), 0);  // 32

// 相邻差
vector<int> diffs;
adjacent_difference(v.begin(), v.end(), back_inserter(diffs));
// [1, 1, 1, 1, 1]

// 部分和(前缀和)
vector<int> prefix;
partial_sum(v.begin(), v.end(), back_inserter(prefix));
// [1, 3, 6, 10, 15]

// 填充(iota:从 start 递增)
vector<int> seq(5);
iota(seq.begin(), seq.end(), 1);   // [1,2,3,4,5]

前缀和的经典应用

// 区间和查询:[l, r) 的和 = prefix[r] - prefix[l]
vector<int> prefix;
partial_sum(v.begin(), v.end(), back_inserter(prefix));

int rangeSum(int l, int r) {
    // prefix 前缀和(prefix[i] = v[0..i-1] 和)
    return prefix[r] - prefix[l];
}
💡 提示

accumulate(求和)、iota(填充递增)、partial_sum(前缀和)是刷题和数值处理的常用工具。

C++ <complex>

掌握 C++ <complex>:复数运算。

基本用法

#include <complex>
using namespace std;

complex<double> c1(3, 4);      // 3 + 4i
complex<double> c2(1, -2);     // 1 - 2i

c1.real();     // 3(实部)
c1.imag();     // 4(虚部)
abs(c1);       // 5(模)
arg(c1);       // 辐角(弧度)
norm(c1);      // 25(模的平方)
conj(c1);      // 3 - 4i(共轭)

// 运算
c1 + c2;  c1 - c2;  c1 * c2;  c1 / c2;

创建与转换

// 从极坐标
complex<double> p = polar(5.0, 0.927);   // 模 5,辐角 0.927

// 虚数单位 i
complex<double> i(0, 1);
complex<double> result = i * i;    // -1 + 0i

// 类型
complex<float> cf(1, 2);
complex<long double> cd(1, 2);

应用

// 复数运算用于信号处理、FFT
// 电路分析(阻抗)
// 量子力学计算

// 使用复数做向量旋转(乘以 i 旋转 90 度)
complex<double> v(1, 0);   // 向量 (1,0)
complex<double> rotated = v * i;   // (0,1) 旋转 90°
💡 提示

需要复数运算(信号处理、FFT、数学计算)时,<complex> 提供完整支持且性能良好。

C++ <valarray>

掌握 C++ <valarray>:数值数组运算。

基本用法

#include <valarray>
using namespace std;

valarray<double> v1 = {1.0, 2.0, 3.0, 4.0};
valarray<double> v2 = {10.0, 20.0, 30.0, 40.0};

// 元素级运算
v1 + v2;    // {11, 22, 33, 44}
v1 * 2;     // {2, 4, 6, 8}
v1 * v2;    // 逐元素乘

v1.sum();       // 10(和)
v1.max();       // 4
v1.min();       // 1
v1.size();      // 4

// 数学函数(元素级)
sqrt(v1);
abs(v1);

切片与掩码

valarray<int> v = {1, 2, 3, 4, 5, 6};

// 切片:从 0 开始,步长 2,取 3 个
valarray<int> slice = v[slice(0, 3, 2)];   // {1, 3, 5}

// 掩码筛选
valarray<bool> mask = (v > 3);    // {f,f,f,t,t,t}
valarray<int> filtered = v[mask]; // {4, 5, 6}

valarray vs vector

对比项valarrayvector
运算元素级数学运算需手写循环/算法
切片内置无
用途数值计算通用容器
使用频率较低极高
💡 提示

valarray 适合数值密集运算(向量化);通用场景用 vector。数值计算库(如 Eigen)更强大,但 valarray 是标准库内置。

C++ <cmath>

掌握 C++ <cmath>:数学函数库。

常用数学函数

#include <cmath>
using namespace std;

// 基础
abs(-10);          // 10 整数绝对值
fabs(-3.5);        // 3.5 浮点绝对值
sqrt(16);          // 4.0 平方根
cbrt(27);          // 3.0 立方根
pow(2, 10);        // 1024.0 幂

// 取整
ceil(3.2);         // 4.0 向上
floor(3.8);        // 3.0 向下
round(3.5);        // 4.0 四舍五入
trunc(3.9);        // 3.0 截断
fmod(10, 3);       // 1.0 浮点取余

// 对数与指数
log(2.71828);      // 1.0 自然对数
log10(100);        // 2.0 常用对数
log2(8);           // 3.0 以 2 为底
exp(1);            // 2.71828 e 的幂
exp2(3);           // 8.0

// 三角函数(弧度)
sin(M_PI / 2);     // 1.0
cos(0);            // 1.0
tan(M_PI / 4);     // 1.0
asin(1);           // 反三角

// 双曲函数
sinh(0); cosh(0); tanh(0);

// 最值
fmax(3.5, 4.5);    // 4.5
fmin(3.5, 4.5);    // 3.5

常量

M_PI       // π = 3.14159...
M_E        // e = 2.71828...
M_SQRT2    // √2
// 需定义 _USE_MATH_DEFINES(MSVC)或使用 C++20 的 std::numbers

C++20 std::numbers

#include <numbers>
using namespace std;

numbers::pi;        // π
numbers::e;         // e
numbers::sqrt2;     // √2

注意

  • 角度与弧度:三角函数用弧度(角度 × π/180)
  • 整数运算先转 double:sqrt(static_cast<double>(n))
  • 负数 sqrt/负数对数返回 NaN
💡 提示

算法题常用:sqrt、pow、abs、min/max、ceil/floor;几何题用 atan2(y, x) 求角度。

C++ <string>

掌握 C++ <string>:字符串类的完整 API。

构造与基本操作

#include <string>
using namespace std;

string s1;                    // 空
string s2 = "hello";          // C 风格转
string s3("world");
string s4(5, 'a');            // "aaaaa"
string s5 = s2 + " " + s3;    // 拼接

s2.size();  s2.length();      // 5
s2.empty();
s2.clear();                   // 清空

查找

string s = "Hello CodeNest";

s.find("Code");        // 6
s.rfind("l");          // 从后找
s.find_first_of("e");  // 找字符
s.find_last_of("e");

// 找不到返回 npos
if (s.find("X") == string::npos) {
    cout << "未找到";
}

截取与修改

string s = "Hello CodeNest";

s.substr(6);          // "CodeNest"
s.substr(0, 5);       // "Hello"

s.insert(5, ",");     // "Hello, CodeNest"
s.erase(5, 1);        // 删除 5 位置 1 个字符
s.replace(6, 4, "C++");  // "Hello C++Nest"

// 转换
string up;
for (char c : s) up += toupper(c);   // 大写

// C++ 无内置大小写函数(用 transform)
transform(s.begin(), s.end(), s.begin(), ::tolower);

比较

string a = "apple", b = "banana";

a < b;        // true(字典序)
a == "apple"; // true
a.compare(b); // 负数/0/正数

数字转换

// 字符串 → 数字
int n = stoi("42");
double d = stod("3.14");
long l = stol("1000");
size_t sz = stoull("99");

// 数字 → 字符串
string s = to_string(3.14159);   // "3.141590"
string t = to_string(42);        // "42"

与 C 字符串互转

// string → const char*
const char* cstr = s.c_str();

// const char* → string
string back = cstr;
💡 提示

string 的 find 返回 size_t(npos = -1 的大数);频繁拼接用 += 或预先 reserve 优化。

C++ <regex>

掌握 C++ <regex>:正则表达式库(C++11+)。

基本用法

#include <regex>
using namespace std;

string text = "手机号 13812345678 和 13987654321";

// regex_match:整体匹配
regex emailPattern(R"(\w+@\w+\.\w+)");
bool isEmail = regex_match("a@b.com", emailPattern);

// regex_search:查找子串
smatch match;
if (regex_search(text, match, regex(R"(1[3-9]\d{9})"))) {
    cout << match[0];    // 13812345678(第一个匹配)
}

// regex_iterator:遍历所有匹配
for (sregex_iterator it(text.begin(), text.end(),
                        regex(R"(1[3-9]\d{9})")), end;
     it != end; ++it) {
    cout << (*it)[0] << " ";   // 13812345678 13987654321
}

替换与分割

// 替换
string result = regex_replace("a1b2c3", regex(R"(\d)"), "#");
// "a#b#c#"

// 分组替换
string phone = regex_replace("138-1234-5678",
    regex(R"((\d{3})-(\d{4})-(\d{4}))"),
    R"($1 $2 $3)");
// "138 1234 5678"

// 分割
vector<string> parts;
for (auto it = sregex_token_iterator("a,b;c",
         regex(R"([,;])"), -1), end;
     it != end; ++it) {
    parts.push_back(*it);
}
// {"a", "b", "c"}

分组提取

string date = "日期 2026-08-11";
regex pattern(R"((\d{4})-(\d{2})-(\d{2}))");
smatch m;

if (regex_search(date, m, pattern)) {
    m[1];   // 2026
    m[2];   // 08
    m[3];   // 11
}

常用模式

// 原始字符串 R"(...)" 避免转义地狱
R"(1[3-9]\d{9})"            // 手机号
R"(\w+@\w+\.\w+)"          // 邮箱
R"((\d{1,3}\.){3}\d{1,3})"  // IP
R"(\d{4}-\d{2}-\d{2})"      // 日期
💡 提示

用原始字符串 R"(...)" 写正则,避免反斜杠转义;性能敏感场景避免正则,用手写解析。

C++ <ctime>

掌握 C++ <ctime>:C 风格时间库。

基本用法

#include <ctime>
using namespace std;

// 当前时间戳(秒,自 1970-01-01)
time_t now = time(nullptr);

// 转本地时间结构
tm* local = localtime(&now);

local->tm_year + 1900;   // 年
local->tm_mon + 1;       // 月(0-11)
local->tm_mday;          // 日
local->tm_hour;          // 时
local->tm_min;           // 分
local->tm_sec;           // 秒
local->tm_wday;          // 星期(0=周日)

// UTC 时间
tm* gmt = gmtime(&now);

// 格式化
char buf[80];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
cout << buf;    // 2026-08-11 14:30:00

时间转换

// 字符串 → 时间
tm t = {};
strptime("2026-08-11", "%Y-%m-%d", &t);   // POSIX
time_t ts = mktime(&t);                    // tm → time_t

// time_t → tm
tm* t2 = localtime(&ts);

// 计算时间差
time_t start = time(nullptr);
// ... 执行代码 ...
time_t end = time(nullptr);
double seconds = difftime(end, start);    // 秒差

与 chrono 对比

对比项ctimechrono
精度秒纳秒
类型安全弱强
格式化strftime 方便需转 time_t
推荐格式化场景计时/计算
💡 提示

需要格式化时间字符串用 ctime 的 strftime;需要高精度计时/计算用 chrono。

C++ <chrono>

掌握 C++ <chrono>:现代时间库(C++11+)。

时钟与时间点

#include <chrono>
using namespace std::chrono;

// 三种时钟
system_clock::now();        // 系统时间(可转日历)
steady_clock::now();        // 单调时钟(只用于计时)
high_resolution_clock::now();  // 最高精度

// 时间点类型
time_point<system_clock> tp = system_clock::now();

// 转 time_t
time_t t = system_clock::to_time_t(tp);

时长 Duration

seconds s(5);          // 5 秒
milliseconds ms(100);  // 100 毫秒
minutes m(2);          // 2 分钟

// 转换
auto ms2 = duration_cast<milliseconds>(seconds(1));  // 1000ms
auto sec = duration_cast<seconds>(milliseconds(1500));  // 1s

// 字面量(C++14+)
using namespace std::chrono_literals;
auto t1 = 5s;      // 5 秒
auto t2 = 100ms;   // 100 毫秒
auto t3 = 2min;    // 2 分钟

计时器(最常用)

auto start = steady_clock::now();

// ... 执行要计时的代码 ...

auto end = steady_clock::now();
auto elapsed = duration_cast<milliseconds>(end - start);
cout << "耗时: " << elapsed.count() << " ms" << endl;

时间点运算

auto now = system_clock::now();
auto later = now + minutes(30);      // 30 分钟后
auto earlier = now - hours(1);       // 1 小时前

// 时间差
auto diff = later - now;             // 30 分钟
auto mins = duration_cast<minutes>(diff).count();  // 30
💡 提示

计时用 steady_clock(单调);要日历时间用 system_clock;duration_cast 做单位转换。这是 C++ 性能测试的标准姿势。

C++ <thread>

掌握 C++ <thread>:线程库(C++11+)。

创建线程

#include <thread>
using namespace std;

void worker(int id) {
    cout << "线程 " << id << " 开始" << endl;
}

int main() {
    thread t1(worker, 1);
    thread t2(worker, 2);

    // join:等待完成(必须 join 或 detach!)
    t1.join();
    t2.join();
    return 0;
}

join 与 detach

thread t(worker, 1);

t.join();       // 阻塞等待
// t.detach();  // 分离后台运行(不等待)

// 析构时若既未 join 也未 detach 会 std::terminate!
if (t.joinable()) t.join();   // 安全检查

Lambda 线程

thread t([](int x) {
    cout << "计算: " << x * x << endl;
}, 5);
t.join();

多线程与引用

// 传引用需用 std::ref(否则拷贝)
void modify(int& x) { x = 100; }

int value = 0;
thread t(modify, ref(value));
t.join();
// value == 100

硬件并发数

unsigned n = thread::hardware_concurrency();
cout << "硬件线程数: " << n;

// 线程池常用:min(目标线程数, hardware_concurrency())

注意

  • 共享数据要加锁(mutex)
  • 线程必须 join 或 detach
  • 传递引用用 std::ref
  • 创建线程有开销,批量任务用线程池
💡 提示

简单并行用 std::async;需要精细控制用 thread + mutex;批量任务建线程池。别裸开大量线程。

C++ <mutex>

掌握 C++ <mutex>:互斥锁(线程同步)。

基本用法

#include <mutex>
#include <thread>
using namespace std;

int counter = 0;
mutex mtx;    // 互斥锁

void increment() {
    for (int i = 0; i < 100000; i++) {
        mtx.lock();
        counter++;      // 临界区
        mtx.unlock();
    }
}

int main() {
    thread t1(increment);
    thread t2(increment);
    t1.join(); t2.join();
    cout << counter;    // 200000(正确)
    return 0;
}

lock_guard(推荐)

// RAII:构造加锁,析构自动解锁(异常安全)
void increment() {
    for (int i = 0; i < 100000; i++) {
        lock_guard<mutex> lock(mtx);   // 加锁
        counter++;                      // 临界区
    }   // 自动解锁
}

unique_lock(灵活)

// 比 lock_guard 灵活:可手动解锁、延迟加锁
void task() {
    unique_lock<mutex> lock(mtx, defer_lock);  // 延迟加锁
    // 做其他事...
    lock.lock();      // 手动加锁
    // 临界区
    lock.unlock();    // 可提前解锁
    // 继续其他事
}

死锁与防护

// 死锁:两个线程互相等对方的锁
// 解决:锁顺序一致,或一次性锁多个

mutex m1, m2;

// 用 lock 一次性锁定多个(避免死锁)
void safe() {
    lock(m1, m2);
    lock_guard<mutex> l1(m1, adopt_lock);
    lock_guard<mutex> l2(m2, adopt_lock);
    // 临界区
}

原子操作替代

#include <atomic>
atomic<int> counter{0};   // 无锁,更快

void increment() {
    for (int i = 0; i < 100000; i++) {
        counter++;        // 原子自增
    }
}

选择

场景方案
简单临界区lock_guard
需要提前解锁unique_lock
读多写少shared_mutex(C++17)
单个变量atomic(最快)
💡 提示

优先 atomic(无锁);需要保护复杂临界区用 lock_guard;能减少锁粒度就减少(锁住越小越好)。

C++ <condition_variable>

掌握 C++ <condition_variable>:条件变量(线程通信)。

生产者-消费者

#include <condition_variable>
#include <mutex>
#include <queue>
using namespace std;

mutex mtx;
condition_variable cv;
queue<int> dataQueue;

// 生产者
void producer() {
    for (int i = 0; i < 10; i++) {
        {
            lock_guard<mutex> lock(mtx);
            dataQueue.push(i);
        }                       // 解锁后通知
        cv.notify_one();        // 通知一个等待线程
    }
}

// 消费者
void consumer() {
    while (true) {
        unique_lock<mutex> lock(mtx);
        // 等待条件(带谓词防止虚假唤醒)
        cv.wait(lock, []{ return !dataQueue.empty(); });

        int val = dataQueue.front();
        dataQueue.pop();
        lock.unlock();

        cout << "消费: " << val << endl;
    }
}

wait 的两种形式

// 1. 无条件等待
cv.wait(lock);                    // 可能虚假唤醒!

// 2. 带谓词(推荐)
cv.wait(lock, []{ return ready; });
// 等价于:
while (!ready) cv.wait(lock);     // 循环检查

notify_one vs notify_all

cv.notify_one();    // 唤醒一个等待线程
cv.notify_all();    // 唤醒所有等待线程

// 单消费者用 notify_one
// 多消费者/广播用 notify_all

wait_for(超时等待)

// 最多等 1 秒
if (cv.wait_for(lock, seconds(1),
                []{ return ready; })) {
    cout << "条件满足";
} else {
    cout << "超时";
}

注意事项

  • wait 必须配合 unique_lock(不能 lock_guard)
  • 务必用带谓词的 wait(防虚假唤醒)
  • 改变共享状态后要 notify
  • notify 在解锁后调用更高效
💡 提示

条件变量解决"等待某个条件成立"的线程同步:生产者-消费者、任务队列、线程池都依赖它。

C++ <future>

掌握 C++ <future>:异步任务与结果获取。

async 基本用法

#include <future>
#include <thread>
using namespace std;

int compute(int x) {
    this_thread::sleep_for(seconds(1));  // 模拟耗时
    return x * x;
}

int main() {
    // 异步启动任务
    future<int> result = async(launch::async, compute, 6);

    // 做其他事...
    cout << "主线程继续..." << endl;

    // 获取结果(阻塞等待)
    int value = result.get();
    cout << "结果: " << value << endl;   // 36
    return 0;
}

launch 策略

// launch::async:新线程执行
// launch::deferred:惰性(get 时才同步执行)
// launch::async | launch::deferred:由实现决定(默认)

future<int> f1 = async(launch::async, compute, 6);
future<int> f2 = async(launch::deferred, compute, 7);

packaged_task

// 包装可调用对象为异步任务
packaged_task<int(int)> task(compute);
future<int> result = task.get_future();

thread t(move(task), 8);   // 在线程中执行
t.join();

cout << result.get();      // 64

promise / future 配对

// 线程间传值
void setValue(promise<int>& prom) {
    this_thread::sleep_for(seconds(1));
    prom.set_value(42);           // 设置结果
}

promise<int> prom;
future<int> fut = prom.get_future();

thread t(setValue, ref(prom));
t.join();

cout << fut.get();    // 42(等待 promise 设置值)

并发执行多个任务

// 同时启动多个异步任务
auto f1 = async(launch::async, compute, 3);
auto f2 = async(launch::async, compute, 4);
auto f3 = async(launch::async, compute, 5);

// 汇总结果(并行计算)
int total = f1.get() + f2.get() + f3.get();   // 50

异常传播

// 异步任务中的异常在 get() 时抛出
future<int> f = async(launch::async, []() -> int {
    throw runtime_error("任务失败");
    return 0;
});

try {
    f.get();
} catch (const exception& e) {
    cout << "捕获: " << e.what();
}
💡 提示

future/async 是"简单并行"的首选:不用手管线程,异常和结果自动传递。比裸 thread 安全得多。

C++ <atomic>

掌握 C++ <atomic>:原子类型(无锁线程安全)。

基本用法

#include <atomic>
using namespace std;

atomic<int> counter{0};     // 原子整数

counter++;                  // 原子自增
counter--;                  // 原子自减
counter.fetch_add(5);       // 原子加 5
counter.fetch_sub(2);       // 原子减 2
counter.load();             // 读取(原子)
counter.store(100);         // 写入(原子)
counter.exchange(50);       // 交换并返回旧值

// 比较交换
int expected = 100;
counter.compare_exchange_strong(expected, 200);
// 若 counter == 100 则设为 200,否则 expected = counter

支持的原子类型

atomic<int>      atomic<long>
atomic<bool>     atomic<double>
atomic<int*>     atomic<size_t>
atomic_flag      // 最简原子布尔(仅 test_and_set/clear)

// 自定义类型需是 trivially copyable
atomic<shared_ptr<T>>   // C++20 支持

内存序(memory_order)

// 默认 seq_cst(顺序一致,最安全)
counter.store(1, memory_order_seq_cst);

// 常用:
// memory_order_relaxed:只保证原子性(不保证顺序)
// memory_order_acquire/release:配对保证可见性
// 默认用 seq_cst,性能敏感再优化

典型应用

// 计数器(多线程自增)
atomic<int> hits{0};
// hits++ 无需加锁

// 标志位
atomic<bool> stop{false};
// 线程检查 stop.load(),主线程 stop.store(true)

// 自旋锁
class SpinLock {
    atomic<bool> flag{false};
public:
    void lock() {
        while (flag.exchange(true)) { /* 自旋 */ }
    }
    void unlock() { flag.store(false); }
};

atomic vs mutex

对比项atomicmutex
性能快(无锁)慢(有系统调用)
适用单个变量复杂临界区
简单简单需管理锁
💡 提示

单个共享变量用 atomic(计数器、标志位);多个变量的一致性保护用 mutex。atomic 不是"万能锁"。

C++ <type_traits>

掌握 C++ <type_traits>:类型特性(编译期类型查询)。

类型判断

#include <type_traits>
using namespace std;

is_integral<int>::value;       // true(整数类型)
is_floating_point<double>::value;  // true(浮点)
is_pointer<int*>::value;       // true(指针)
is_reference<int&>::value;     // true(引用)
is_const<const int>::value;    // true(常量)
is_same<int, int>::value;      // true(相同类型)
is_base_of<Base, Derived>::value;  // 继承关系

// C++17 简写(_v 后缀)
is_integral_v<int>             // true

类型修改

remove_const<const int>::type;    // int
add_const<int>::type;             // const int
remove_reference<int&>::type;     // int
remove_pointer<int*>::type;       // int
decay<const int&>::type;          // int(退化为值类型)
conditional<true, int, double>::type;  // int(条件类型)

enable_if(SFINAE)

// 模板条件启用
template <typename T>
enable_if_t<is_integral_v<T>, T>   // 仅整数类型可用
twice(T x) {
    return x * 2;
}

twice(5);       // ✅ int
// twice(5.5);  // ❌ 编译错误(double 不满足)

if constexpr(C++17)

// 编译期条件分支
template <typename T>
void process(T value) {
    if constexpr (is_integral_v<T>) {
        cout << "整数: " << value << endl;
    } else if constexpr (is_floating_point_v<T>) {
        cout << "浮点: " << value << endl;
    } else {
        cout << "其他类型" << endl;
    }
}
// 注意:if constexpr 分支在编译期选择,不生成代码

应用

  • 泛型代码中按类型分支处理
  • SFINAE 约束模板参数
  • 类型安全的序列化/转换
  • 库开发中的类型检查
💡 提示

type_traits 是模板元编程的基础;C++17 的 if constexpr 让按类型分支简单直观。

C++ <typeinfo>

掌握 C++ <typeinfo>:运行时类型信息(RTTI)。

typeid 运算符

#include <typeinfo>
#include <iostream>
using namespace std;

int x = 42;
const type_info& t = typeid(x);
cout << t.name();    // 实现定义(如 "i")

// 类型比较
if (typeid(x) == typeid(int)) {
    cout << "是 int" << endl;
}

// 多态对象的动态类型
class Animal { virtual ~Animal() {} };
class Dog : public Animal {};

Animal* a = new Dog();
typeid(*a).name();     // Dog(运行时类型)
typeid(a).name();      // Animal*(静态类型,指针自身)

dynamic_cast

// 安全向下转型(需多态)
Animal* animal = new Dog();

// 成功返回 Dog*,失败返回 nullptr
if (Dog* dog = dynamic_cast<Dog*>(animal)) {
    dog->bark();
} else {
    cout << "不是 Dog" << endl;
}

// 引用版本(失败抛 bad_cast)
Dog& dogRef = dynamic_cast<Dog&>(*animal);

RTTI 注意事项

  • typeid/dynamic_cast 依赖 RTTI(默认开启)
  • 编译可禁用(-fno-rtti),但 dynamic_cast 不可用
  • 有运行时开销(虚表查询)
  • 优先用虚函数而非 dynamic_cast(更好的设计)
// 设计建议:用虚函数替代类型判断
class Animal {
public:
    virtual void speak() = 0;
    virtual ~Animal() {}
};
// 而不是 if (typeid(*a) == typeid(Dog))
💡 提示

typeid 查类型、dynamic_cast 安全转型;但好的多态设计应少用 RTTI——用虚函数表达行为差异。

C++ <exception>

掌握 C++ <exception>:异常基类与处理机制。

标准异常层次

#include <exception>
using namespace std;

// 层次结构
exception
├── logic_error
│   ├── invalid_argument
│   ├── domain_error
│   ├── length_error
│   └── out_of_range
├── runtime_error
│   ├── range_error
│   ├── overflow_error
│   └── underflow_error
└── bad_alloc(内存分配失败)
    bad_cast、bad_typeid

基本处理

try {
    vector<int> v(1000000000000);   // bad_alloc
} catch (const bad_alloc& e) {
    cout << "内存不足: " << e.what();
} catch (const exception& e) {
    cout << "异常: " << e.what();
} catch (...) {
    cout << "未知异常";
}

自定义异常

// 继承 exception
class MyError : public exception {
    string msg;
public:
    explicit MyError(const string& m) : msg(m) {}

    // 覆盖 what(),返回错误信息
    const char* what() const noexcept override {
        return msg.c_str();
    }
};

throw MyError("业务错误");

try {
    // ...
} catch (const MyError& e) {
    cout << e.what();
}

异常安全级别

级别说明
基本保证异常后对象有效但状态不确定
强保证异常后状态回滚(提交/回滚模式)
无抛出保证不抛异常(析构、swap)

注意事项

  • 析构函数不要抛异常(会 terminate)
  • 按引用捕获(避免切片)
  • 构造失败用异常(无法用返回值)
  • noexcept 标记不抛异常的函数
💡 提示

异常处理三原则:按引用捕获、析构不抛、RAII 保证清理。自定义异常继承 std::exception 并重写 what()。

C++ <stdexcept>

掌握 C++ <stdexcept>:标准异常类。

常用标准异常

#include <stdexcept>
using namespace std;

// 逻辑错误(程序 bug)
invalid_argument("参数无效");
out_of_range("索引越界");
length_error("长度错误");
domain_error("定义域错误");

// 运行时错误(环境/数据问题)
runtime_error("运行时错误");
range_error("范围错误");
overflow_error("数值溢出");
underflow_error("数值下溢");

使用示例

int at(vector<int>& v, size_t i) {
    if (i >= v.size()) {
        throw out_of_range("索引 " + to_string(i) + " 越界");
    }
    return v[i];
}

int divide(int a, int b) {
    if (b == 0) {
        throw invalid_argument("除数不能为 0");
    }
    return a / b;
}

double sqrtSafe(double x) {
    if (x < 0) {
        throw domain_error("不能对负数开方");
    }
    return sqrt(x);
}

捕获顺序

try {
    // ...
} catch (const invalid_argument& e) {
    // 具体类型先捕获
} catch (const logic_error& e) {
    // 父类
} catch (const exception& e) {
    // 基类兜底
}

异常 vs 错误码

对比项异常错误码
传播自动向上手动传递
忽略风险不可忽略(强制处理)易被忽略
性能有开销快
适用业务逻辑错误性能热路径
💡 提示

标准异常类开箱即用:参数校验用 invalid_argument,索引检查用 out_of_range,数值问题用 overflow_error。

C++ <cstdio>

掌握 C++ <cstdio>:C 风格标准输入输出。

printf / scanf

#include <cstdio>
using namespace std;

// 格式化输出
printf("Hello %s, age %d, score %.1f
",
       "张三", 25, 88.5);

// 格式符
%d     // 整数
%f     // 浮点(%.2f 两位小数)
%c     // 字符
%s     // 字符串
%x     // 十六进制
%p     // 指针
%zu    // size_t

// 输入
int age;
printf("输入年龄: ");
scanf("%d", &age);    // 注意取地址 &

char name[50];
scanf("%49s", name);   // 字符串(限长防溢出)

字符串格式化

char buf[100];
snprintf(buf, sizeof(buf), "值: %d, 名: %s", 42, "test");
// 安全:限长

// 现代替代:std::format(C++20)
#include <format>
string s = format("值: {}, 名: {}", 42, "test");

文件操作

FILE* fp = fopen("data.txt", "w");
if (fp) {
    fprintf(fp, "写入 %d
", 100);
    fclose(fp);
}

fp = fopen("data.txt", "r");
char line[256];
while (fgets(line, sizeof(line), fp)) {
    printf("%s", line);
}
fclose(fp);

printf vs cout

对比项printfcout
类型安全弱(格式符易错)强
扩展性差(自定义类型难)重载 << 即可
性能快略慢(可加速)
推荐C 兼容/性能✅ 现代 C++
💡 提示

新代码用 cout/format;需要与 C 库交互或极致性能用 printf。scanf 务必注意缓冲区溢出。

C++ <cstdint>

掌握 C++ <cstdint>:定宽整数类型。

定宽整数

#include <cstdint>
using namespace std;

int8_t      // 8 位有符号(-128~127)
uint8_t     // 8 位无符号(0~255)
int16_t     // 16 位
uint16_t
int32_t     // 32 位(跨平台保证 4 字节)
uint32_t
int64_t     // 64 位
uint64_t

// 最小宽度
int_least8_t   // 至少 8 位
int_fast32_t   // 最快 32 位

// 最大整数
intmax_t    // 最大有符号
uintmax_t

为什么用定宽类型

// int 大小依赖平台(32 位系统 4 字节,部分 16 位系统 2 字节)
// 跨平台/网络协议/文件格式需要确定字节数

int32_t port = 8080;      // 任何平台都是 4 字节
uint64_t timestamp;       // 任何平台都是 8 字节

// 网络字节序转换配合使用
htonl(port);  ntohl(port);

常量与打印

INT32_MAX;   // 2147483647
INT32_MIN;
UINT64_MAX;  // 18446744073709551615
SIZE_MAX;    // size_t 最大值

// 打印
printf("%" PRId64 "
", int64_value);   // 可移植打印
cout << int64_value << endl;            // cout 更简单

应用场景

  • 网络协议(固定字段宽度)
  • 文件格式(二进制布局)
  • 嵌入式/跨平台代码
  • 位运算与掩码
💡 提示

需要"确定大小"的整数(协议、文件、跨平台)用 cstdint 的定宽类型;普通计数用 int 即可。

C++ <memory>

掌握 C++ <memory>:智能指针与内存管理。

unique_ptr(独占)

#include <memory>
using namespace std;

// make_unique(推荐,异常安全)
unique_ptr<int> p = make_unique<int>(42);
unique_ptr<string> s = make_unique<string>("hello");

*p;             // 42
p->size();      // 成员访问(若为对象)
p.get();        // 裸指针
p.reset();      // 释放并置空
p.reset(new int(100));  // 替换

// 独占:不能拷贝
// unique_ptr<int> q = p;   // ❌
unique_ptr<int> q = move(p);  // ✅ 转移所有权

shared_ptr(共享)

auto p1 = make_shared<int>(42);
auto p2 = p1;            // ✅ 可拷贝(引用计数 +1)

p1.use_count();   // 2
p1.unique();      // false(非独占)

p1.reset();       // 计数 -1
p2.reset();       // 计数 0 → 对象释放

weak_ptr(弱引用)

// 观察但不拥有,解决循环引用
weak_ptr<int> wp;

{
    auto sp = make_shared<int>(42);
    wp = sp;
    // sp 作用域结束,对象释放
}

if (auto sp = wp.lock()) {   // 尝试获取
    cout << *sp;             // 对象还在
} else {
    cout << "已释放";
}

循环引用问题

struct Node {
    shared_ptr<Node> next;   // 循环引用泄漏!
    // 应改为:
    weak_ptr<Node> next;     // ✅ 不增加计数
};

// A -> B,B -> A 会互相持有导致永不释放
// 用 weak_ptr 打破循环

自定义删除器

// 管理非 new 资源(如文件句柄)
unique_ptr<FILE, decltype(&fclose)> file(fopen("a.txt", "r"), fclose);
// 离开作用域自动 fclose

选择指南

场景选择
独占资源unique_ptr
多方共享shared_ptr
观察/防循环weak_ptr
不需要所有权裸指针/引用
💡 提示

现代 C++ 黄金法则:优先 make_unique/make_shared,禁止裸 new。智能指针是内存安全的第一道防线。

C++ <new>

掌握 C++ <new>:动态内存分配。

new / delete

#include <new>
using namespace std;

// 分配单个
int* p = new int;           // 未初始化
int* p2 = new int(42);      // 初始化为 42
int* p3 = new int{42};      // 列表初始化

// 分配数组
int* arr = new int[10];
int* arr2 = new int[10]{1, 2, 3};   // 部分初始化

// 释放
delete p;
delete[] arr;     // 数组必须 delete[]

// 二维
int** matrix = new int*[3];
for (int i = 0; i < 3; i++) matrix[i] = new int[4];

new 失败处理

// 默认:失败抛 bad_alloc
try {
    int* p = new int[1000000000];
} catch (const bad_alloc& e) {
    cout << "内存不足: " << e.what();
}

// nothrow:失败返回 nullptr
int* p = new (nothrow) int[1000000000];
if (!p) {
    cout << "分配失败";
}

placement new(定位构造)

// 在指定内存上构造对象
char buffer[sizeof(int)];
int* p = new (buffer) int(42);   // 在 buffer 上构造

// 显式调用析构
p->~int();

// 用于内存池、缓存优化

⚠️ 常见错误

// 内存泄漏:忘记 delete
// 悬垂指针:delete 后使用
// 双重释放:delete 两次
// new[] / delete 不匹配
// 数组越界写

// 解决方案:智能指针 + RAII
auto p = make_unique<int[]>(10);   // 数组版
💡 提示

裸 new/delete 是 bug 温床。现代 C++:栈对象优先,智能指针其次,new 仅用于特殊场景。

C++ <utility>

掌握 C++ <utility>:通用工具(pair、move、swap)。

std::pair

#include <utility>
using namespace std;

pair<string, int> p("张三", 25);
p.first;    // "张三"
p.second;   // 25

// 快捷构造
auto p2 = make_pair("李四", 30);

// 结构化绑定(C++17)
auto [name, age] = p2;
// name="李四", age=30

// 比较(先 first 后 second)
p < p2;

std::move

// 转为右值引用,启用移动语义
string a = "hello";
string b = move(a);    // a 的内容被"移动"给 b
// a 处于"有效但未指定"状态(通常为空)

// 移动 vs 拷贝:移动更高效(不复制数据)

// 用途:
// 1. 移动构造/赋值
// 2. 传入需要右值的函数(如 unique_ptr 转移)

std::swap

int x = 1, y = 2;
swap(x, y);    // x=2, y=1

vector<int> v1(100000), v2(100000);
swap(v1, v2);  // O(1) 交换指针(高效)

std::exchange / std::forward

// exchange:赋值并返回旧值
int a = 10;
int old = exchange(a, 20);   // old=10, a=20

// forward:完美转发(模板中)
template <typename T>
void wrapper(T&& arg) {
    inner(forward<T>(arg));   // 保持左值/右值属性
}

std::cmp 比较函数(C++20)

#include <utility>
// cmp_equal / cmp_less / cmp_greater
cmp_less(5u, -1);    // false(安全比较,防符号坑)
💡 提示

pair 用于"两个值"的返回;move 触发移动语义(高效转移);swap 交换容器 O(1)。都是高频小工具。

C++ <random>

掌握 C++ <random>:随机数生成库(C++11+)。

现代随机数流程

#include <random>
using namespace std;

// 1. 随机种子(硬件熵源)
random_device rd;

// 2. 伪随机引擎
mt19937 gen(rd());    // 梅森旋转(质量好)

// 3. 分布器
uniform_int_distribution<> dice(1, 6);   // 掷骰子

// 4. 生成
int roll = dice(gen);   // 1-6

常用分布

uniform_int_distribution<> uni(1, 100);      // 均匀整数
uniform_real_distribution<double> ur(0.0, 1.0);  // 均匀实数

normal_distribution<double> norm(0.0, 1.0);  // 正态分布
bernoulli_distribution coin(0.5);            // 伯努利(掷硬币)
discrete_distribution<> weights({1, 2, 3});  // 加权选择
poisson_distribution<> poisson(4.0);         // 泊松分布

引擎对比

引擎特点
mt19937梅森旋转,质量好(推荐)
mt19937_6464 位版
minstd_rand线性同余,快但质量一般
ranlux24高质量

随机选择元素

vector<string> names = {"A", "B", "C", "D"};
uniform_int_distribution<size_t> pick(0, names.size() - 1);
string chosen = names[pick(gen)];

// 洗牌
shuffle(names.begin(), names.end(), gen);

rand() 的局限

// 旧 API(了解即可,不推荐)
srand(time(nullptr));
int x = rand() % 100 + 1;   // 有偏、质量差、不可复现

// 现代:随机引擎 + 分布器
// 分布均匀、可复现(种子固定)、类型安全
💡 提示

随机数标准姿势:random_device 种子 + mt19937 引擎 + 分布器。需要可复现测试时固定种子(如 mt19937 gen(42))。

C++ <locale>

掌握 C++ <locale>:本地化与字符集。

locale 基础

#include <locale>
using namespace std;

// 获取全局 locale
locale loc = locale::global(locale(""));

// 常见 locale 名称
"zh_CN.UTF-8"   // 中文
"en_US.UTF-8"   // 美式英文
"C"             // 默认(C 风格)

// 切换全局 locale
locale::global(locale("zh_CN.UTF-8"));

数字/货币格式化

// 使用 locale 的 facet
locale loc("en_US.UTF-8");

// 数字分组(千分位)
cout.imbue(loc);
cout << 1234567.89 << endl;   // 1,234,567.89

// 货币格式化
const money_put<char>& mp = use_facet<money_put<char>>(loc);

字符分类(locale 相关)

// 注意:isupper 等受 locale 影响
locale loc("zh_CN.UTF-8");

isupper('A', loc);   // true
isdigit('5', loc);   // true

// 但中文等宽字符处理要小心(C 风格函数不支持)

宽字符与编码

// wchar_t 处理 Unicode(配合 wstring)
wstring ws = L"你好";
wcout << ws << endl;

// 现代推荐:用 UTF-8 的 std::string + 编码转换库
// C++20 char8_t / std::filesystem 原生支持 UTF-8
💡 提示

locale 用于数字/货币/排序的本地化;现代 C++ 处理文本推荐 UTF-8 std::string,locale 主要用于格式化输出。

C++ <codecvt>

掌握 C++ <codecvt>:字符编码转换(已弃用,了解即可)。

codecvt 背景

// C++11 提供 UTF-8/16/32 转换
// C++17 标记为弃用(deprecated)
// C++23 移除(建议用第三方库)

#include <codecvt>
// 已弃用,不建议新代码使用

转换示例(旧代码参考)

// UTF-8 → UTF-16
wstring_convert<codecvt_utf8_utf16<char16_t>, char16_t> conv;
u16string utf16 = conv.from_bytes("你好");

// UTF-16 → UTF-8
string utf8 = conv.to_bytes(utf16);

现代替代方案

// 1. C++20:char8_t + std::filesystem
u8"UTF-8 字符串";    // const char8_t*

// 2. 第三方库(推荐)
// ICU(国际组件库)
// utf8cpp(轻量 UTF-8 处理)
// 开源的 u8 转换函数

// 3. 简单场景手写转换
// UTF-8 解析:按首字节判断字符长度

实用建议

  • 内部统一用 UTF-8 std::string
  • 与外部交互(文件/网络)按需转换
  • 需要完整 Unicode 处理用 ICU
  • 避免 wchar_t 滥用(跨平台大小不同)
💡 提示

codecvt 已弃用。现代 C++ 文本处理策略:UTF-8 为唯一内部编码,必要时用 utf8cpp 或 ICU 转换。

C++ <cassert>

掌握 C++ <cassert>:断言调试。

assert 基本用法

#include <cassert>
using namespace std;

int divide(int a, int b) {
    assert(b != 0);      // 条件为假则终止程序
    return a / b;
}

// assert 失败输出:Assertion `b != 0' failed.
// 并终止程序(abort)

启用/禁用断言

// 定义 NDEBUG 则 assert 不生效
// 编译时:-DNDEBUG

// 调试版:启用断言(默认)
g++ main.cpp -o app

// 发布版:禁用断言
g++ main.cpp -o app -DNDEBUG

assert 使用场景

// 前置条件
void push(vector<int>& v, int x) {
    assert(v.size() < 1000000);   // 大小限制
    v.push_back(x);
}

// 后置条件
int abs(int x) {
    int r = x >= 0 ? x : -x;
    assert(r >= 0);               // 结果检查
    return r;
}

// 不变量
class Stack {
    // ...
    void checkInvariant() {
        assert(size <= capacity);
    }
};

assert vs 异常

对比项assert异常
用途调试期逻辑检查运行时错误处理
NDEBUG发布版消失始终生效
失败行为终止程序可捕获处理
适用程序员错误(bug)可恢复/环境错误
💡 提示

assert 用于"不可能发生"的假设检查(调试利器);用户输入校验用 if + 异常/错误码。发布版记得 -DNDEBUG 关闭。

C++ <cwchar>

掌握 C++ <cwchar>:宽字符函数(C 风格)。

宽字符基础

#include <cwchar>
using namespace std;

// 宽字符(通常 4 字节,可表示 Unicode)
wchar_t wc = L'中';
wstring ws = L"你好世界";

// 宽字符串操作
wcslen(ws.c_str());          // 长度
wcscmp(a.c_str(), b.c_str());  // 比较
wcscpy(dest, src);           // 复制
wcscat(dest, src);           // 拼接

// 宽字符 I/O
wcout << L"宽输出" << endl;
wcin >> wc;

宽字符格式化

wchar_t buf[100];
swprintf(buf, 100, L"数字: %d", 42);

// 宽字符转换
wcsrtombs / mbsrtowcs   // 宽窄互转(受 locale 影响)

wchar_t 的跨平台问题

平台wchar_t 大小
Windows2 字节(UTF-16)
Linux/macOS4 字节(UTF-32)
// 大小不同 → 代码不可移植!
// 现代 C++ 建议:
// - 内部统一 UTF-8 std::string
// - 跨平台 Unicode 用 char16_t/char32_t 或第三方库
// - 避免直接使用 wchar_t 做跨平台开发
💡 提示

wchar_t 跨平台大小不一致,现代 C++ 推荐 UTF-8 std::string;需要宽字符时用 char16_t/char32_t(定宽)。

C++ <climits>

掌握 C++ <climits>:整数类型极限值。

整数极限常量

#include <climits>
using namespace std;

CHAR_BIT      // 每字节位数(通常 8)
CHAR_MAX      // char 最大值
CHAR_MIN

INT_MAX       // int 最大值:2147483647
INT_MIN       // int 最小值:-2147483648
UINT_MAX      // unsigned int 最大值

LONG_MAX / LONG_MIN
LLONG_MAX / LLONG_MIN   // long long

SHRT_MAX / SHRT_MIN     // short

使用场景

// 初始化"最大/最小"哨兵值
int minVal = INT_MAX;
for (int x : nums) {
    minVal = min(minVal, x);
}

// 溢出检查
int addSafe(int a, int b) {
    if (a > INT_MAX - b) {
        throw overflow_error("整数溢出");
    }
    return a + b;
}

// 与类型实际大小对比
static_assert(sizeof(int) == 4, "int 应为 4 字节");

溢出陷阱

// 有符号溢出是未定义行为!
int x = INT_MAX;
x + 1;    // ❌ 未定义行为(可能变成负数)

// 无符号溢出:回绕(定义行为,但通常不是想要的)
unsigned y = UINT_MAX;
y + 1;    // 0(回绕)

// 用 long long 或先检查避免溢出
💡 提示

INT_MAX/MIN 常用于哨兵值和溢出检查;涉及大数计算用 long long,可能溢出时先判断。

C++ <cfloat>

掌握 C++ <cfloat>:浮点数极限与精度。

浮点极限常量

#include <cfloat>
using namespace std;

FLT_MAX       // float 最大值:~3.4e38
FLT_MIN       // float 最小正数:~1.2e-38
DBL_MAX       // double 最大值:~1.8e308
DBL_MIN       // double 最小正数:~2.2e-308

FLT_DIG       // float 十进制有效数字:6
DBL_DIG       // double 十进制有效数字:15

FLT_EPSILON   // 1.0 与下一个 float 的差:~1.2e-7
DBL_EPSILON   // ~2.2e-16

浮点比较(重要)

// ❌ 不要直接用 == 比较浮点
double a = 0.1 + 0.2;    // 0.30000000000000004
if (a == 0.3) { }        // false!

// ✅ 用误差范围比较
bool approxEqual(double x, double y, double eps = 1e-9) {
    return fabs(x - y) < eps;
}

approxEqual(0.1 + 0.2, 0.3);   // true

精度注意事项

// float 只有约 7 位有效数字
float f = 3.14159265358979f;
// 存储为 3.1415927(精度丢失)

// 金额计算避免浮点(用整数分或 BigDecimal)

// 大数小数相减精度差
double x = 1e16;
x + 1 == x;    // true!(1 被吃掉)

// 避免接近 0 的数作分母

特殊值

#include <cmath>

isfinite(x);    // 是否有限
isnan(x);       // 是否 NaN
isinf(x);       // 是否无穷

// NaN 判断不能用 ==
double n = 0.0 / 0.0;    // NaN
n == n;    // false!(NaN != NaN)
isnan(n);  // true
💡 提示

浮点三坑:不能 == 比较、精度有限、NaN 不自等。金额用整数/定点数,比较用 epsilon。

C++ <cstdlib>

掌握 C++ <cstdlib>:C 标准工具库。

内存管理

#include <cstdlib>
using namespace std;

// C 风格内存(与 C 库交互时用)
void* p = malloc(100);
free(p);

// C++ 中优先 new/delete 或智能指针

数值转换

int n = atoi("42");          // 字符串 → int
long l = atol("1000");
double d = atof("3.14");

// 更安全的 strtol(可检测错误)
char* end;
long val = strtol("123abc", &end, 10);   // val=123, end 指向 "abc"

// 现代 C++:stoi / stod 更安全

随机数(旧 API)

srand(time(nullptr));    // 种子
int r = rand();          // 0 ~ RAND_MAX
int dice = rand() % 6 + 1;   // 1-6
// 现代 C++ 用 <random> 引擎

进程控制

exit(0);          // 立即退出
abort();          // 异常终止
system("ls");     // 执行系统命令(慎用!)

// atexit 注册退出回调
atexit([](){ cout << "程序退出"; });

排序与查找(C 风格)

int arr[] = {5, 2, 8, 1, 9};
int n = 5;

// 排序
qsort(arr, n, sizeof(int),
      [](const void* a, const void* b) {
          return *(int*)a - *(int*)b;
      });

// 二分查找(需有序)
int* found = (int*)bsearch(&key, arr, n, sizeof(int),
                           [](const void* a, const void* b) {
                               return *(int*)a - *(int*)b;
                           });

// C++ 优先 std::sort / binary_search
💡 提示

cstdlib 的多数功能有 C++ 现代替代(stoi、sort、random)。它主要用于 C 库兼容和进程控制(exit/system)。

C++ <numbers>

掌握 C++ <numbers>:数学常量(C++20+)。

数学常量

#include <numbers>
using namespace std;

numbers::pi;         // π = 3.1415926535...
numbers::pi_v<float>;  // 指定类型
numbers::e;          // 自然常数 e
numbers::sqrt2;      // √2
numbers::ln2;        // ln 2
numbers::ln10;       // ln 10
numbers::log2e;      // log2(e)
numbers::phi;        // 黄金比例 φ

// 类型指定
numbers::pi_v<double>
numbers::e_v<long double>

与传统宏对比

// 旧方式:M_PI(非标准,需 _USE_MATH_DEFINES)
// #define _USE_MATH_DEFINES
// #include <cmath>
// M_PI

// 新方式:标准、类型安全、支持任意浮点类型
numbers::pi;                    // double
numbers::pi_v<float>;           // float
numbers::pi_v<long double>;     // long double

使用示例

// 圆面积
double circleArea(double r) {
    return numbers::pi * r * r;
}

// 角度转弧度
double toRadians(double degrees) {
    return degrees * numbers::pi / 180.0;
}

// 黄金比例相关
double golden = numbers::phi;   // 1.618...
💡 提示

C++20 的 <numbers> 是数学常量的标准来源(替代非标准 M_PI),类型安全且支持任意浮点类型。

C++ OpenCV

掌握 C++ OpenCV:计算机视觉库集成。

OpenCV 简介

OpenCV(Open Source Computer Vision Library)是最流行的开源计算机视觉库,C++ 是其首选语言,支持图像处理、视频分析、机器学习等。

安装

# Ubuntu
sudo apt install libopencv-dev

# macOS
brew install opencv

# Windows/跨平台:vcpkg
vcpkg install opencv4

读取与显示图像

#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
    // 读取图像
    Mat img = imread("photo.jpg");
    if (img.empty()) {
        std::cerr << "无法读取图像" << std::endl;
        return -1;
    }

    // 显示
    imshow("窗口", img);
    waitKey(0);        // 等待按键
    return 0;
}

图像处理

// 灰度化
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);

// 模糊
Mat blurred;
GaussianBlur(img, blurred, Size(5, 5), 0);

// 边缘检测(Canny)
Mat edges;
Canny(gray, edges, 100, 200);

// 缩放
Mat resized;
resize(img, resized, Size(320, 240));

// 旋转
Mat rotated;
rotate(img, rotated, ROTATE_90_CLOCKWISE);

保存图像

imwrite("output.jpg", edges);   // 保存结果

编译链接

# 手动编译
g++ main.cpp -o app $(pkg-config --cflags --libs opencv4)

# CMake
# find_package(OpenCV REQUIRED)
# target_link_libraries(app ${OpenCV_LIBS})

进阶功能

  • 人脸检测(Haar/深度学习模型)
  • 视频处理(VideoCapture)
  • 特征匹配(SIFT/ORB)
  • 目标跟踪、图像拼接
  • 深度学习推理(DNN 模块)
💡 提示

OpenCV 的 C++ API 性能最佳(比 Python 快),适合生产级视觉应用;Mat 是核心数据结构(自动内存管理)。

第 1 / 99 节