目录
[1. 线程](#1. 线程)
[1.1 基本概念](#1.1 基本概念)
[1.2 线程管理 (join() vs detach())](#1.2 线程管理 (join() vs detach()))
[2. 计时器](#2. 计时器)
[3. 多维数组](#3. 多维数组)
[3.1 什么是多维数组?](#3.1 什么是多维数组?)
[3.2 静态多维数组(编译时维度已知)](#3.2 静态多维数组(编译时维度已知))
[3.3 数组的数组 vs 指针的指针](#3.3 数组的数组 vs 指针的指针)
[3.4 动态分配多维数组](#3.4 动态分配多维数组)
[3.5 常见陷阱与注意事项](#3.5 常见陷阱与注意事项)
[4. std::sort](#4. std::sort)
[4.1 基本概念](#4.1 基本概念)
[4.2 自定义排序规则](#4.2 自定义排序规则)
1. 线程
1.1 基本概念
std::thread是C++并发编程的核心,它将操作系统的线程能力抽象为与平台无关的API,并提供了强大的工具来管理、同步和测量线程。
-
头文件 :使用线程必须包含
#include <thread>。 -
启动线程 :通过在创建
std::thread对象时,传入一个函数指针 (即函数名,不带括号())来指定线程的入口函数-1-10。你也可以向线程函数传递参数,只需在函数名后依次列出即可。 -
获取线程ID :可以通过
std::this_thread::get_id()获取当前线程的唯一标识符。 -
除了函数指针,
std::thread的构造函数也接受任何可调用(Callable) 类型,包括Lambda表达式和带有operator()的类对象。 -
线程对象一经创建,新线程便会立即开始执行 其入口函数,不存在单独的
start()方法。因此,构造函数的设计必须保证在对象构造完成后线程即处于可运行状态。
1.2 线程管理 (join() vs detach())
线程创建后,需要决定如何管理它的生命周期:
-
join()- 等待 :调用worker.join()会阻塞当前线程,直到worker线程执行完毕。如果主线程不等待,直接结束,程序可能会在worker线程还在运行时被强制终止。join的确切含义是"会合",即主线程在此处与worker线程会合,等待其执行完毕。这类似于C#中wait或waitExit的概念。 -
detach()- 分离 :调用worker.detach()会让worker线程在后台独立运行,与主线程完全分离。一旦detach,该std::thread对象就不再代表任何线程,你再也无法控制或等待它。
cpp
#include <iostream>
#include <thread>
#include <chrono>
static bool s_Finished = false; // 全局标志
void DoWork() {
using namespace std::literals::chrono_literals;
while (!s_Finished) {
std::cout << "Working...\n";
std::this_thread::sleep_for(1s); // 让线程暂停1秒
}
}
int main() {
std::thread worker(DoWork);
std::cin.get(); // 等待用户输入
s_Finished = true; // 通知worker线程退出
worker.join(); // 等待worker线程结束
std::cin.get(); // 等待程序结束
}
main线程等待用户输入,并通过一个共享的bool标志 s_Finished 来控制worker线程的运行状态。
std::this_thread::sleep_for(1s)让线程在指定时间内主动休眠,避免空转消耗CPU。
2. 计时器
|---------------------|-------------------------------------------------------------------------------------------------|
| 时钟(Clock) | 提供当前时间点的来源,例如 system_clock、steady_clock、high_resolution_clock。 |
| 时间点(Time point) | 某个时钟上的一个具体时刻,类型为 std::chrono::time_point<Clock>。 |
| 时长(Duration) | 两个时间点之间的差值,类型为 std::chrono::duration<Rep, Period>,例如 seconds、milliseconds、microseconds。 |
| 时钟 | 特性 | 适用场景 |
|---|---|---|
std::chrono::system_clock |
系统范围的实时时钟,可以调整(闰秒、用户修改)。 | 需要与日历时间交互(如显示时间)。 |
std::chrono::steady_clock |
单调时钟,永远不会被调整,适合测量时间间隔。 | 性能测量首选(推荐)。 |
std::chrono::high_resolution_clock |
通常是 steady_clock 或 system_clock 的别名,提供最高精度。 |
需要极短时间测量时使用。 |
基本用法:测量代码块耗时
cpp
#include <iostream>
#include <chrono>
int main() {
auto start = std::chrono::steady_clock::now();
// 要测量的代码
for (int i = 0; i < 1000000; ++i);
auto end = std::chrono::steady_clock::now();
// 计算时长(转换为微秒)
auto diff = end - start;
auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(diff);
std::cout << "耗时: " << microseconds.count() << " 微秒\n";
return 0;
}
-
now()返回当前时间点。 -
end - start得到一个duration对象。 -
duration_cast将原始时长转换为指定的单位(如microseconds、milliseconds、seconds)。 -
.count()返回该时长的数值。
创建计时器类,可以在作用域内更方便地进行性能评估:
cpp
#include <iostream>
#include <chrono>
class Timer {
public:
Timer() : m_start(std::chrono::steady_clock::now()) {}
~Timer() {
auto end = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - m_start);
std::cout << "耗时: " << duration.count() << " 微秒\n";
}
private:
std::chrono::steady_clock::time_point m_start;
};
// 使用示例
int main() {
Timer t; // 构造函数开始计时
// 要测量的代码...
return 0; // 析构时自动输出耗时
}
3. 多维数组
3.1 什么是多维数组?
-
本质 :多维数组并非真正"多维",而是元素本身又是数组的一维数组。
-
例如 :
int arr[3][2]可以看作一个长度为 3 的数组,它的每个元素是一个长度为 2 的int数组。 -
维度:C++ 支持任意维度的数组,但最常用的是二维(表格状数据)。
3.2 静态多维数组(编译时维度已知)
声明与初始化
cpp
// 3 行 2 列的二维数组
int matrix[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
// 也可省略内层大括号(按行填充)
int matrix2[3][2] = {1, 2, 3, 4, 5, 6};
// 部分初始化(未指定的元素置零)
int matrix3[3][2] = {{1}, {3}}; // 第一行 {1,0},第二行 {3,0},第三行 {0,0}
访问元素
cpp
int value = matrix[1][0]; // 第二行第一列 → 3
matrix[2][1] = 99; // 修改第三行第二列
静态多维数组内存布局
-
行主序(Row-major) :所有元素在内存中是连续存储的,先存完第一行所有列,再存第二行,依此类推。
-
对于
int arr[3][2],内存顺序为:
arr[0][0]→arr[0][1]→arr[1][0]→arr[1][1]→arr[2][0]→arr[2][1]。
理解这一点对优化缓存访问非常重要------按行遍历比按列遍历快得多。
3.3 数组的数组 vs 指针的指针
| 特性 | int arr[3][2] |
int** ptr |
|---|---|---|
| 内存布局 | 单一连续内存块 | 多个不连续内存块(需要两次解引用) |
| 类型退化(传参时) | int (*arr)[2](指向数组的指针) |
int** |
| 可动态分配行大小 | 否(每行列数固定) | 是(每行可不同长度) |
3.4 动态分配多维数组
方法一:使用 new
cpp
// 动态分配 3 行 2 列
int** matrix = new int*[3];
for (int i = 0; i < 3; ++i)
matrix[i] = new int[2];
// 使用...
matrix[0][0] = 42;
// 释放
for (int i = 0; i < 3; ++i)
delete[] matrix[i];
delete[] matrix;
缺点:多次内存分配、内存不连续、需要手动管理。
方法二:使用一维数组模拟
cpp
int rows = 3, cols = 2;
int* matrix = new int[rows * cols];
// 访问元素 (i, j) -> index = i * cols + j
matrix[i * cols + j] = value;
delete[] matrix;
- 优点:单次内存分配、连续内存、缓存友好。
方法三:使用 std::vector 的嵌套
cpp
#include <vector>
std::vector<std::vector<int>> matrix(3, std::vector<int>(2, 0));
matrix[1][1] = 5;
- 方便安全 ,但每行的
vector内存独立,不保证整体连续。
3.5 常见陷阱与注意事项
- 传参时类型退化:将多维数组传递给函数时,第一维的大小会被忽略(退化为指针),必须显式指定其他维度的大小或使用模板。
cpp
void print(int arr[][2], int rows); // 正确:第二维必须给出
void print(int arr[3][2]); // 3 实际上会被忽略
- 使用模板保留维度:
cpp
template<size_t Rows, size_t Cols>
void print(int (&arr)[Rows][Cols]) { ... }
- 不要用
int**接收静态二维数组:类型不匹配,编译错误或未定义行为。
| 特性 | 静态数组(含局部/全局) | 动态数组(堆) |
|---|---|---|
| 分配时机 | 编译时确定大小,运行时自动分配(栈或静态区) | 运行时按需显式分配(new/malloc) |
| 大小是否可变 | 必须为编译时常量 (如 int arr[10][20]) |
可以是运行时变量 (如 int* p = new int[n]) |
| 生命周期 | 受作用域控制(局部:离开作用域自动销毁;全局:程序结束销毁) | 由程序员控制(必须手动 delete) |
| 内存位置 | 局部→栈,全局/静态→静态存储区 | 堆 |
| 分配/释放开销 | 极低(移动栈指针或静态初始化) | 较高(涉及堆管理算法,可能系统调用) |
| 错误风险 | 栈溢出(大数组) | 内存泄漏、悬空指针 |
4. std::sort
4.1 基本概念
-
头文件 :
#include <algorithm> -
默认排序 :
std::sort(container.begin(), container.end())→ 按升序 (<)排列。 -
适用范围 :支持随机访问迭代器的容器(
vector、array、deque,以及普通数组)。 -
时间复杂度 :平均复杂度 O(n log n) ,最坏 O(n log n), 通常是快速排序算法。
cpp
std::vector<int> v = {5, 2, 8, 1, 9};
std::sort(v.begin(), v.end()); // 结果:1, 2, 5, 8, 9
4.2 自定义排序规则
使用函数指针
cpp
bool descending(int a, int b) {
return a > b; // 降序
}
std::sort(v.begin(), v.end(), descending);
使用重载了 operator()的对象
cpp
struct Desc {
bool operator()(int a, int b) const {
return a > b;
}
};
std::sort(v.begin(), v.end(), Desc());
使用 Lambda 表达式
cpp
std::sort(v.begin(), v.end(), [](int a, int b) {
return a > b; // 降序
});