当前位置: 首页 > news >正文

C++标准库大全(STL)

C++标准库大全(STL)


1. 容器(Containers)
*问题类型:

  • 序列容器(std::vector, std::deque, std::list, std::forward_list, std::array, std::string):

    • 各自的特点、底层实现、优缺点和适用场景?

      容器特点底层实现优点缺点适用场景
      std::vector动态数组,支持快速随机访问连续内存 + 三指针(数据头/尾/容量尾)随机访问 O(1);缓存友好;尾部操作高效中间插入/删除 O(n);扩容需数据拷贝随机访问为主;尾部增删(如数据缓存)
      std::deque双端队列,头尾插入高效分段连续数组 + 中央映射表头尾插入/删除 O(1);支持随机访问 O(1)中间插入 O(n);内存碎片化;访问速度略慢于 vector队列/栈需双端操作(如任务调度)
      std::list双向链表,任意位置插入高效双向链表节点(prev/data/next)任意位置插入/删除 O(1);无扩容开销随机访问 O(n);内存占用高;缓存不友好频繁中间增删(如 LRU 缓存)
      std::forward_list单向链表,内存更省单向链表节点(data/next)内存开销极低(单指针);插入/删除 O(1)仅单向遍历;无反向迭代器;操作需前驱节点内存敏感场景(如嵌入式系统链表)
      std::array固定大小数组,编译时确定尺寸栈上分配的 C 风格数组封装零内存开销;随机访问 O(1);无动态分配成本大小不可变;无动态扩容替代 C 数组(如固定尺寸矩阵运算)
      std::string动态字符串,支持丰富操作类 vector + SSO 优化自动内存管理;内置操作(find/replace 等)性能略低于 char*;SSO 有大小限制字符串处理(如文本解析/日志拼接)
      1. vector 扩容

        • 扩容因子(1.5/2倍)平衡 均摊 O(1) 时间成本内存碎片问题
          • 1.5 倍:释放的内存块可被后续扩容重用(例:释放 4MB 后,1.5 倍扩容 6MB 可重用该内存)。
          • 2 倍:内存重用困难(例:4MB → 8MB → 16MB,释放的 4MB+8MB 无法用于 16MB)。
      2. deque 随机访问

        • 计算过程:元素位置 = 段起始地址 + 段内偏移,比 vector 多一次地址跳转。
      3. SSO(短字符串优化)

        • string 对短字符串(通常 ≤15 字符)直接在栈存储,避免堆分配:

          std::string s = "Short";  // 栈存储  
          std::string l = "Long string over 15 chars";  // 堆存储  
          
      4. 链表选择指南

        • 需双向遍历 → list
        • 仅需单向遍历 + 省内存 → forward_list
        • 高频中间插入 → 优先链表;高频随机访问 → 优先 vector/deque
      5. 性能临界场景

        • 超高频操作(如金融交易):优先 array/vector(缓存友好)
        • 内存敏感(如嵌入式):优先 forward_list/array
    • vector的扩容机制?为什么是 2 倍或 1.5 倍?

      • 扩容机制

        • 当插入元素超过当前容量时,分配新内存(原容量的 n 倍),拷贝旧数据到新空间,释放旧内存。
      • 扩容因子(1.5或2倍)的原因

        1. 均摊时间复杂度

          • 扩容因子需保证插入操作的均摊时间复杂度为 O(1)。数学证明:当因子 k > 1 时,均摊成本为 O(1)
        2. 内存重用

          • 1.5倍:旧内存块释放后,后续扩容可能重用(新旧内存块大小无重叠)。

            例如:释放 size=M 后,新申请 1.5M,后续扩容可能使用之前释放的 M 内存。

          • 2倍:释放的内存块总和(M + 2M + 4M + ...)无法被后续更大的块重用(如 8M)。

        3. 折中策略

          • 过小(如1.1倍):扩容频繁,拷贝开销大。
          • 过大(如3倍):内存浪费严重。
        • 主流实现
          • GCC:2倍扩容;
          • VS:1.5倍扩容。
    • vectorlist的区别?

      特性vectorlist
      底层结构动态数组(连续内存)双向链表(非连续内存)
      随机访问O(1)(支持下标访问)O(n)(需遍历)
      头部插入/删除O(n)(移动元素)O(1)
      尾部插入/删除均摊 O(1)O(1)
      中间插入/删除O(n)(移动元素)O(1)(定位后操作)
      内存占用较少(仅需存储数据)较高(每个节点含两个指针)
      缓存友好性高(连续内存)低(内存碎片化)
      扩容开销需重新分配内存和拷贝无扩容(动态分配节点)
    • stringchar*的区别?

      特性stringchar*
      内存管理自动分配/释放(RAII)手动管理(malloc/free
      安全性防越界(自动扩容)易缓冲区溢出/内存泄漏
      功能扩展丰富成员函数(findappend等)依赖C库函数(strcpystrcat
      结尾标识可包含 \0(长度独立管理)\0 结尾
      性能开销略高(封装成本)极低(直接操作内存)
      兼容性C++专用兼容C/C++

      关键结论

      • 优先使用 string:安全、便捷,适合大多数场景。
      • 使用 char*:需兼容C或极限性能优化(如高频交易系统)。
  • 关联容器(std::set, std::multiset, , std::map, std::multimap):

    • 各自的特点、底层实现(红黑树?)、优缺点和适用场景

      容器特点底层实现优点缺点适用场景
      std::set唯一键集合,自动排序红黑树有序遍历;查找/插入 O(log n)内存占用高(每个节点额外信息)需有序唯一键(如字典)
      std::multiset键可重复,自动排序红黑树支持重复键;有序set需有序但键可重复(如成绩排名)
      std::map键值对(键唯一),按键排序红黑树按键有序;范围查询高效set键值映射需有序(如学生ID→信息)
      std::multimap键值对(键可重复),按键排序红黑树支持重复键;有序set一键多值(如作者→著作列表)
      底层实现核心:红黑树
      • 特性
        • 自平衡二叉搜索树(保证树高 ≈ log n)。
        • 节点含颜色标记(红/黑),通过旋转和变色维持平衡。
      • 操作复杂度
        • 查找、插入、删除:O(log n)
      • 额外开销
        • 每个节点存储父/子指针、颜色标记(内存占用高于哈希表)。
    • mapunordered_map的区别?

      特性std::mapstd::unordered_map
      底层实现红黑树(平衡二叉搜索树)哈希表(桶数组 + 链表/红黑树)
      元素顺序按键有序(升序)无序(取决于哈希函数)
      查找复杂度O(log n)平均 O(1),最坏 O(n)
      内存占用较低(树结构)较高(预分配桶 + 链表指针)
      键类型要求需支持 < 或自定义比较器需支持哈希函数和 == 比较
      适用场景需有序访问/范围查询只需快速查找(如缓存/计数器)
      关键区别:
      1. 顺序需求
        • map:保证顺序(遍历时按键升序)。
        • unordered_map:元素顺序不可控(依赖哈希函数)。
      2. 性能权衡
        • unordered_map:平均 O(1) 查找,但哈希冲突时退化(最坏 O(n))。
        • map:稳定 O(log n),无性能波动。
      3. 内存敏感场景
        • 优先 map(无预分配桶开销)。
      4. C++11 优化
        • unordered_map 桶内改用红黑树(如 GCC),最坏复杂度优化至 O(log n)
  • 如何自定义容器的比较函数(std::less)?

    1. 关联容器(set/map等)

    在模板参数中指定自定义比较类型:

    // 方法1:函数对象(推荐)  
    struct CustomCompare {  bool operator()(const T& a, const T& b) const {  return /* 自定义逻辑 */;  // 例:a.salary > b.salary  }  
    };  
    std::set<T, CustomCompare> mySet;  // 方法2:Lambda(C++11+)  
    auto comp = [](const T& a, const T& b) { /* ... */ };  
    std::set<T, decltype(comp)> mySet(comp);  // 需传递Lambda对象  
    
    1. 序列容器(vector/list等)

    在排序操作时传入比较函数:

    std::vector<T> vec;  
    // 方法1:Lambda表达式  
    std::sort(vec.begin(), vec.end(), [](const T& a, const T& b) {  return a.id < b.id;  // 按ID升序  
    });  // 方法2:函数指针  
    bool compareFunc(const T& a, const T& b) { /* ... */ }  
    std::sort(vec.begin(), vec.end(), compareFunc);  
    
    1. 优先队列(priority_queue

    在模板参数中指定比较类型:

    // 小顶堆示例  
    struct Greater {  bool operator()(int a, int b) const {  return a > b;  // 小值优先  }  
    };  
    std::priority_queue<int, std::vector<int>, Greater> minHeap;  
    

    关键规则

    1. 严格弱序要求
      • 需满足:

        !comp(a, a)               // 非自反  
        comp(a, b) => !comp(b, a)  // 非对称  
        comp(a, b) && comp(b, c) => comp(a, c)  // 传递性  
        
      • 违反示例(错误):

        [](int a, int b) { return a <= b; }  // 包含相等,违反非自反性  
        
    2. 比较对象类型
      • 函数对象:需重载 operator() 且为 const 成员函数
      • Lambda:推荐捕获列表为空(无状态)
    3. 性能影响
      • 关联容器:比较函数复杂度需为 O(1),否则树操作退化为 O(n log n)
      • 排序操作:比较函数应轻量(高频调用)
  • 无序关联容器(std::unordered_set, std::unordered_multiset, std::unordered_map, std::unordered_multimap):

    • 各自的特点、底层实现(哈希表)、优缺点和适用场景?

      容器特点底层实现优点缺点适用场景
      std::unordered_set唯一键集合,无序存储哈希表(桶+链表/红黑树)平均 O(1) 查找/插入最坏 O(n);内存占用高快速去重(如URL黑名单)
      std::unordered_multiset键可重复,无序存储同上支持重复键unordered_set词频统计(如单词计数)
      std::unordered_map键值对(键唯一),无序存储同上平均 O(1) 键值访问unordered_set高速键值查询(如缓存)
      std::unordered_multimap键值对(键可重复),无序存储同上支持一键多值unordered_set多值映射(如电话簿)

      底层实现核心

      • 哈希表 = 桶数组(连续内存) + 冲突解决结构(链表/红黑树)
      • C++11 优化:桶内元素超阈值(如 GCC 为 8)时,链表转红黑树(最坏复杂度优化至 O(log n)
    • 哈希冲突的解决方法?

      方法原理实现示例特点
      链地址法桶内挂链表存储冲突元素bucket[i] = head→a→b→null简单通用;C++标准库默认方案
      开放寻址法线性探测:冲突时找下一个空桶bucket[(hash+1)%size]缓存友好;需负载因子控制
      罗宾汉哈希冲突时比较探测距离,抢占更远槽位复杂优化减少最长探测距离

      关键参数

      • 负载因子 = 元素数 / 桶数(默认 1.0)
      • 扩容触发:负载因子 > max_load_factor() 时,桶数翻倍并重哈希
    • 如何自定义容器的比较函数?

      需定义 两个函数对象

      1. 哈希函数(计算键的哈希值)
      2. 键相等比较(判断键是否相同)
      // 示例:自定义Point类型作为键  
      struct Point { int x; int y; };  // 1. 自定义哈希函数  
      struct PointHash {  size_t operator()(const Point& p) const {  return std::hash<int>()(p.x) ^ (std::hash<int>()(p.y) << 1);  }  
      };  // 2. 自定义键相等比较  
      struct PointEqual {  bool operator()(const Point& a, const Point& b) const {  return a.x == b.x && a.y == b.y;  }  
      };  // 定义容器  
      std::unordered_map<Point, std::string, PointHash, PointEqual> pointMap;  
      

      关键规则

      • 哈希一致性:若 a == b,则 hash(a) == hash(b)

      • 性能要求:哈希函数需高效(O(1) 复杂度)

      • 特化 std::hash(可选):

        namespace std {  
        template<> struct hash<Point> { /* ... */ };  
        }    
        
  • 容器配合(std::stack, std::queue, , std::priority_queue):

    • 各自的特点、底层实现、优缺点和适用场景?

      适配器特点默认底层容器核心操作优点缺点适用场景
      std::stack后进先出(LIFO)std::dequepush(), pop(), top()操作简单高效(O(1))只能访问顶部元素函数调用栈/撤销操作/括号匹配
      std::queue先进先出(FIFO)std::dequepush(), pop(), front()头尾操作高效(O(1))只能访问两端元素消息队列/缓冲区/广度优先搜索
      std::priority_queue优先级队列(最大堆)std::vectorpush(), pop(), top()高效获取极值(O(1))插入慢(O(log n))任务调度/事件处理/Dijkstra算法

      底层实现详解

      1. stack & queue 默认容器选择
        • 使用 deque(双端队列)原因:

          • 两端插入/删除均为 O(1)

          • 内存自动扩展(避免 vector 扩容时的全量拷贝)

          • 示例:

            std::stack<int> s;       // 等价于 std::stack<int, std::deque<int>>  
            std::queue<char> q;      // 等价于 std::queue<char, std::deque<char>>  
            
      2. priority_queue 实现原理
        • 基于 堆结构(完全二叉树)

        • 底层容器需支持:

          • 随机访问(operator[])→ 故用 vector
          • 前端插入/删除(push_back(), pop_back()
        • 堆操作:

          // 插入元素(上浮)
          vec.push_back(x); 
          std::push_heap(vec.begin(), vec.end());// 删除顶部(下沉)
          std::pop_heap(vec.begin(), vec.end());
          vec.pop_back();
          

      自定义底层容器

      // 使用 vector 作为 stack 的底层容器
      std::stack<int, std::vector<int>> vecStack;  // 使用 list 作为 queue 的底层容器
      std::queue<int, std::list<int>> listQueue;  // 使用 deque 作为 priority_queue 的底层容器
      std::priority_queue<int, std::deque<int>> dequePQ;  
      

      注意限制

      • stack:需支持 push_back(), pop_back(), back()
      • queue:需支持 push_back(), pop_front(), front()
      • priority_queue:需支持随机访问迭代器 + front()

      性能与选择指南

      1. stack/queue vs 原生容器
        • 优先用适配器:语义明确 + 防止误操作
        • 需要中间访问时 → 改用 deque
      2. priority_queue 优化
        • 自定义比较器实现最小堆:

          std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
          
        • 复杂类型场景:

          struct Task { int priority; /*...*/ };  
          auto comp = [](const Task& a, const Task& b) { return a.priority < b.priority; 
          };  
          std::priority_queue<Task, std::vector<Task>, decltype(comp)> taskQueue(comp);
          
      3. 内存敏感场景
        • 固定大小栈 → 用 array 替代 deque

          std::stack<int, std::array<int, 100>> fixedStack;
          

2. 修改算法(算法)
*问题类型:

  • 常用的非式修改序列操作(std::for_each, std::find,std::count等)?

    算法功能示例
    std::for_each对每个元素执行操作for_each(v.begin(), v.end(), print)
    std::find查找首个匹配元素find(v.begin(), v.end(), 42)
    std::find_if查找首个满足条件的元素find_if(v.begin(), v.end(), isEven)
    std::count统计匹配元素数量count(v.begin(), v.end(), 'a')
    std::count_if统计满足条件的元素数量count_if(v.begin(), v.end(), isPrime)
    std::all_of检查所有元素满足条件 (C++11)all_of(v.begin(), v.end(), isPositive)
    std::any_of检查至少一个元素满足条件 (C++11)any_of(v.begin(), v.end(), isNegative)
  • 常用的非式序列操作(std::copy, std::remove, std::sort, std::unique,std::reverse等)?

    算法功能注意要点
    std::copy复制序列到目标位置目标容器需预分配空间
    std::remove逻辑删除匹配元素(移动元素)需配合 erase 物理删除(见示例↓)
    std::remove_if逻辑删除满足条件的元素同上
    std::sort排序(默认升序)需随机访问迭代器(不支持 list
    std::stable_sort稳定排序(保持相等元素顺序)性能略低于 sort
    std::unique删除连续重复元素需先排序;返回新逻辑终点
    std::reverse反转序列元素顺序list 有成员函数 reverse()

    删除元素标准写法

    // vector 删除偶数  
    auto newEnd = remove_if(vec.begin(), vec.end(), isEven);  
    vec.erase(newEnd, vec.end());  // 物理删除  
    
  • 常用的数值算法(std::accumulate等)?

    算法功能示例
    std::accumulate累加/自定义归约操作accumulate(v.begin(), v.end(), 0)
    std::inner_product计算内积(点积)inner_product(a.begin(), a.end(), b.begin(), 0)
    std::partial_sum生成前缀和序列partial_sum(v.begin(), v.end(), out)
    std::iota填充递增序列 (C++11)iota(v.begin(), v.end(), 10) // 10,11,12…
  • 如何使用这些算法以及它们与容器成员函数的区别?

    场景选择原因
    list 排序成员 sort()算法 std::sort 需随机访问(不支持链表)
    set 查找成员 find()算法 std::find 是 O(n),成员是 O(log n)
    关联容器删除成员 erase()算法 std::remove 破坏树结构
    通用序列操作STL 算法统一接口,可跨容器使用

    关键原则

    • 关联容器/链表:优先用成员函数(性能/正确性)
    • 序列容器(vector/deque):优先 STL 算法(通用性)
  • std::sort底层实现?(通常是 Introsort)

    • 混合排序策略

      1. 快速排序:主递归阶段(平均 O(n log n))
      2. 堆排序:当递归深度 > 2 log n 时切换(避免最坏 O(n²))
      3. 插入排序:小区间优化(n ≤ 16 时)
    • 核心优势

      • 最坏时间复杂度 O(n log n)(优于纯快排)
      • 避免递归过深(堆排序兜底)
      • 小数据局部性优(插入排序)
    • 示例代码

      std::vector<int> v = {5, 3, 2, 8, 1};  
      std::sort(v.begin(), v.end());  // 升序  
      std::sort(v.begin(), v.end(), std::greater<int>());  // 降序  
      

3. 迭代器(Iterators)
*问题类型:

  • 迭代器的概念和?

    • 定义:迭代器是 STL 中用于 遍历容器元素 的通用抽象接口,行为类似指针(支持 *->++ 等操作)。
    • 作用
      • 解耦算法与容器(如 std::sort 可作用于所有支持随机访问迭代器的容器)
      • 提供统一的容器访问方式
  • 五种迭代器作用类别(输入、输出、前向、个体、随机访问)及其特性?

    类别支持操作容器示例
    输入迭代器只读单次遍历 (*it, ++, ==, !=)istream_iterator
    输出迭代器只写单次遍历 (*it=, ++)ostream_iterator
    前向迭代器读写 + 多次遍历(继承输入/输出)forward_list, unordered_*
    双向迭代器前向 + 反向遍历 (--)list, set, map
    随机访问迭代器双向 + 跳跃访问 (it+n, it[n], <)vector, deque, array

    层级关系
    输入 → 前向 → 双向 → 随机访问
    输出 → 前向 → …

  • begin(), end(), cbegin(), cend(), rbegin(),rend()的区别?

    函数返回类型方向可修改性容器示例调用
    begin()普通迭代器正向可读写vec.begin()
    end()普通迭代器正向尾后不可解引用vec.end()
    cbegin()const 迭代器正向只读vec.cbegin()
    cend()const 迭代器正向尾后不可解引用vec.cend()
    rbegin()反向迭代器反向可读写vec.rbegin()
    rend()反向迭代器反向尾后不可解引用vec.rend()
    crbegin()const 反向迭代器反向只读vec.crbegin()

    反向迭代器注意

    std::vector<int> v = {1,2,3};  
    auto rit = v.rbegin(); // 指向 3  
    *rit = 4;              // v = {1,2,4}   
    
  • 迭代器故障问题,何时会发生,如何避免?

    • 何时发生

      容器导致失效的操作
      vector/string插入/删除(导致扩容或元素移动)
      deque首尾外插入/删除、扩容
      list/forward_list仅删除时使被删元素迭代器失效
      关联容器仅删除时使被删元素迭代器失效
    • 避免方法

      1. 插入后更新迭代器

        auto it = vec.begin();  
        it = vec.insert(it, 10); // 获取新迭代器  
        
      2. 删除时用返回值更新

        for (auto it = lst.begin(); it != lst.end(); ) {  if (*it % 2 == 0) it = lst.erase(it); // 更新为下一个元素  else ++it;  
        }  
        
      3. 避免失效区间操作

        // 错误:删除导致后续迭代器失效  
        for (auto it = vec.begin(); it != vec.end(); ++it) {  if (cond) vec.erase(it); // UB!  
        }  
        

    关键原则

    • 序列容器(vector/deque)修改后所有迭代器可能失效
    • 链表/关联容器修改后仅被删元素迭代器失效

4. 函数对象 (Functors) / Lambda 表达式
*问题类型:

  • 函数对象的概念和作用?

    • 概念:重载了 operator() 的类对象,可像函数一样调用

    • 作用

      • 可携带状态(通过成员变量)
      • 可作模板参数(编译期多态)
      • 比函数指针更高效(可内联优化)
    • 示例

      struct Compare {  bool operator()(int a, int b) const {  return a > b;  // 实现自定义比较  }  
      };  
      std::sort(v.begin(), v.end(), Compare());  
      
  • Lambda 表达式的语法和捕获列表(Capture List)?

    • 语法

      [捕获列表](参数) -> 返回类型 { 函数体 }  
      
    • 捕获列表(Capture List)

      捕获方式效果
      []不捕获外部变量
      [x]按值捕获变量 x(副本)
      [&x]按引用捕获变量 x
      [=]按值捕获所有外部变量(不推荐!)
      [&]按引用捕获所有外部变量(不推荐!)
      [this]捕获当前类对象的 this 指针
      [x = expr]C++14:用表达式初始化捕获(移动捕获)
    • 示例

      int base = 100;  
      auto add = [base](int x) { return x + base; };  
      std::cout << add(5);  // 输出 105  
      
  • Lambda 表达式的本质?

    • 编译器行为
      将 Lambda 转换为匿名函数对象类(含重载的 operator()

    • 转换示例

      // Lambda: [](int x) { return x * 2; }  
      // 编译器生成类似:  
      class __Lambda_123 {  
      public:  int operator()(int x) const { return x * 2; }  
      };  
      
  • 什么时候使用函数对象或 Lambda 表达式?

    场景推荐方式原因
    简单逻辑(如比较器)Lambda代码简洁,无需额外定义
    需要携带复杂状态函数对象可定义多个成员变量/辅助方法
    需要递归调用函数对象Lambda 递归需 std::function(性能损失)
    需作为模板参数(如容器的比较器)函数对象或无捕获 Lambda无捕获 Lambda 可转换为函数指针
    需复用逻辑函数对象避免重复定义

    关键区别

    • 函数对象:显式定义类型,适合复杂/复用逻辑
    • Lambda:隐式匿名类型,适合一次性简单逻辑
http://www.xdnf.cn/news/1016299.html

相关文章:

  • Spring Boot 集成国内AI,包含文心一言、通义千问和讯飞星火平台实战教程
  • 域名+nginx反向代理实现案例
  • Python学习笔记:错误和异常处理
  • 影像组学5:Radiomics Score的计算
  • 深度学习驱动的验证码识别实战:从原理到高并发工业部署
  • YOLOV11改进之多尺度扩张残差模块(MS-DRM)
  • [特殊字符][特殊字符] Harmony OS Next玩转多层级手势事件:当组件遇上“套娃”,触摸该怎么分家?
  • 北斗导航 | 基于matlab的卫星导航单点定位算法
  • Linux文件权限详解:从入门到精通
  • 每日Prompt:Steve Winter风格插画
  • 2.3 ASPICE的架构与设计
  • 服务器上安装配置vsftpd
  • Java流处理中的常见错误与最佳实践
  • 第八十篇 大数据开发基石:深入解析栈结构及其生活化应用(附全流程图解)
  • Cloud Events:事件驱动架构的未来标准化
  • 访问者模式:解耦数据结构与操作的优雅之道
  • 前端性能优化:打造极致用户体验
  • 洛谷:B3799 [NICA #1] 序列
  • 单片机,主循环和中断资源访问冲突的案例
  • P1197 [JSOI2008] 星球大战
  • AI 应用开发(一):TRAE 下自定义 MCP Server
  • 【压缩中断数目--二级中断查找】
  • PostgreSQL的扩展adminpack
  • 机器翻译指标:BLEU
  • 基于边缘计算的丝杆状态实时监测系统设计?
  • 【通用定时器TIM2 TIM3 TIM4 TIM5】
  • Codeforces Round 1023 (Div. 2) C. Maximum Subarray Sum
  • 2025秋招后端突围:JVM核心面试题与高频考点深度解析
  • 电脑在使用过程中频繁死机怎么办
  • Java并发编程实战 Day 21:分布式并发控制