C++中std::vector Vs std::deque VS std::list對比詳解

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;因此也用于復雜軟件故障定位、性能…