C++基础复习笔记

一、数组定义

在C++中,数组初始化有多种方式,以下是常见的几种方法:

默认初始化

数组元素未显式初始化时,内置类型(如intfloat)的元素值未定义(垃圾值),类类型调用默认构造函数。

int arr1[5]; // 元素值未定义

聚合初始化(列表初始化)

使用花括号{}直接初始化所有元素。若列表元素少于数组长度,剩余元素默认初始化(内置类型为0)。

int arr2[3] = {1, 2, 3}; // 完全初始化
int arr3[5] = {1, 2};    // 部分初始化,剩余为0

省略数组长度

编译器自动推断数组长度,适用于初始化列表完整的情况。

int arr4[] = {1, 2, 3, 4}; // 编译器推断长度为4

值初始化

使用空花括号{}={},所有元素初始化为0或默认值。

int arr5[3] = {};  // 全部初始化为0
int arr6[3]{};     // C++11统一初始化语法

字符数组的特殊初始化

字符数组可以用字符串字面量初始化,注意预留\0的空间。

char str1[6] = "hello"; // 自动添加\0
char str2[] = "world";  // 编译器推断长度为6

C++11后的扩展初始化

支持省略等号、嵌套列表初始化等。

int arr7[2][3] { {1, 2, 3}, {4, 5, 6} }; // 多维数组初始化

每种方式适用于不同场景,需根据实际需求选择。列表初始化是推荐做法,因其清晰且能避免未定义行为。

二、常量指针和指针

常量指针(Pointer to Constant)

常量指针指向的内容不可修改,但指针本身可以重新指向其他地址。
语法形式:const int* ptrint const* ptr
示例:

const int value = 10;
const int* ptr = &value;
// *ptr = 20;  // 错误:不能修改指向的内容
int another = 30;
ptr = &another;  // 正确:指针本身可以修改

指针常量(Constant Pointer)

指针本身不可修改(必须初始化且不能指向其他地址),但可以通过指针修改指向的内容。
语法形式:int* const ptr
示例:

int value = 10;
int* const ptr = &value;
*ptr = 20;  // 正确:可以修改指向的内容
// ptr = &another;  // 错误:指针本身不能修改

关键差异总结

  • 常量指针:指向的数据是常量,指针可变。
  • 指针常量:指针是常量,指向的数据可变。
  • 双重常量(const int const ptr)*:指针和指向的数据均不可变。

记忆技巧

从右向左读声明:

  • const int* ptr → “ptr is a pointer to a const int”(指向常量的指针)。
  • int* const ptr → “ptr is a const pointer to int”(常量指针)。

三、字符串初始化方式及基本操作

字符串初始化方式

在C++中,字符串可以通过多种方式初始化,以下是常见的几种方法:

使用双引号直接初始化

std::string str = "Hello, World!";

使用构造函数初始化

std::string str1; // 默认构造函数,空字符串
std::string str2(5, 'a'); // 重复字符构造,结果为 "aaaaa"
std::string str3(str2); // 拷贝构造函数

使用字符数组初始化

char charArray[] = {'H', 'e', 'l', 'l', 'o'};
std::string str4(charArray, 5); // 从字符数组构造

使用部分字符串初始化

std::string original = "Hello, World!";
std::string str5(original, 7, 5); // 从原字符串的第7个字符开始取5个字符,结果为 "World"

使用迭代器初始化

std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
std::string str6(vec.begin(), vec.end());

字符串基本操作

访问字符

std::string str = "Hello";
char ch = str[0]; // 获取第一个字符 'H'
char ch_at = str.at(1); // 获取第二个字符 'e',会检查边界

修改字符串

str[0] = 'h'; // 修改第一个字符为 'h'
str.append(" World"); // 追加字符串,结果为 "hello World"
str.push_back('!'); // 追加单个字符,结果为 "hello World!"
str.insert(5, ", C++"); // 在位置5插入字符串,结果为 "hello, C++ World!"
str.erase(5, 5); // 从位置5删除5个字符,结果为 "hello World!"
str.replace(6, 5, "Universe"); // 从位置6替换5个字符,结果为 "hello Universe!"

字符串比较
在C++中,字符串的比较确实遵循字典序(lexicographical order)原则,即逐个字符进行对比。这种比较方式适用于标准库中的std::string类以及C风格的字符串(如char*

std::string str1 = "apple";
std::string str2 = "banana";
bool isEqual = (str1 == str2); // false
bool isLess = (str1 < str2); // true

字符串长度和容量

size_t len = str.length(); // 或 str.size()
bool isEmpty = str.empty();
str.resize(10); // 调整字符串大小
size_t cap = str.capacity(); // 当前分配的存储空间

子串操作

std::string substr = str.substr(6, 8); // 从位置6开始取8个字符
size_t pos = str.find("Universe"); // 查找子串位置

输入输出操作

std::cout << str << std::endl;
std::cin >> str; // 输入字符串(遇到空格停止)
std::getline(std::cin, str); // 输入一行字符串

转换操作

const char* cstr = str.c_str(); // 转换为C风格字符串
int num = std::stoi("123"); // 字符串转整数
double d = std::stod("3.14"); // 字符串转浮点数
std::string numStr = std::to_string(123); // 数值转字符串

四、结构体初始化与内存对齐

结构体初始化方法

在C++中,结构体可以通过多种方式初始化。现代C++提供了灵活的初始化语法,包括聚合初始化、列表初始化以及构造函数初始化。

聚合初始化适用于没有用户定义构造函数、私有或保护非静态数据成员的结构体:

struct Point {int x;int y;
};Point p1 = {10, 20};  // C风格聚合初始化
Point p2{30, 40};     // C++11列表初始化

对于包含构造函数的复杂结构体:

struct Employee {std::string name;int id;Employee(std::string n, int i) : name(std::move(n)), id(i) {}
};Employee e{"John", 1001};

内存对齐原理

结构体内存对齐遵循以下基本原则:

  • 每个成员的偏移量必须是其类型对齐值的整数倍
  • 结构体总大小必须是最大成员对齐值的整数倍
  • 编译器可能插入填充字节(padding)以满足对齐要求

典型对齐值示例:

  • char: 1字节
  • short: 2字节
  • int: 4字节
  • double: 8字节(32位系统可能为4字节)

对齐控制方法

使用alignas指定对齐要求:

struct alignas(16) AlignedStruct {char c;int i;double d;
};

通过#pragma pack修改默认对齐:

#pragma pack(push, 1)
struct PackedStruct {char c;int i;
};
#pragma pack(pop)

C++11标准布局类型要求:

  • 所有非静态成员具有相同的访问控制
  • 没有虚函数或虚基类
  • 非静态成员都是标准布局类型

实际应用示例

计算结构体大小和对齐:

struct Example {char a;      // 偏移0// 3字节填充int b;       // 偏移4double c;    // 偏移8short d;     // 偏移16// 6字节填充(总大小需是8的倍数)
};  // 总大小24字节

跨平台注意事项:

  • 不同平台可能有不同的默认对齐规则
  • 网络传输或文件存储时应使用1字节对齐
  • 关键数据结构建议显式指定对齐方式### 结构体初始化方法

五、常见容器类及适用场景


1. vector(动态数组)

适用场景
需要随机访问、尾部频繁插入/删除,元素连续存储的场景(如数据缓存、动态数据集)

#include <iostream>
#include <vector>int main() {std::vector<int> nums{7, 3, 5};  // 初始化nums.push_back(9);                  // 尾部插入nums.pop_back();                    // 尾部删除std::cout << "元素: ";for (int n : nums) std::cout << n << " ";  // 遍历输出: 7 3 5return 0;
}

2. Array(数组)

适用场景

  • 固定大小的数据存储
    std::array 适用于已知编译时确定大小的场景,例如存储固定数量的配置参数、物理常数或预定义表。其大小在编译时确定,避免了动态内存分配的开销。

    constexpr std::array<double, 3> gravity = {9.7803, 9.832, 9.80665}; // 不同纬度重力加速度
    
  • 需要 STL 接口兼容性
    当算法需要接受 STL 兼容容器(如需要.begin()/.end()迭代器)时,std::array 可以直接替代 C 风格数组,无需额外适配。

    std::array<int, 5> arr = {1,2,3,4,5};
    std::sort(arr.begin(), arr.end()); // 直接使用 STL 算法
    
  • 性能敏感场景
    std::array 数据存储在栈上,访问速度与 C 风格数组相当,适合实时系统或高频调用的代码段。其内存局部性优于动态容器(如 std::vector)。

    // 矩阵乘法中的小块操作
    std::array<std::array<float, 4>, 4> matrix_multiply(const std::array<std::array<float, 4>, 4>& a, const std::array<std::array<float, 4>, 4>& b);
    
  • 作为函数参数或返回值
    std::array 支持值语义,可以安全地作为函数参数或返回值传递,避免指针和手动内存管理。

    std::array<char, 16> generate_id() {std::array<char, 16> id;// ...填充数据return id; // 返回值优化(RVO)确保高效
    }
    
  • 元编程与 constexpr 上下文
    std::array 完全支持 constexpr,可在编译时计算中使用,配合模板元编程时尤其有用。

    template<std::size_t N>
    constexpr std::array<int, N> create_factorial_table() {std::array<int, N> res{};res[0] = 1;for (int i=1; i<N; ++i) res[i] = res[i-1] * i;return res;
    }
    
  • 替代 C 风格数组
    std::array 提供.at()边界检查、size()方法等安全特性,同时保持相同性能,是 C 风格数组的类型安全替代品。

    std::array<int, 10> safe_arr;
    // safe_arr[10] = 5; // 未定义行为
    safe_arr.at(10) = 5; // 抛出 std::out_of_range 异常
    

3. list(双向链表)

适用场景
频繁在任意位置插入/删除,不需要随机访问(如任务队列、撤销操作栈)

#include <iostream>
#include <list>int main() {std::list<std::string> words{"apple", "banana"};auto it = words.begin();it++;                              // 指向第二个元素words.insert(it, "orange");        // 在中间插入words.erase(words.begin());        // 删除头部for (auto& w : words) std::cout << w << " ";         // 输出: orange bananareturn 0;
}

4. map(红黑树字典)

适用场景
需要按键排序的键值对存储(如字典、配置参数)

#include <iostream>
#include <map>int main() {std::map<int, std::string> students;students[101] = "Alice";          // 插入键值对students[102] = "Bob";students.erase(101);               // 按键删除for (auto& [id, name] : students) std::cout << id << ":" << name << " ";  // 输出: 102:Bobreturn 0;
}

5. unordered_map(哈希字典)

适用场景
需要快速查找的键值对,不要求顺序(如缓存系统、词频统计)

#include <iostream>
#include <unordered_map>int main() {std::unordered_map<std::string, int> wordCount;wordCount["hello"] = 1;            // 插入wordCount["world"]++;std::cout << wordCount.at("world"); // 输出: 1return 0;
}

6. deque(双端队列)

适用场景
频繁在头尾插入/删除(如滑动窗口、排队系统)

#include <iostream>
#include <deque>int main() {std::deque<int> dq = {2, 3};dq.push_front(1);                  // 头部插入dq.push_back(4);                   // 尾部插入dq.pop_front();                    // 头部删除for (int n : dq) std::cout << n << " ";         // 输出: 2 3 4return 0;
}

7. set(红黑树集合)

适用场景
需要自动排序的唯一元素集合(如黑名单、词典)

#include <iostream>
#include <set>int main() {std::set<int> uniqueNums = {5, 3, 5, 2}; // 自动去重uniqueNums.insert(1);if (uniqueNums.find(3) != uniqueNums.end()) std::cout << "3存在";          // 输出: 3存在return 0;
}

8. stack(栈)

适用场景
后进先出(LIFO)操作(如函数调用栈、表达式求值)

#include <iostream>
#include <stack>int main() {std::stack<int> s;s.push(10); s.push(20);            // 压栈std::cout << s.top() << " ";       // 输出栈顶: 20s.pop();                           // 弹栈std::cout << s.top();              // 输出: 10return 0;
}

9. queue(队列)

适用场景
先进先出(FIFO)操作(如消息队列、BFS算法)

#include <iostream>
#include <queue>int main() {std::queue<std::string> q;q.push("first");                   // 入队q.push("second");std::cout << q.front() << " ";     // 输出队首: firstq.pop();                           // 出队std::cout << q.front();            // 输出: secondreturn 0;
}

容器选择建议

操作需求推荐容器
快速随机访问vector, array
头尾频繁插入/删除deque
中间频繁插入/删除list
按键快速查找(有序)map, set
按键快速查找(无序)unordered_map
LIFO操作stack
FIFO操作queue

六、左值、右值和移动语义

左值(Lvalue)

左值是指能够明确标识内存位置的表达式,通常可以取地址。左值具有持久性,例如变量、函数返回的左值引用等。

int x = 10; // x是左值  
int* ptr = &x; // 可以取地址  

右值(Rvalue)

右值通常是临时对象或字面量,没有明确的内存位置,不能取地址。右值包括纯右值(如临时对象、字面量)和将亡值(即将被移动的对象)。

int y = 20; // 20是右值  
int z = x + y; // (x + y)的结果是右值  

左值引用与右值引用

左值引用(T&)只能绑定到左值,右值引用(T&&)只能绑定到右值。

int a = 5;  
int& lref = a; // 左值引用  
int&& rref = 10; // 右值引用  

移动语义(Move Semantics)

移动语义通过右值引用避免不必要的拷贝,提升性能。std::move将左值强制转换为右值引用,触发移动构造函数或移动赋值运算符。

class MyClass {  
public:  MyClass() = default;  MyClass(MyClass&& other) noexcept { // 移动构造函数  // 转移资源  }  MyClass& operator=(MyClass&& other) noexcept { // 移动赋值运算符  if (this != &other) {  // 释放当前资源并转移  }  return *this;  }  
};  MyClass obj1;  
MyClass obj2 = std::move(obj1); // 调用移动构造函数  

完美转发(Perfect Forwarding)

std::forward保持参数的值类别(左值或右值),用于模板函数中实现完美转发。

template<typename T>  
void wrapper(T&& arg) {  func(std::forward<T>(arg)); // 保持arg的原始类别  
}  

注意事项

  • 移动后对象应处于有效但未定义状态,通常为空或可析构。
  • 移动构造函数和移动赋值运算符应标记为noexcept,避免异常问题。
  • std::move仅转换类型,不执行移动操作,实际移动由构造函数或赋值运算符完成。

七、面向对象

1. RAII(资源获取即初始化)

核心思想:资源生命周期与对象绑定。对象构造时获取资源,析构时自动释放资源。避免内存泄漏和资源未释放问题。

class FileHandler {
public:FileHandler(const std::string& path) : file_(fopen(path.c_str(), "r")) {}~FileHandler() { if(file_) fclose(file_); } // 析构时自动释放
private:FILE* file_;
};

2. RTTI(运行时类型识别)

动态类型识别机制:

  • dynamic_cast:安全向下转型
    Base* b = new Derived();
    if (Derived* d = dynamic_cast<Derived*>(b)) { /* 成功转换 */ }
    
  • typeid:获取类型信息
    std::cout << typeid(*b).name(); // 输出实际类型名
    

限制:需启用RTTI编译选项(-frtti),且类至少含一个虚函数。

3. 默认构造函数

  • 无参或所有参数有默认值的构造函数
  • 编译器自动生成条件:未显式定义任何构造函数时
  • 重要场景:
    MyClass obj;          // 调用默认构造
    std::vector<MyClass> v(10); // 元素默认构造
    

4. explicit关键字

禁止隐式类型转换

class Timer {
public:explicit Timer(int ms) {} // 阻止隐式转换
};
// Timer t = 1000;  // 错误!必须显式:Timer t(1000);

适用场景:单参数构造函数,避免意外类型转换。

5. 复制语义

成员签名格式作用
复制构造函数ClassName(const ClassName&)深拷贝对象
复制赋值运算符ClassName& operator=(const ClassName&)对象赋值时深拷贝

6. 移动语义(C++11)

成员签名格式作用
移动构造函数ClassName(ClassName&&)转移资源所有权
移动赋值运算符ClassName& operator=(ClassName&&)赋值时转移资源
class Buffer {
public:Buffer(Buffer&& other) : data_(other.data_), size_(other.size_) {other.data_ = nullptr; // 转移后置空原指针}
private:int* data_;size_t size_;
};

关键对比表

特性核心目的典型应用场景
RAII自动化资源管理文件/锁/内存管理
RTTI运行时类型安全操作多态类型检查
explicit防止意外隐式转换单参数构造的包装类
移动语义优化临时对象资源转移容器操作/大对象传递

最佳实践

  1. 优先使用=default/=delete显式控制特殊成员函数
  2. 移动构造函数应标记noexcept
  3. 资源管理类必须禁用复制语义或实现深拷贝### C++面向对象重点知识总结

八、类型转换

C++类型转换概述

C++提供四种类型转换运算符:static_castdynamic_castconst_castreinterpret_cast,用于替代C风格的强制转换,增强安全性和可读性。


static_cast

用于编译时已知的静态类型转换,适用于相关类型间的转换(如基本类型、父子类指针等)。

double d = 3.14;
int i = static_cast<int>(d); // 基本类型转换  class Base {};
class Derived : public Base {};
Base* b = new Derived();
Derived* dd = static_cast<Derived*>(b); // 父子类指针转换(无运行时检查)

特点

  • 不进行运行时类型检查,不安全的上行转换(子类→父类)可能成功,但下行转换(父类→子类)需谨慎。
  • 不能移除constvolatile属性。

dynamic_cast

主要用于多态类型(含虚函数)的安全转换,依赖RTTI(运行时类型信息)。

class Base { virtual void foo() {} };
class Derived : public Base {};  Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 成功  
Base* bb = dynamic_cast<Base*>(d);      // 上行转换总是安全  Base* invalid = new Base();
Derived* fail = dynamic_cast<Derived*>(invalid); // 返回nullptr(指针)或抛出异常(引用)

特点

  • 下行转换时检查类型安全性,失败返回nullptr(指针)或抛出std::bad_cast(引用)。
  • 仅适用于多态类型(类至少有一个虚函数)。

const_cast

用于修改类型的constvolatile属性。

const int x = 10;
int* y = const_cast<int*>(&x); // 移除const  
*y = 20; // 未定义行为(原变量可能为常量存储区)  void print(char* str) { cout << str; }  
const char* msg = "hello";  
print(const_cast<char*>(msg)); // 安全用法:函数参数非修改场景

特点

  • 不能改变基础类型(如intdouble)。
  • 修改原为const的值可能导致未定义行为。

reinterpret_cast

低级别重新解释位模式,用于无关类型间的危险转换。

int* p = new int(65);  
char* ch = reinterpret_cast<char*>(p); // int* → char*  
cout << *ch; // 输出'A'(ASCII 65)  uintptr_t addr = reinterpret_cast<uintptr_t>(p); // 指针转整数

特点

  • 高度依赖平台,可能引发安全问题。
  • 常见用途:指针与整数互转、函数指针类型转换。

旧式C风格转换

C++中仍支持但 discouraged,行为相当于组合使用上述四种转换。

int a = (int)3.14;            // 类似static_cast  
Base* b = (Base*)new Derived(); // 可能类似static_cast或reinterpret_cast  
const int* pc = &a;  
int* pv = (int*)pc;           // 类似const_cast  

风险

  • 缺少明确语义,易引入错误。

最佳实践

  1. 优先使用C++风格转换,明确意图。
  2. dynamic_cast仅用于多态类型,避免性能开销。
  3. const_cast慎用,确保逻辑正确性。
  4. reinterpret_cast仅在底层编程(如硬件操作)中使用。

九、模板

1. 模板基本概念

  • 核心思想:编写与类型无关的通用代码,实现代码复用。
  • 工作原理:编译器在编译期根据具体类型生成特化代码。
  • 优势:避免重复代码,提升类型安全性和灵活性。

2. 函数模板

  • 定义:通过参数化类型实现通用函数。
  • 语法
    template <typename T>
    T max(T a, T b) {return (a > b) ? a : b;
    }
    
  • 使用:编译器自动推导类型或显式指定:
    max(3, 5);       // 推导为 int
    max<double>(3.1, 2); // 显式指定
    

3. 类模板

  • 定义:参数化类成员的类型。
  • 语法
    template <typename T>
    class Stack {
    private:T elements[100];int top;
    public:void push(T const&);T pop();
    };
    
  • 实例化
    Stack<int> intStack;  // 存储 int 类型
    Stack<std::string> strStack; // 存储 string 类型
    

4. 非类型模板参数

  • 用途:传递常量值(如整数、枚举、指针)。
  • 示例
    template <typename T, int size>
    class Array {T data[size];
    };
    Array<double, 10> arr; // 大小为 10 的 double 数组
    

5. 模板特化

  • 全特化:为特定类型提供特殊实现。
    template <>
    class Stack<bool> {  // 针对 bool 类型的特化// 优化存储(如位向量)
    };
    
  • 偏特化:对部分参数特化(仅类模板支持)。
    template <typename T>
    class Stack<T*> {  // 针对指针类型的偏特化// 特殊处理指针
    };
    

6. 可变参数模板(C++11)

  • 用途:处理任意数量、任意类型的参数。
  • 语法
    template <typename... Args>
    void log(Args... args) {// 使用折叠表达式或递归展开参数包
    }
    
  • 示例
    log("Error:", 42, 3.14); // 接受多个参数
    

7. 模板元编程(TMP)

  • 核心:在编译期执行计算,生成高效代码。
  • 示例:编译期阶乘计算:
    template <int N>
    struct Factorial {static const int value = N * Factorial<N-1>::value;
    };
    template <>
    struct Factorial<0> {static const int value = 1;
    };
    int x = Factorial<5>::value; // 编译期计算 120
    

8. 注意事项

  • 编译分离问题:模板定义需在头文件中(链接器限制)。
  • 类型约束:C++20 引入 concepts 明确类型要求。
  • 性能:过度使用模板可能导致编译时间增加。

9. 典型应用场景

  • 标准库容器(vector<T>, map<K, V>
  • 算法(std::sort, std::find
  • 智能指针(shared_ptr<T>
  • 元编程库(如 Boost.MPL)

十、智能指针

核心概念

智能指针是管理动态内存的RAII(资源获取即初始化)对象,自动释放内存,防止内存泄漏。主要类型:

1. std::unique_ptr
  • 独占所有权:同一时间只有一个指针可持有资源
  • 轻量高效:零额外开销(无引用计数)
  • 移动语义:可通过std::move转移所有权
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
std::unique_ptr<int> ptr2 = std::move(ptr1);  // ptr1变为nullptr
2. std::shared_ptr
  • 共享所有权:多个指针共享同一资源
  • 引用计数:通过控制块记录引用数,计数归零时释放资源
  • 线程安全:引用计数原子操作(但资源访问需额外同步)
auto ptr3 = std::make_shared<int>(100);
auto ptr4 = ptr3;  // 引用计数+1
3. std::weak_ptr
  • 观察者模式:不增加引用计数,避免循环引用
  • 需转换为shared_ptr访问资源
std::weak_ptr<int> weak = ptr3;
if (auto temp = weak.lock()) {  // 尝试获取shared_ptr// 使用*temp
}

关键特性对比

特性unique_ptrshared_ptrweak_ptr
所有权独占共享无所有权
复制语义✅(不增计数)
自定义删除器
循环引用风险解决方案
内存开销极小控制块开销控制块开销

最佳实践

1. 优先使用make_xxx
auto p = std::make_unique<MyClass>();  // 避免显式new
  • 异常安全:防止内存泄漏
  • 性能优化:减少内存分配次数
2. 避免循环引用
class Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev;  // 用weak_ptr打破循环
};
3. unique_ptr作为工厂返回值
std::unique_ptr<Base> createObject(int type) {if (type == 1) return std::make_unique<Derived1>();else return std::make_unique<Derived2>();
}
4. weak_ptr检查资源有效性
if (!weak.expired()) {  // 检查资源是否存在auto res = weak.lock();
}

常见错误

1. 误用裸指针初始化
int* raw = new int(10);
std::shared_ptr<int> p1(raw);
std::shared_ptr<int> p2(raw);  // 错误!双重释放
2. 忽略自定义删除器
FILE* f = fopen("file.txt", "r");
std::unique_ptr<FILE, decltype(&fclose)> file(f, &fclose);  // 需指定删除器
3. shared_ptr循环引用
struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };  // 内存泄漏!

智能指针显著提升内存安全性,但需理解所有权语义。建议结合Valgrind/AddressSanitizer工具检测内存问题。

十一、并发与多线程

1. 线程基础

  • 线程创建:使用std::thread

    #include <thread>
    void task() { /* 任务逻辑 */ }
    std::thread t(task);  // 创建线程
    t.join();             // 等待线程结束
    
  • 线程分离detach()使线程在后台运行

    std::thread t(task);
    t.detach();  // 主线程不再管理此线程
    

2. 互斥锁(Mutex)

  • 作用:防止数据竞争,确保临界区互斥访问
  • 类型
    • std::mutex:基础互斥锁
    • std::recursive_mutex:可重入锁
    • std::timed_mutex:支持超时锁定
  • 使用示例
    std::mutex mtx;
    void safe_increment(int& counter) {mtx.lock();++counter;  // 临界区mtx.unlock();
    }
    

3. 智能锁管理

  • lock_guard:RAII风格自动锁管理
    std::mutex mtx;
    void safe_func() {std::lock_guard<std::mutex> lock(mtx); // 构造时加锁,析构时解锁// 临界区操作
    }
    
  • unique_lock:更灵活的锁(支持延迟锁定、转移所有权)
    std::mutex mtx;
    void flexible_func() {std::unique_lock<std::mutex> lock(mtx, std::defer_lock);lock.lock();  // 手动加锁// 操作...lock.unlock(); // 可手动解锁
    }
    

4. 条件变量(Condition Variable)

  • 作用:线程间同步,等待特定条件成立
  • 典型生产者-消费者模式
    std::mutex mtx;
    std::condition_variable cv;
    std::queue<int> data_queue;void producer() {while (true) {std::unique_lock<std::mutex> lock(mtx);data_queue.push(42);cv.notify_one();  // 通知消费者}
    }void consumer() {while (true) {std::unique_lock<std::mutex> lock(mtx);cv.wait(lock, []{ return !data_queue.empty(); }); // 等待非空条件int data = data_queue.front();data_queue.pop();}
    }
    

5. 原子操作(Atomic)

  • 作用:无锁线程安全操作
  • 示例
    #include <atomic>
    std::atomic<int> counter(0);void increment() {counter.fetch_add(1, std::memory_order_relaxed);
    }
    
  • 内存序
    • memory_order_relaxed:宽松顺序
    • memory_order_seq_cst:严格顺序(默认)

6. 异步任务(Async/Future)

  • std::async:异步执行函数

  • std::future:获取异步结果

    #include <future>
    int compute() { return 42; }std::future<int> fut = std::async(compute);
    int result = fut.get();  // 阻塞获取结果
    
  • std::packaged_task:封装可调用对象(如函数、Lambda),执行结果与std::future绑定

    #include <future>
    #include <iostream>
    #include <thread>int main() {// 封装Lambda任务(计算平方)std::packaged_task<int(int)> task([](int x) { return x * x; });// 获取关联的futurestd::future<int> result = task.get_future();// 在独立线程执行任务std::thread t(std::move(task), 5);t.detach();// 获取结果(阻塞直到完成)std::cout << "Result: " << result.get() << std::endl;  // 输出25
    }
  • std::promise:显式设置异步操作的结果值或异常

    #include <future>
    #include <thread>void compute(std::promise<int> prom) {try {int res = 42; // 复杂计算prom.set_value(res); // 设置结果} catch(...) {prom.set_exception(std::current_exception()); // 传递异常}
    }int main() {std::promise<int> prom;std::future<int> fut = prom.get_future();std::thread t(compute, std::move(prom));// 等待结果std::cout << "Result: " << fut.get() << std::endl; // 输出42t.join();
    }
    

7. 线程安全设计原则

  • 避免共享数据:使用线程局部存储(thread_local
    thread_local int local_var = 0;  // 每个线程独立副本
    
  • 锁粒度最小化:减少临界区范围
  • 预防死锁
    • 固定锁获取顺序
    • 使用std::lock()同时锁定多个互斥量
      std::lock(mtx1, mtx2);  // 原子化锁定
      std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
      std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
      

8. 性能优化

  • 无锁数据结构:如std::atomic_flag实现自旋锁
  • 缓存对齐:避免伪共享(False Sharing)
    struct alignas(64) CacheLineAligned { int data;  // 单独缓存行
    };
    

问题

怎么实现一个内存池?

#pragma once
class MyMemoPool {
public:static void* operator new(size_t size);static void operator delete(void* pHead);static int m_iCount;        // 分配计数统计,每new一次+1static int m_iMallocCount;  // 统计malloc次数,每malloc一次+1
private:MyMemoPool* next;static MyMemoPool* m_FreePos; // 总是指向一块可以分配出去的内存首地址static int m_sTrunkCount;     // 一次分配多少倍该类的内存
};
#include "MyMemoPool.h"
#include <memory>#define MYMEMPOOL 1void* MyMemoPool::operator new(size_t size) {
#ifndef MYMEMPOOLMyMemoPool* pPoint = (MyMemoPool*)malloc(size);return pPoint;
#endif MyMemoPool* tmpLink;if (m_FreePos == nullptr) {// 为空,我们要申请内存,申请很大一块内存size_t realsize = m_sTrunkCount * size;m_FreePos = reinterpret_cast<MyMemoPool*>(new char[realsize]);tmpLink = m_FreePos;// 把分配的这一大块内存链接起来,供后续使用for (; tmpLink != &m_FreePos[m_sTrunkCount - 1]; ++tmpLink) {tmpLink->next = tmpLink + 1;}tmpLink->next = nullptr;++m_iMallocCount;}tmpLink = m_FreePos;m_FreePos = m_FreePos->next;++m_iCount;return tmpLink;
}void MyMemoPool::operator delete(void* pHead) {
#ifndef MYMEMPOOLfree(pHead);return;
#endif // MYMEMPOOL(static_cast<MyMemoPool*>(pHead))->next = m_FreePos;m_FreePos = static_cast<MyMemoPool*>(pHead);
}int MyMemoPool::m_iCount = 0;
int MyMemoPool::m_iMallocCount = 0;
MyMemoPool* MyMemoPool::m_FreePos = nullptr;
int MyMemoPool::m_sTrunkCount = 350;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/bicheng/87978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机和PC远控安全深度测评:TeamViewer/ToDesk/向日葵安全防线对比

声明&#xff1a;本测试报告系作者基于个人兴趣及使用场景开展的非专业测评&#xff0c;测试过程中所涉及的方法、数据及结论均为个人观点&#xff0c;不代表任何官方立场或行业标准。 一、引言 当下远程控制技术已深度融入大众的工作与生活&#xff0c;无论是上班族在家操…

Windows 11的开始菜单调整为左下角布局

1.桌面右键个性化 2.个性化中任务栏 3.任务栏选择任务栏行为 4.任务栏行为中 任务栏对齐方式选择靠左即可

Go语言项目工程化 — 常见开发工具与 CI/CD 支持

在Go语言的项目工程化实践中&#xff0c;常见开发工具与 CI/CD 支持是保障团队协作、高效交付与项目质量的关键。以下是第 68 章的详细内容。一、开发辅助工具Go语言生态为开发者提供了丰富的工具&#xff0c;以提高代码质量与开发效率。1. 格式化与静态检查工具说明gofmt标准格…

OpenCV人脸分析------绘制面部关键点函数drawFacemarks()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于在图像上绘制面部关键点&#xff08;facial landmarks&#xff09;&#xff0c;例如使用 FacemarkLBF, FacemarkKazemi 等算法检测到的…

Linux内核ext4 extent:解决大文件存储难题的关键

在Linux 操作系统的庞大生态中&#xff0c;文件系统犹如一座城市的基础设施&#xff0c;支撑着数据的有序存储与高效访问。而 ext4 文件系统&#xff0c;作为 Linux 文件系统家族中的重要一员&#xff0c;自诞生起便凭借诸多先进特性备受瞩目。其中&#xff0c;extent 机制堪称…

reactnative页面适配UI设计尺寸px转dp的完美解决方案px2dp精要篇

你的 px2dp 函数基本思路是正确的&#xff0c;但可以进一步优化以确保更精确的适配。以下是改进后的完美精确方案&#xff1a; 完美精确的适配方案 import { Dimensions, PixelRatio, Platform, ScaledSize } from react-native;// 获取屏幕尺寸&#xff08;考虑横竖屏&#…

【世纪龙科技】汽车钣金虚拟仿真教学实训软件

在汽车后市场人才紧缺的当下&#xff0c;职业院校汽车钣金教学却长期面临“三难困境”&#xff1a;实训设备昂贵且损耗快、学生实操机会稀缺、教学评价依赖主观经验。江苏世纪龙科技公司以十余年汽车教育数字化积淀为基石&#xff0c;推出《汽车钣金教学软件》&#xff0c;通过…

Fiddler中文版抓包工具在后端API调试与Mock中的巧用

在现代开发中&#xff0c;前后端往往分属不同小组甚至不同公司&#xff0c;接口联调变得至关重要。尤其是在多团队合作、后端接口尚未完成或频繁变动的项目中&#xff0c;前端开发进度容易被阻碍。此时&#xff0c;通过灵活运用 Fiddler抓包工具&#xff0c;前端可以在后端接口…

基于 Flask框架开发的轻量级招聘网站

简单的招聘网站示例 这是一个基于 Flask 框架开发的轻量级招聘网站示例&#xff0c;采用 Jinja2 模板引擎和 Bootstrap 前端框架&#xff0c;模仿 拉勾网 风格&#xff0c;实现了招聘平台的核心功能。系统支持 个人用户 和 企业用户 两种角色&#xff0c;个人用户可以浏览职位、…

2025 年使用大模型进行软件工程:现实检验

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

如何使用单例模式保证全局唯一实例(复杂版本)

/// <summary> /// 登录管理类&#xff08;单例模式&#xff09;&#xff0c;负责用户登录、注销及用户信息管理 /// </summary> public class LoginMananger {// 用于线程同步的锁对象static object _lockObj new object();// 单例实例&#xff08;延迟初始化&am…

瑞斯拜考研词汇课笔记

学习视频链接&#xff1a;瑞斯拜考研词汇系统课-外刊50篇- 第一讲_哔哩哔哩_bilibili Text 1 1.气候危机让普通人经历了额外六周的高温天气。 The climate crisis caused the average person to experience six extra weeks of hot days. 2.碳排放是全球变暖的重要原因之一。 C…

SqlServer安装后JDBC连接失败——TCP/IP

朋友公司接了个项目&#xff0c;甲方BaBa用的数据库是SqlServer 2022的Express版本&#xff0c;朋友让我帮忙验证下环境有没有什么问题&#xff0c;软件开发用的框架还是比较老的&#xff0c;spring的xml方式配置&#xff0c;用的c3p0的数据库连接池&#xff0c;启动项目连接池…

如何解决pip安装报错ModuleNotFoundError: No module named ‘datetime’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘datetime’问题 摘要 在日常Python开发中&#xff0c;我们常常需要通过pip install来安装第三方包&#xff0c;但有时会在PyCharm的控制台里遇到奇怪的ModuleN…

Windows 10 2016 长期服务版

系统介绍 Windows 10 2016 长期服务版。专为需要高度稳定性和最小功能变更的环境设计。它不仅适合专业领域&#xff0c;也是办公环境的理想选择。 系统特点 一、极致的稳定性 精简的系统组件&#xff1a;移除许多现代应用&#xff0c;只保留基础功能。 无强制功能更新&…

基于springboot的文件上传系统:重新定义大文件传输的可靠性边界

一、文件分块上传解析1、为什么传统文件上传已经无法满足现代需求&#xff1f;在云原生时代&#xff0c;文件上传不再是简单的"选择文件-点击上传"的过程。随着视频、设计图、数据集等大文件的普及&#xff0c;传统的单文件上传方式面临着诸多挑战&#xff1a;网络不…

系统学习Python——并发模型和异步编程:进程、线程和GIL

分类目录&#xff1a;《系统学习Python》总目录 在文章《并发模型和异步编程&#xff1a;基础知识》我们简单介绍了Python中的进程、线程和协程。本文就着重介绍Python中的进程、线程和GIL的关系。 Python解释器的每个实例都是一个进程。使用multiprocessing或concurrent.futu…

【playwright篇】教程(十七)[html元素知识]

1 html中&#xff0c;button元素中的aria-describedby"tooltip-r1k"属性&#xff0c;主要用来做什么&#xff1f;在 HTML 中&#xff0c;button 元素中的 aria-describedby"tooltip-r1k" 属性主要用于提升网页的可访问性&#xff08;Accessibility&#xf…

Python: 正则表达式

正则表达式是处理文本数据的强大工具&#xff0c;Python通过re模块提供了完整的正则表达式功能。本文将详细介绍Python正则表达式的使用方法&#xff0c;包括基础语法、高级技巧和re模块API的详细解析。一、正则表达式基础1.1 什么是正则表达式正则表达式(Regular Expression)是…

pytest合并allure报告解决方案

背景 在执行自动化测试的过程中&#xff0c;为了实现自动化的高通过率&#xff0c;可能会反复的重试&#xff0c;直至大多数甚至全部用例执行通过&#xff0c;以此来需要人为分析的用例量&#xff0c;减少人力投入&#xff0c;提高执行效率&#xff1b; 在用例少或者资源消耗小…