1. stack介绍及使用方法
stack是一种后进先出的数据结构,所以在C++的STL库中也同样遵循了这一点,我们在使用的时候
不支持随机访问或迭代器遍历。
注意事项
- 调用
top()
或pop()
前需确保栈非空,否则可能引发未定义行为。 stack
没有clear()
函数,如需清空栈需手动循环调用pop()
。- 性能:所有操作的时间复杂度为 O(1)。
stack的底层是一个 deque<T>
(双端队列),虽然deque是可以从两端来进行操作的,但是我们只要只提供一端的接口,那么就可以来把它当做stack来使用。
我们在空间上要把它理解成这个样子,瘟疫这样更好理解。
2. stack的常用接口
函数名 | 作用 |
---|---|
void push(const T& x) | 放一个元素进入stack |
void pop() | 抛出一个元素 |
T& top() | 返回最后一个放入的元素 |
size_t size() | 返回stack里面的元素个数 |
bool empty() | 判断是否为空 |
接下来我会一一实现这些端口。
3. template
在这里写两个calss是为了方便使用,这两个模板参数实际上是各司其职,只是语法上要求写在同一个 template<>
里,使用时会根据场景自动匹配对应的参数。
- 前面的
T
是明确指定栈中元素的类型(比如int
、string
等),这是栈对外提供的 “数据类型契约”—— 栈里只能存T
类型的东西。- 后面的
Container
是指定用什么容器来存储这些T
类型的元素,而默认的deque<T>
就是 “用双端队列来存T
类型元素” 的意思。
PS:只写后面那一个理论上来说也是可以的,但是会让用户使用门槛变高,比如想声明一个存 int
的栈,用户不能直接写 stack<int>
,而必须写成 stack<deque<int>>
(如果想用默认容器),或者 stack<vector<int>>
。这会让接口变得不直观 —— 用户需要先知道底层容器的类型,才能正确声明栈,违背了 “栈是容器适配器,用户无需关心底层实现” 的设计初衷。
PS:只写前面哪一个是不行的,如果只写 template<class T>
,就意味着用户无法指定底层容器了 —— 因为没有 Container
这个参数来接收用户自定义的容器类型。
template<class T, class Container = deque<T>>
4. private
这就是申明一个私有成员_con。
private:Container _con;
5.pop()
抛出最后一个元素(因为是后进先出)。
void pop()
{_con.pop_back();
}
6. top()
返回最后一个元素(不抛出)。
T& top()
{return _con.back();
}
7. size()
返回stack里面元素的个数。
size_t size()
{return _con.size();
}
8.empty
判断是否为空,为空的话就返回true。
bool empty()
{return _con.empty();
}
9. push()
在尾部的位置上插入一个元素。
void push(const T& x)
{_con.push_back(x);
}
10. 总结
stack到这边就讲完了,作为 C++ 中常用的容器适配器,以简洁的接口和灵活的底层设计,为 LIFO 场景提供了高效支持。理解其底层依赖deque
的原因、掌握核心接口的使用规范,能帮助开发者在实际场景中更合理地选择和使用stack
,避免因误用接口导致的逻辑错误。
以下是完整代码:
#pragma once
#include<vector>
#include<deque>namespace struggle
{// template<class T, class Container = deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};