1) 核心差异速览

  • std::vector:连续内存、随机访问 O(1)、尾部 push_back 摊还 O(1)、中间插入/删除 O(n),非常缓存友好。
  • std::deque:分段(block)存储,不是整体连续;随机访问 O(1)(但常数比 vector 大);两端插入/删除摊还 O(1);中间插入/删除 O(n)。适合双端队列。
  • std::list:双向链表,节点分散、随机访问 O(n)、在已知位置插入/删除 O(1)、splice(跨 list 移动)O(1) 并且不使其他迭代器失效(被移动元素的迭代器继续有效但指向新容器)。适合需要稳定指针/节点移动而不复制元素的场景。

2) 内存布局与缓存局部性(关键影响性能)

  • std::vector:元素紧密连续排列(C-style 数组),单次分配大块内存 → 最佳缓存局部性 / CPU 预取效果好,遍历与数值运算非常快。适合数值、图像、点云等大量数据的顺序处理。
  • std::deque:实现为若干固定大小块(segments)+ 一个“map”(指针数组)指向块。元素在逻辑上是连续的,但物理上分段存放;随机访问需要两级寻址(map + block index),因此本地性和常数开销不如 vector
  • std::list:每个元素封装在节点里(元素 + 前向/后向指针),每个节点通常是独立分配 → 极差的缓存局部性 & 大量小分配开销。只有在需要节点稳定性和 O(1) 插入/删除时才有价值。

3) 算法复杂度(常见操作 Big-O)

(常见且直观版本)

操作vectordequelist
随机访问 operator[]O(1)(连续)O(1)(分段)O(n)
push_back摊还 O(1)(可能重分配)摊还 O(1)O(1)
push_frontO(n)(要移动元素)摊还 O(1)O(1)
插入/删除 中间位置O(n)(移动元素)O(n)(移动/拷贝)O(1)(若已知迭代器)
splice / 跨容器移动O(1)(不复制元素)
遍历(线性访问成本)最快(缓存)中等最慢(指针跳转)

注:vector 的尾部 push_back摊还 O(1)(因为扩容策略),但发生重分配时会拷贝/移动全部元素。deque 在两端插入通常是 O(1),但插入可能导致 map 调整。list 的插入/删除在已定位节点时是真正 O(1)。(表中结论与 cppreference 的复杂度说明一致)


4) 迭代器 / 指针 / 引用失效规则(非常重要)

这是容易出 bug 的地方,下面列出常见操作如何影响已有的迭代器/指针/引用(以当前标准行为为准)。

std::vector

  • 重分配(capacity 变化):会使所有迭代器、指针和引用失效(因为底层缓冲区搬移)。
  • push_back / emplace_back:若触发重分配 → 所有失效;若不触发重分配 → 仅影响 end()(过去的 end 不再有效)。
  • insert(非尾部):若不重分配 → 使插入点之后的迭代器/引用失效(元素被移动);若重分配 → 全部失效。
  • erase:擦除后从被擦除位置到末尾的迭代器/引用失效。

std::deque

  • 规则稍复杂,总结常见点:

    • 在中间 insert/erase:通常使所有迭代器失效(实现细节有所不同,但应当假设会全失效)。
    • 在两端(push_front/push_back / pop_front/pop_back:通常是 O(1),但有可能使迭代器失效(特别是当内部 map/blocks 需要扩展时)。擦除两端通常只使被擦除元素的迭代器失效,但 end() 在某些情况也会失效。简而言之:不要对 deque 假设严格的迭代器稳定性。

std::list

  • 插入/移动(insert, splice 等):不会使其他迭代器/引用失效(节点只是调整指针)。
  • 擦除:只有指向被擦除元素的迭代器/引用失效;其他迭代器保持有效。
  • splice:把一段节点从一个 list 移到另一个 list,不复制元素、也不失效迭代器(但指向被移动元素的迭代器现在属于新容器)。这点非常有用(实现 LRU、链表合并等)。

5) 典型使用场景与实战建议(什么时候选哪个)

  • 优先选择 std::vector

    • 默认容器:绝大多数场景(顺序访问、数值计算、排序、与 C API 交互、内存紧凑很重要)都用 vector
    • 优化项:对频繁 push_back 的场景先 reserve(),用 emplace_back() 来避免多余拷贝/移动。reserve 可避免重分配从而保持指针/引用稳定。
  • 选择 std::deque

    • 需要在两端高效插入/删除(如双端队列、实现 std::stack 默认底层)。
    • 不需要与 C 风格连续内存交互,且能接受略差的缓存局部性。不要以为 deque 保证插入不失效 — 对迭代器失效要有防范。
  • 选择 std::list

    • 必须在容器内部频繁在已知迭代器处做 O(1) 插入/删除,且必须要迭代器/引用稳定(例如实现复杂链式结构、需要频繁 splice 的场景)。
    • 否则通常不推荐:链表遍历慢、内存开销大(节点 + 分配器元数据),并且常常有更好的替代(vector + index、deque、或 intrusive containers)。

6) 常见陷阱 & 优化技巧(实战干货)

  • 默认用 vector:现代 C++ 社区共识是“首选 vector,除非有明确理由”。

  • 避免频繁小分配std::list 每个节点通常单独分配,带来 malloc/allocator 开销;如果必须,考虑自定义内存池或 boost::intrusive_list

  • reserve() 避免重分配:对于 vector,若能预知元素数量,v.reserve(n) 会大幅降低重分配/拷贝成本并保持指针稳定。

  • 删除元素时的正确写法(在遍历中安全删元素):

    • vector / deque

      for (auto it = c.begin(); it != c.end(); ) {if (should_erase(*it)) it = c.erase(it); // erase 返回下一个迭代器(C++11 起)else ++it;
      }
      
    • list

      for (auto it = l.begin(); it != l.end(); ) {if (cond) it = l.erase(it); // O(1),其他迭代器不受影响else ++it;
      }
      
  • erase-remove 惯用法:对 vector 批量删除满足条件的元素应该使用:

    v.erase(std::remove_if(v.begin(), v.end(), pred), v.end());
    

    这比在循环中逐个 erase 快很多。

  • 避免在 deque 中假设迭代器稳定性:如果需要稳定引用并且要频繁在两端操作,重新验证你的需求;有时 vector + index 更简单且更快。

  • splice 是链表的王牌:当需要移动大量元素而不做复制/构造/析构时,用 list::splice (O(1))能大幅提高性能。


7) 代码示例 — 常见用法与陷阱演示

预分配避免重分配(vector)

std::vector<MyType> v;
v.reserve(1000);           // 预分配,避免扩容导致的整体移动
for (...) v.emplace_back(...); // in-place 构造,避免拷贝

遍历时删除(安全写法)

// vector / deque
for (auto it = v.begin(); it != v.end(); ) {if (need_erase(*it)) it = v.erase(it);else ++it;
}// list
for (auto it = l.begin(); it != l.end(); ) {if (need_erase(*it)) it = l.erase(it); // 只使被删节点的迭代器失效else ++it;
}

list::splice(O(1) 移动节点,不复制)

std::list<int> a{1,2,3,4}, b{10,11};
// 把 a 中的 [2,4) 移到 b 的开头
auto first = std::next(a.begin()); // 指向 2
auto last  = std::next(first, 2);  // 指向 4 (不含)
b.splice(b.begin(), a, first, last); // O(1)

8) 快速参考表(便于记忆)

  • 需要最快的遍历/数值处理/与 C 互操作std::vector
  • 需要两端高效插入/弹出(deque/queue/stack)std::deque
  • 需要在已知位置 O(1) 插入/删除 & 稳定迭代器/指针/引用;或需要 O(1) 跨容器移动节点std::list(并考虑内存/缓存开销)

9)std::queue 应用示例

下面是一个 C++11/17 标准库实现的多线程生产者-消费者模型完整示例。采用 std::threadstd::mutexstd::condition_variable,队列用 std::queue 封装,支持多生产者、多消费者。


示例代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>// 线程安全队列
template <typename T>
class ThreadSafeQueue {
public:void push(T value) {{std::lock_guard<std::mutex> lock(mtx_);queue_.push(std::move(value));}cv_.notify_one(); // 唤醒一个等待的消费者}T pop() {std::unique_lock<std::mutex> lock(mtx_);cv_.wait(lock, [this]{ return !queue_.empty() || done_; });if (queue_.empty()) {return T(); // 若结束且队列空,返回默认值}T value = std::move(queue_.front());queue_.pop();return value;}void shutdown() {{std::lock_guard<std::mutex> lock(mtx_);done_ = true;}cv_.notify_all(); // 唤醒所有等待线程}private:std::queue<T> queue_;std::mutex mtx_;std::condition_variable cv_;bool done_ = false;
};// ------------------- 生产者/消费者测试 -------------------
void producer(ThreadSafeQueue<int>& q, int id, int count) {for (int i = 0; i < count; i++) {std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟耗时int value = id * 100 + i;q.push(value);std::cout << "[Producer " << id << "] produced: " << value << "\n";}
}void consumer(ThreadSafeQueue<int>& q, int id) {while (true) {int value = q.pop();if (value == 0 && q.pop() == 0) break; // 简单退出条件(可换为特殊结束标记)if (value != 0) {std::cout << "    [Consumer " << id << "] consumed: " << value << "\n";}}
}int main() {ThreadSafeQueue<int> q;// 启动多个生产者和消费者std::thread p1(producer, std::ref(q), 1, 5);std::thread p2(producer, std::ref(q), 2, 5);std::thread c1(consumer, std::ref(q), 1);std::thread c2(consumer, std::ref(q), 2);// 等生产者结束p1.join();p2.join();// 通知消费者结束q.shutdown();c1.join();c2.join();std::cout << "All threads finished.\n";return 0;
}

运行逻辑

  1. 生产者线程 持续往队列里放任务 (push)。
  2. 消费者线程 调用 pop,若队列为空则阻塞等待。
  3. shutdown() 用于通知消费者退出。
  4. std::condition_variable 保证高效等待,而不是忙轮询。

输出示例(顺序可能不同,因为多线程)

[Producer 1] produced: 100
[Producer 2] produced: 200[Consumer 1] consumed: 100[Consumer 2] consumed: 200
[Producer 1] produced: 101
[Producer 2] produced: 201[Consumer 1] consumed: 101[Consumer 2] consumed: 201
...
All threads finished.

10)总结

  • 首选 std::vector(最快、最省内存、最常用);
  • 如果必须在两端频繁操作,考虑 std::deque(牺牲一些局部性和常数);
  • 只有在确实需要节点级别稳定性或 O(1) splice/插入/删除时才用 std::list(并准备承担内存与缓存的代价)。

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

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

相关文章

【js】js实现日期转大写:

文章目录一、方法&#xff1a;二、使用效果&#xff1a;一、方法&#xff1a; export function dateToChnese(strDate) {let dateMap {year: "",month: "",day: ""}if (!strDate || strDate.length 0) return dateMap;const chineseDigit [&…

逆向 js

参考地址&#xff1a;https://blog.csdn.net/2302_80243887/article/details/146349209 注意事项 1. crypto-js 安装 需要你的.js文件同级目录执行npm install crypto-js 才能让js文件引入包 注意事项2&#xff1a; crypto-js 执行js 报错_external_runtime.py" A…

FFmpeg的安装及简单使用

简介 FFmpeg 是一个跨平台的音视频处理工具库/命令行工具&#xff0c;其核心作用是&#xff1a;对音视频文件或流进行解码、转换&#xff08;编码&#xff09;、封装/解封装等处理。 友情提示 本次安装以Windows64位操作系统为例 一、下载及安装 1、前往FFmpeg官网&#xff0…

Science Advances--3D打印生物启发扭曲双曲超材料,用于无人机冲击缓冲和自供电实时传感

湍流引起的振动会对飞机的结构完整性及飞行稳定性造成巨大威胁&#xff0c;尤其是在无人驾驶飞行器&#xff08;UAV&#xff09;中&#xff0c;实时的冲击监测和轻质防护尤为重要。该研究基于生物启发&#xff0c;通过3D 打印尼龙PA12 制备了一种扭转-双曲面超材料&#xff08;…

AI大模型的研发流程

开发一个大模型是一个庞大、复杂且资源密集的系统工程&#xff0c;涉及算法研究、工程实现、数据管理和算力基础设施等多个层面。下面我将为您提供一个从零开始开发大模型的全景式路线图&#xff0c;涵盖了从概念到部署的全过程。请注意&#xff0c;完全从零开始训练一个类似GP…

Docker desktop安装Redis Cluster集群

本文章将介绍如何在 Windows 系统的 Docker Desktop 环境中搭建 Redis 集群。将创建一个包含 6 个节点&#xff08;3 主 3 从&#xff09;的 Redis 集群。 环境准备 Windows 10/11 操作系统Docker Desktop 已安装并运行 步骤 清理环境&#xff08;如之前有尝试&#xff09; 如果…

Zynq开发实践(SDK之第一个纯PS工程)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】学编程的时候&#xff0c;大家一般都比较重视第一个项目的创建和执行。第一个fpga程序一般是led闪烁&#xff0c;第一个c程序一般就是hello world程…

EJS(Embedded JavaScript)(一个基于JavaScript的模板引擎,用于在HTML中嵌入动态内容)

文章目录**1. 什么是 EJS&#xff1f;****2. 核心特点**- **接近原生 HTML**- **动态渲染**- **轻量高效**- **与 Express 深度集成****3. EJS 的基本语法****4. 示例代码****HTML 模板&#xff08;views/user.ejs&#xff09;****Express 中渲染模板****5. 使用场景**1. **服务…

Linux:基于阻塞队列的生产者消费模型

文章目录一、生产者消费者模型的基本原则&#x1f495;&#x1f495;生产者-消费者模型的 321 原则&#x1f495;&#x1f495;二、为何要使用生产者消费者模型1. 解耦2. 支持并发 &#xff08;提高效率&#xff09;3. 忙闲不均的支持三、基于 BlockingQueue 的生产者消费者模型…

ensp启动路由器报错40

1. 先关闭 eNSP 模拟器、关闭 Virtualbox2. 在everything里面搜索 .VirtualBox文件夹&#xff0c;然后删掉3. 再打开 eNSP&#xff0c;不添加任何模拟设备&#xff0c;单击“菜单-工具-注册设备”&#xff0c;将 AR_Base 重新注册。4. 关闭 eNSP 模拟器

代码随想录二刷之“图论”~GO

A.深搜与广搜&#xff08;重点掌握&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 深搜类似于回溯法 搜索方向&#xff0c;是认准一个方向搜&#xff0c;直到碰壁之后再换方向换方向是撤销原路径&#xff0c;改为节点链接的下一个路径&#xff0c;回溯的过程…

基于Echarts+HTML5可视化数据大屏展示-白茶大数据溯源平台V2

效果展示&#xff1a;代码结构&#xff1a;主要代码实现 index.html布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta n…

Linux 系统网络配置及 IP 地址相关知识汇总

Linux 系统网络配置及 IP 地址相关知识汇总 一、IP地址基础 IP地址&#xff1a;在计算机网络中用来唯一标识一台设备的一组数字。 二、IPv4相关知识 1. IPv4的表示方法 采用点分十进制表示&#xff0c;即由4个0-255的十进制数通过点分隔组成&#xff08;如192.168.1.1&#xff…

百度股价突破120美元创年内新高,AI云成为增长新引擎

美东时间9月16日&#xff0c;百度&#xff08;NASDAQ: BIDU&#xff09;美股大涨近8%&#xff0c;收盘价突破120美元&#xff0c;站上124美元高位&#xff0c;创2023年10月以来新高。北京时间9月17日港股开盘&#xff0c;百度&#xff08;09888.HK&#xff09;港股再次暴涨&…

《彩虹六号:围攻》“Siege X”发布会3月14日举行!

使用jQuery的常用方法与返回值分析 jQuery是一个轻量级的JavaScript库&#xff0c;旨在简化HTML文档遍历和操作、事件处理以及动画效果的创建。本文将介绍一些常用的jQuery方法及其返回值&#xff0c;帮助开发者更好地理解和运用这一强大的库。 1. 选择器方法 jQuery提供了多种…

[从青铜到王者] Spring Boot+Redis+Kafka电商场景面试全解析

互联网大厂Java开发岗技术面试实录&#xff1a;严肃面试官VS搞笑程序员谢飞机 文章内容 第一轮&#xff1a;基础框架与并发控制&#xff08;电商系统基础能力&#xff09; 面试官&#xff08;严肃&#xff09;&#xff1a;欢迎进入面试环节&#xff0c;首先请用3句话总结Spring…

【DMA】DMA架构解析

目录 1 DMA架构 1. 芯片架构图一览 2. AHB总线矩阵挂载 3. AHB1/APB1的桥和AHB1/APB2的桥 4. DMA1 和 DMA2 的区别 2 AHB总线矩阵 1 DMA架构 1. 芯片架构图一览 2. AHB总线矩阵挂载 stm32F411 芯片的 AHB 总线矩阵上共挂载了 6 主 5 从 六主&#xff1a; Icode-bus、D…

GPS 定位器:精准追踪的“隐形守护者”

GPS 定位器&#xff1a;精准追踪的“隐形守护者” 一、什么是 GPS 定位器&#xff1f; GPS 定位器是一种基于 全球定位系统&#xff08;Global Positioning System, GPS&#xff09; 的智能追踪设备。 通过接收卫星信号并结合通信模块&#xff08;如 4G、NB-IoT&#xff09;&am…

前端拖拽排序实现

1. 使用 HTML5 事件 触发时机 核心任务 dragstart 开始拖拽时 准备数据&#xff0c;贴上标签 dragover 经过目标上方时 必须 preventDefault()&#xff0c;发出“允许放置”的信号 dragleave 离开目标上方时 清理高亮等临时视觉效果 drop 在目标上松手时 接收数据…

arm coresight

这是一个arm设计的调试基础架构&#xff0c;我们常用的debug基本都包含在内。比如ETM、PTM、ITM、HTM、ETB等。 注意ETM、PTM、ITM、HTM、ETB是coresight的子集。这些工具相比普通debug的断点调试&#xff0c;需要更高的专业水平&#xff0c;因此也用于复杂软件故障定位、性能…