C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ std::vector 和 std::list 区别

C++ 中 std::vector 和 std::list 的区别详解

作者:是娇娇公主~

本文详细介绍了C++标准库中的std::vector和std::list的区别,从底层数据结构、内存布局与分配策略、操作时间复杂度对比、迭代器有效性与类别、其他特性与成员函数差异、内存占用与缓存性能等方面进行了阐述,感兴趣的朋友跟随小编一起看看吧

前言

在 C++ 标准库(STL)中,std::vector 和 std::list 都是最常用的序列容器,它们都支持 push_back、insert、erase、begin()/end() 等相似接口,看起来“用法差不多”。但如果只停留在接口层面,就很容易在性能瓶颈出现时踩坑。本篇文章将一步步帮你彻底搞清楚二者的本质区别~~

一、底层数据结构(这是理解一切差异的根源)

std::vector< T >:动态数组。
它在连续的一块内存上存储元素,就像一个可以自动扩容的普通数组。当元素数量超过当前容量时,会向操作系统申请一块更大的连续内存,把旧元素搬过去(复制或移动),再释放旧内存。

std::list< T >:双向链表。
每个元素都是一个独立的节点,节点结构大致为:

struct Node{
    T data;
    Node* prev;
    Node* next;
};

节点之间通过指针链接,内存完全不连续。list 内部只维护头尾指针(head 和 tail),无需连续内存块。

二、内存布局与分配策略

  1. vector:
  1. list:

vector 更“省内存 + 快访问”,list 更“灵活但浪费空间”。

三、操作时间复杂度对比

操作std::vectorstd::listjieshi
随机访问 v[i] / *itO(1)O(n)vector 直接指针运算,list 只能从头/尾遍历
尾部插入 push_back摊销 O(1)O(1)vector 偶尔扩容导致摊销
尾部删除 pop_backO(1)O(1)-
头部插入 push_frontO(n)O(1)vector 需要整体右移
头部删除 pop_frontO(n)O(1)-
中间插入/删除(有迭代器)O(n)O(1)vector 需要移动后续所有元素
查找元素(无序)O(n)O(n)两者都需要线性遍历
排序 std::sort极快(连续内存)极慢(无法随机访问)std::list 只能用自己的 sort()

记忆总结:
vector:随机访问快,中间修改慢。
list:任意位置修改快,随机访问慢。

四、迭代器有效性与类别

迭代器是 STL 容器的“指针”,修改容器后迭代器可能失效,这是很多Bug的源头。

1.迭代器类别:

vector:随机访问迭代器,支持 it + n、it[n]、it < other 等。
list:双向迭代器,仅支持 ++、–。

2.插入/删除后的迭代器有效性

vector:
insert/erase:可能导致所有迭代器、指针、引用全部失效(因为可能触发 reallocation)。
即使不 reallocation,后面的元素也会前移,指向后面元素的迭代器会“错位”。
#:reallocation 指的是动态数组的内存重新分配。

list:
insert/erase:只有指向被删除元素的迭代器失效,其他所有迭代器、指针、引用全部保持有效(这是链表的最大优势)。

所以在循环中边遍历边删除时,list 可以安全地 it = lst.erase(it);,vector 必须小心处理索引或使用 erase 返回的迭代器。

五、其他特性与成员函数差异

  1. vector 独有(动态数组特权):
  1. list 独有(链表特权):

共同点:两者都支持 emplace_back、emplace(C++11 原地构造)、移动语义(高效转移)。

六、内存占用与缓存性能

假设 sizeof(T) = 8 字节,存储 10000 个元素:

vector:约 80KB + 少量元数据。
list:约 80KB(数据)+ 10000×16 字节(指针)≈ 240KB,3 倍内存。

缓存命中率:

vector 在顺序遍历时几乎 100% 命中 L1/L2 缓存。
list 每个节点跳转可能导致缓存失效,性能差距可达 5~10 倍。

七、适用场景

  1. 选择 std::vector 的情况:

2.选择 std::list 的情况:

八、代码示例

#include <vector>
#include <list>
#include <iostream>
using namespace std;
int main() 
{
    vector<int> v = {1,2,3,4,5};
    list<int> l = {1,2,3,4,5};
    // vector 中间插入(慢)
    v.insert(v.begin()+2,999);   // 1 2 999 3 4 5
    // list 中间插入(快 + 迭代器不失效)
    auto it = l.begin();
    advance(it,2);            // 指向第 3 个元素
    l.insert(it,999);              // 1 2 999 3 4 5
    // 此时原 it 仍指向原来的 3,不会失效
    cout<<"vector[2] ="<<v[2]<<endl;  // 999(O(1))
    // list 不能写 l[2],必须遍历
    return 0;
}

总结:
vector = 动态数组:连续内存 -》 随机访问快、缓存友好、内存省,但中间操作慢。
list = 双向链表:指针链接 -》 任意位置修改快、迭代器稳定,但随机访问慢、内存占用高。

到此这篇关于C++ 中 std::vector 和 std::list 的区别的文章就介绍到这了,更多相关C++ std::vector 和 std::list 区别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文