0

0

c++如何使用std::deque双端队列_c++ 容器内部结构与随机访问性能【教程】

尼克

尼克

发布时间:2026-01-01 08:13:21

|

790人浏览过

|

来源于php中文网

原创

c++kquote>std::deque随机访问为O(1)但比vector慢,因其分段连续内存需计算段号与偏移、两次解引用及除模运算;vector则直接指针偏移。实测延迟高2–3倍,适用于首尾频繁增删+偶发索引读取场景。

c++如何使用std::deque双端队列_c++ 容器内部结构与随机访问性能【教程】

std::deque 的随机访问为什么比 vector 慢但仍是 O(1)

因为 std::deque 不是连续内存块,而是分段连续的缓冲区(通常为固定大小的数组)组成的链表结构。索引访问需先计算段号和段内偏移,涉及两次指针解引用和除法取模运算,而 std::vector 直接 base + i 即可。实测在大量随机读写时,deque 的访存延迟通常高出 2–3 倍。

典型使用场景:需要频繁在首尾插入/删除,且偶尔按索引读取(如实现滑动窗口、撤销、BFS 队列中需回溯中间元素)。

  • 不要用 deque 替代 vector 仅为了“支持 push_front”——若无首部修改需求,vector 更快更省内存
  • 默认缓冲区大小由标准库实现决定(libstdc++ 通常为 512 字节,MSVC 约 16 个元素),不可配置;不要假设其值
  • 迭代器失效规则宽松:仅首尾插入不使其他迭代器失效,但 erase 中间元素仍会使所有迭代器失效(与 vector 一致)

如何安全地在 deque 开头插入并避免意外拷贝

push_front()emplace_front() 行为差异关键在于构造方式:push_front 先构造临时对象再移动/拷贝入容器;emplace_front 直接在容器内部内存原位构造。对自定义类型尤其重要。

以下示例中,MyClass 的移动构造函数会被 emplace_front 调用,而 push_front 可能触发额外拷贝(取决于编译器优化):

立即学习C++免费学习笔记(深入)”;

STORYD
STORYD

帮你写出让领导满意的精美文稿

下载
struct MyClass {
    MyClass(int x) : val(x) { std::cout << "ctor\n"; }
    MyClass(const MyClass& other) : val(other.val) { std::cout << "copy ctor\n"; }
    MyClass(MyClass&& other) noexcept : val(other.val) { std::cout << "move ctor\n"; }
    int val;
};

std::deque dq;
dq.emplace_front(42);   // 输出: ctor
dq.push_front(MyClass(99)); // 输出: ctor → move ctor(或 copy ctor,若未启用 RVO)
  • 始终优先用 emplace_front/emplace_back,尤其参数复杂或类型昂贵时
  • push_front({a, b, c}) 是合法的,但会调用初始化列表构造函数;确认类是否支持
  • 注意:deque 不提供 reserve(),无法预分配首尾空间,频繁小量 push_front 可能引发多次缓冲区分配

迭代 deque 时哪些操作会导致未定义行为

最常见误用是边遍历边修改首尾以外的位置。例如在 for-range 循环中调用 erase(iterator) 后继续用原迭代器 ++ —— 这在 deque 中是未定义行为,因为 erase 可能使后续迭代器失效(即使只删一个元素)。

正确做法是使用返回的迭代器(C++11 起 erase 返回下一个有效位置):

std::deque dq = {1, 2, 3, 4, 5};
for (auto it = dq.begin(); it != dq.end(); ) {
    if (*it % 2 == 0) {
        it = dq.erase(it); // ✅ 安全
    } else {
        ++it;
    }
}
  • pop_front()pop_back() 不影响其他迭代器,可安全混用遍历
  • insert(pos, ...)erase(pos) 在非端点位置操作后,所有现存迭代器、指针、引用均可能失效(标准未保证,各实现通常全部失效)
  • 不要依赖 deque[0]&deque[0] 获取首元素地址来模拟 C 数组——该地址不指向连续数据起点,且不能用于 std::sort 等算法

deque 与 list、vector 在典型操作下的性能对比

选择依据不是“功能覆盖”,而是具体操作频次与数据规模。以下是常见操作的渐进复杂度与实际倾向:

  • push_front/push_back:三者都是摊还 O(1),但 deque 实际常数最小(无需重新分配+拷贝整个块,只需新增/复用缓冲区)
  • random access(如 dq[i]):vector 最快,deque 次之(缓存局部性差),list 是 O(n)
  • insert/erase at middle:list 是 O(1)(给定迭代器),deque 和 vector 都是 O(n);但 deque 的移动开销远小于 vector(只挪本缓冲区内元素,而非整块内存)
  • 内存占用:deque > vector(多出指针数组和缓冲区管理开销),deque ≈ list(但 list 每节点额外两个指针)

真正容易被忽略的是:当元素类型很大(如 std::array)且只做首尾操作时,deque 的缓存友好性反而可能优于 vector——因为 vector 每次 push_back 触发扩容时要 memcpy 几 KB 数据,而 deque 只复制指针和少量元数据。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

379

2023.09.04

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

367

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

561

2023.08.10

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

389

2023.08.14

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

319

2023.10.09

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

371

2023.10.16

asp连接access数据库的方法
asp连接access数据库的方法

连接的方法:1、使用ADO连接数据库;2、使用DSN连接数据库;3、使用连接字符串连接数据库。想了解更详细的asp连接access数据库的方法,可以阅读本专题下面的文章。

119

2023.10.18

access和trunk端口的区别
access和trunk端口的区别

access和trunk端口的区别是Access端口用于连接终端设备,提供单个VLAN的接入,而Trunk端口用于连接交换机之间,提供多个VLAN的传输;Access端口只传输属于指定VLAN的数据,而Trunk端口可以传输多个VLAN的数据,并使用VLAN标签进行区分。想了解更多access和trunk端口相关内容,可以阅读本专题下面的文章。

314

2023.10.31

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
CSS3 教程
CSS3 教程

共18课时 | 4.1万人学习

Sass 教程
Sass 教程

共14课时 | 0.7万人学习

Pandas 教程
Pandas 教程

共15课时 | 0.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号