C++学习笔记——线程、计时器、多维数组、排序

目录

[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#中waitwaitExit的概念。

  • 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_clocksteady_clockhigh_resolution_clock。 |
| 时间点(Time point) | 某个时钟上的一个具体时刻,类型为 std::chrono::time_point<Clock>。 |
| 时长(Duration) | 两个时间点之间的差值,类型为 std::chrono::duration<Rep, Period>,例如 secondsmillisecondsmicroseconds。 |

时钟 特性 适用场景
std::chrono::system_clock 系统范围的实时时钟,可以调整(闰秒、用户修改)。 需要与日历时间交互(如显示时间)。
std::chrono::steady_clock 单调时钟,永远不会被调整,适合测量时间间隔。 性能测量首选(推荐)。
std::chrono::high_resolution_clock 通常是 steady_clocksystem_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 将原始时长转换为指定的单位(如 microsecondsmillisecondsseconds)。

  • .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()) → 按升序<)排列。

  • 适用范围 :支持随机访问迭代器的容器(vectorarraydeque,以及普通数组)。

  • 时间复杂度 :平均复杂度 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;   // 降序
});
相关推荐
无限进步_2 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
克里斯蒂亚诺·罗纳尔达2 小时前
智能体学习16——学习与适应(Learning-and-Adaptation)-深入解读
深度学习·学习·机器学习
charlie1145141912 小时前
嵌入式现代C++工程实践——第10篇:HAL_GPIO_Init —— 把引脚配置告诉芯片的仪式
开发语言·c++·stm32·单片机·c
呼啦啦5612 小时前
C++动态内存管理
c++
ljt27249606612 小时前
Compose笔记(七十六)--拍照预览
笔记·android jetpack
ZC跨境爬虫2 小时前
dankoe视频笔记:如何培养对自己喜欢之事的痴迷感
人工智能·笔记·搜索引擎
paeamecium2 小时前
【PAT甲级真题】- Count PAT‘s (25)
c++·算法·动态规划·pat考试·pat
九英里路2 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串
A.A呐2 小时前
【C++第二十七章】C++类型转换
c++