【C++容器】queue使用与底层实现

更多 C++语法知识在 C++语言学记专栏
往期 STL 容器看点:
这篇文章是关于 C++所有数据结构概览中的单独专题

顺序容器vectorarraylistdequeforward_list
关联容器set&multisetmap&multimap无序关联容器Unordered set&multisetUnordered map&multimap
容器适配器stackqueuepriority_queue其他容器string

std::queue(队列)和 std::stack(栈)一样,也是 C++ 标准模板库(STL)中的一个容器适配器。但它实现的是**“先进先出”(First-In, First-Out, FIFO)**的数据结构。你可以把它想象成一个排队买票的队伍:最早来排队的人最先买到票离开,新来的人只能排在队尾。


1. std::queue 的基本使用语法

要使用 queue,首先需要包含头文件 <queue>

#include <iostream>
#include <queue>
#include <string>
1.1 声明和初始化

std::queue 也是一个模板类,在创建时需要指定存储的元素类型。

// 创建一个存储 int 类型的队列
std::queue<int> q1;

// 创建一个存储 std::string 类型的队列
std::queue<std::string> q2;
1.2 核心成员函数

std::queue 的接口同样非常直观,完全服务于“先进先出”的原则。

函数名功能描述
push(element)将一个元素 element 添加到队尾(入队)。
pop()移除队首元素(出队)。注意:这个函数也没有返回值。
front()返回对队首元素的引用。注意:它只返回,不移除。
back()返回对队尾元素的引用。
empty()检查队列是否为空。如果为空,返回 true;否则返回 false
size()返回队列中元素的数量。

stack 的关键区别

  • stacktop() 来访问栈顶(也就是最后进入的元素)。

  • queuefront() 访问队首(最先进入的元素)和 back() 访问队尾(最后进入的元素)。

1.3 代码示例

下面是一个完整的例子,展示了 queue 的基本操作:

#include <iostream>
#include <queue>
#include <string>

int main() {
    // 1. 创建一个空队列,用于存放字符串
    std::queue<std::string> myQueue;

    // 2. 使用 empty() 检查队列是否为空
    if (myQueue.empty()) {
        std::cout << "Queue is initially empty." << std::endl;
    }

    // 3. 使用 push() 向队尾添加元素
    std::cout << "Pushing 'Alice', 'Bob', 'Charlie' into the queue..." << std::endl;
    myQueue.push("Alice");
    myQueue.push("Bob");
    myQueue.push("Charlie");

    // 4. 使用 size() 查看队列中元素数量
    std::cout << "Current queue size: " << myQueue.size() << std::endl; // 输出: 3

    // 5. 使用 front() 和 back() 访问队首和队尾元素
    // 队列内元素顺序 (从头到尾): "Alice", "Bob", "Charlie"
    std::cout << "Front element is: " << myQueue.front() << std::endl; // 输出: Alice
    std::cout << "Back element is: " << myQueue.back() << std::endl;  // 输出: Charlie

    // 6. 使用 pop() 移除队首元素
    std::cout << "Popping the front element..." << std::endl;
    myQueue.pop(); // "Alice" 出队

    // 再次访问队首元素
    std::cout << "Now the front element is: " << myQueue.front() << std::endl; // 输出: Bob
    std::cout << "Current queue size: " << myQueue.size() << std::endl;      // 输出: 2

    // 7. 遍历并清空队列
    std::cout << "Popping all elements: ";
    while (!myQueue.empty()) {
        std::cout << myQueue.front() << " "; // 访问队首
        myQueue.pop();                       // 移除队首
    }
    std::cout << std::endl; // 输出: Bob Charlie

    // 检查队列是否已清空
    if (myQueue.empty()) {
        std::cout << "Queue is now empty." << std::endl;
    }

    // 重要:对空队列调用 front(), back() 或 pop() 是未定义行为,会导致程序崩溃
    // myQueue.front(); // 这行代码会引发错误!

    return 0;
}

关键点总结

  • push() 在队尾添加元素。

  • pop() 从队首移除元素。

  • 获取并移除队首元素需要两步:先用 front() 获取值,再用 pop() 移除。

  • 同样,在调用 front()back()pop() 前,务必用 empty() 进行检查。


2. std::queue 的底层知识

std::queuestd::stack 在设计理念上是相同的,它也是一个容器适配器。

2.1 容器适配器

std::queue 同样包装了另一个序列容器,并限制其接口,使其只能进行 FIFO 操作。它本身不负责内存管理。

2.2 底层容器(Underlying Container)

std::queue 的底层容器必须支持以下操作:

  • front():访问第一个元素。

  • back():访问最后一个元素。

  • push_back():在末尾添加元素。

  • pop_front()从头部移除元素

  • empty():检查是否为空。

  • size():返回大小。

std::queue 的操作与底层容器的映射关系如下:

  • queue::push() 内部调用 container::push_back()

  • queue::pop() 内部调用 container::pop_front()

  • queue::front() 内部调用 container::front()

  • queue::back() 内部调用 container::back()

2.3 默认的底层容器:std::deque

std::stack 一样,std::queue 默认使用的底层容器也是 std::deque(双端队列)。

为什么默认是 std::deque?这次的原因更加关键!

  • 为什么不能是 std::vector

    • std::vector 不支持高效的 pop_front() 操作。要在 vector 的头部移除一个元素,必须将它后面的所有元素都向前移动一位。如果 vector 中有 N 个元素,这个操作的时间复杂度是 O(N),效率极低。因此,std::vector 不能作为 std::queue 的底层容器。
  • std::list (双向链表) vs std::deque (双端队列)

    • std::list:它支持高效的 push_back()pop_front() 操作,时间复杂度都是 O(1)。所以 std::list 可以作为 std::queue 的底层容器。

    • std::deque:它被设计为在两端进行添加和删除都非常高效。它的 push_back()pop_front() 操作的平摊时间复杂度也是 O(1)。

    • deque 胜出的原因:与 list 相比,deque 的内存布局更加紧凑(由连续的内存块组成),这使得它的缓存命中率更高,通常在实际性能上优于 list。因此,std::deque 成为了 std::queue 的默认选择。

2.4 更换底层容器

你可以显式地将 std::queue 的底层容器指定为 std::list

std::queue 的模板定义如下:

template<
    class T,
    class Container = std::deque<T>
> class queue;

示例:使用 std::list 作为底层容器

#include <list>
#include <queue>
#include <iostream>

// 声明一个使用 std::list<int> 作为底层容器的队列
std::queue<int, std::list<int>> list_based_queue;

list_based_queue.push(100);
list_based_queue.push(200);

std::cout << "Front: " << list_based_queue.front() << std::endl; // 输出: 100
list_based_queue.pop();
std::cout << "Front after pop: " << list_based_queue.front() << std::endl; // 输出: 200

总结

  • 是什么std::queue 是一个实现 FIFO(先进先出)策略的容器适配器

  • 怎么用:通过 push()(入队)、pop()(出队)、front()(访问队首)、back()(访问队尾)等函数操作。

  • 底层是什么:它包装了一个序列容器来存储数据。

  • 默认底层容器std::deque,因为它在两端的增删操作都非常高效,且内存性能通常优于 std::list

  • 不可用 vectorstd::vector 因为没有高效的头部删除操作,所以不能作为 std::queue 的底层容器。

  • 可定制:如果特定场景下有需求(例如,频繁的插入删除导致 deque 性能下降),可以将底层容器更换为 std::list

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值