std::vector stack 容器适配器

 

std::vector - cppreference.com https://en.cppreference.com/w/cpp/container/vector

std::stack - cppreference.com https://en.cppreference.com/w/cpp/container/stack

c++ 中明明有vector了为什么还要有stack? - 知乎 https://www.zhihu.com/question/378846608

https://cplusplus.com/reference/stack/stack/ https://cplusplus.com/reference/stack/stack/

 

std::stack

 
 
 
std::stack
 
Defined in header <stack>
   
template<

    class T,
    class Container std::deque<T>

class stack;
   
     

The std::stack class is a container adaptor that gives the programmer the functionality of a stack - specifically, a LIFO (last-in, first-out) data structure.

The class template acts as a wrapper to the underlying container - only a specific set of functions is provided. The stack pushes and pops the element from the back of the underlying container, known as the top of the stack.

Template parameters

T - The type of the stored elements. The behavior is undefined if T is not the same type as Container::value_type.
Container - The type of the underlying container to use to store the elements. The container must satisfy the requirements of SequenceContainer. Additionally, it must provide the following functions with the usual semantics:
  • back()
  • push_back()
  • pop_back()

The standard containers std::vector (including std::vector<bool>), std::deque and std::list satisfy these requirements. By default, if no container class is specified for a particular stack class instantiation, the standard container std::deque is used.

Member types

Member type Definition
container_type Container
value_type Container::value_type
size_type Container::size_type
reference Container::reference
const_reference Container::const_reference

 
 
 
 

std::vector

 
 
 
std::vector
 
Defined in header <vector>
   
template<

    class T,
    class Allocator std::allocator<T>

class vector;
(1)  
namespace pmr {

    templateclass T >
    using vector = std::vector<T, std::pmr::polymorphic_allocator<T>>;

}
(2) (since C++17)
     
1) std::vector is a sequence container that encapsulates dynamic size arrays.
2) std::pmr::vector is an alias template that uses a polymorphic allocator.

The elements are stored contiguously, which means that elements can be accessed not only through iterators, but also using offsets to regular pointers to elements. This means that a pointer to an element of a vector may be passed to any function that expects a pointer to an element of an array.

The storage of the vector is handled automatically, being expanded as needed. Vectors usually occupy more space than static arrays, because more memory is allocated to handle future growth. This way a vector does not need to reallocate each time an element is inserted, but only when the additional memory is exhausted. The total amount of allocated memory can be queried using capacity() function. Extra memory can be returned to the system via a call to shrink_to_fit()[1].

Reallocations are usually costly operations in terms of performance. The reserve() function can be used to eliminate reallocations if the number of elements is known beforehand.

The complexity (efficiency) of common operations on vectors is as follows:

  • Random access - constant 𝓞(1).
  • Insertion or removal of elements at the end - amortized constant 𝓞(1).
  • Insertion or removal of elements - linear in the distance to the end of the vector 𝓞(n).

std::vector (for T other than bool) meets the requirements of ContainerAllocatorAwareContainer(since C++11), SequenceContainerContiguousContainer(since C++17) and ReversibleContainer.

Member functions of std::vector are constexpr: it is possible to create and use std::vector objects in the evaluation of a constant expression.

However, std::vector objects generally cannot be constexpr, because any dynamically allocated storage must be released in the same evaluation of constant expression.

(since C++20)
  1.  In libstdc++, shrink_to_fit() is not available in C++98 mode.

 

 https://www.zhihu.com/question/378846608/answer/1074222213
 stack是一个容器适配器类型。这里的适配器adaptor就是取自设计模式中适配器模式。

适配器模式同样如此,提供实际数据结构存储和操作的另有其人,它只是一层封装,将其他容器的接口,模塑成了符合离散数学中 栈 定义的操作模式。

去看stack的声明:

template <class T, class Container = deque<T> > class stack;

模板参数二可以指定容器类型,默认的时候是deque。你当然也可以传入vector、list。

对于一个数学意义上的栈而言,你应该只关心入栈的一端,而不应该去关心出栈那端,更不应该去关心,栈中任意位置的其他元素。如果你的程序需要关心这些,对不起。请不要用栈。

再做个假设,假如你要设计某个库或框架给公司的其他团队使用,里面涉及到一个栈的数据结构。用vector当然可以实现,不停的push_back和pop_back呗。但是你如果提供出去,你怎么能保证使用者可以如你所愿,如果他们任意修改了任意位置的元素,是否会为线上事故埋下隐患?

STL的设计哲学,除了方便之外,就是苦口婆心地让大家避免滥用容器。避免程序员因为有了STL,不用手写数据结构了,就滥用容器。就和写业务逻辑的程序员喜欢滥用RPC一样(就像调用一个普通函数那样调用了一个远程方法,但是它真的是要消耗时间的

要做到避免滥用,不提供相应的操作接口就可以了。比如这个stack,就不提供tail()、不提供[],是实现不了吗?肯定不是。类似的地方还有很多,比如迭代器为什么要分那么多种,list为什么不能提供[],它的迭代器为什么不能+n,只能+1。+n能实现么,肯定也可以,内部多遍历几遍呗。但是就不提供你这个便利,怕的就是滥用;为什么vector都没有一个自带的sort成员函数,而list会提供一个。这个当然是怕你把std::sort 用list身上,但STL作者又不希望大家手写一个链表排序,所以提供给你好了。借此你也可以了解到链表的排序和数组是不同的。

总而言之,STL在提供方便的同时,也带着限制。看似带着镣铐跳舞,但是用多了,你会发现自己身轻如燕。那些被封禁的操作,是对程序员的保护,也是教化!

 

 

 

 

 

 

posted @ 2024-01-23 11:39  papering  阅读(4)  评论(0编辑  收藏  举报