【问题标题】:std::stack implementation on different containers what is the actual difference?不同容器上的 std::stack 实现有什么实际区别?
【发布时间】:2020-07-22 21:13:37
【问题描述】:

当实现std::stack

有几个选项,例如:

   // stack with default underlying deque
   std::stack< int > intDequeStack;   

   // stack with underlying vector
   std::stack< int, std::vector< int > > intVectorStack;

   // stack with underlying list
   std::stack< int, std::list< int > > intListStack;

定义std::stack 有哪些优点和缺点? 在不同的容器上,当我从中得到的只是相同的操作“推送、弹出和顶部”?

换句话说:deque 堆栈与向量堆栈和列表堆栈之间有什么区别?为什么我要选择除 deque 以外的任何东西?

【问题讨论】:

  • 你的前提有点破。 stack 有多个 pushpoptop。例如,它有一个成员c,它是底层容器。它是受保护的,所以如果你愿意,你可以随意摆弄它
  • 忘记堆栈。向量、列表或双端队列在正常使用中的优势是什么?然后从堆栈的角度考虑它。
  • 你让我很好奇,我该如何处理它,我真的应该这样做吗?

标签: c++ stl containers stdstack


【解决方案1】:

堆栈继承了底层容器的性能特征。

  • std::vector 对于最大大小可能不同的堆栈来说是一个糟糕的选择。堆栈上的每个push 都可能需要在向量中进行大量重新分配。除非明确要求,否则向量也永远不会缩小,因此如果堆栈变大然后缩小,则永远不会释放额外的内存(直到堆栈被销毁)。但是,向量与存储的数据之间只有一层间接性。

    因此:如果您知道堆栈将达到的最大大小并且该大小相对较小,那么您在2 上调用的向量可能会是reserve()在所有情况下都比列表或双端队列更快;它具有非常好的数据局部性和更少的访问元素的间接级别。

  • std::list 是一个链表,因此堆栈在弹出1 时会有两级间接,并且它始终会使用它需要的内存量。 push 没有昂贵的重新分配,但它的数据局部性很差,因为每个节点都可以分配到堆中的任何位置;处理堆栈中的大量数据将无法有效地使用各种 CPU 内存缓存,因为每次弹出都可能需要跳转到堆中完全不同的位置。

  • std::deque 通过在一个结构(通常是一个链表)中维护一组短数组来结合两者的某些方面。因此,项目集群将具有良好的数据局部性,并且该结构可以按需增长和收缩而不会大惊小怪,因为数组永远不会重新分配——如果它需要更多空间,它会分配一个新数组并将其添加到开头/结构结束。它是向量和列表之间的一个很好的中间地带,这就是为什么它是默认的。


1 对数据有一级间接性,但是为了移除最后一个元素,链表需要对前一个节点进行另一次间接性以清除下一个指针。 p>

2 请注意,在构建容器时需要移动向量。复制向量不一定保留其容量。例如,您可以使用这个助手:

template <typename T>
std::stack<T, std::vector<T>> vector_stack(
    typename std::vector<T>::size_type capacity
) {
    std::vector<T> vec;
    vec.reserve(capacity);

    return std::stack<T, std::vector<T>>{std::move(vec)};
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-08
    • 1970-01-01
    • 2013-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    相关资源
    最近更新 更多