前言
作者今天来实现一下stack
stack逻辑上是一个先进后出的栈,底层可以考虑用其它容器来实现
在功能上stack相比于vector做了很多简化,比如少了迭代器的功能
1. 容器与容器适配器
cpp
template<class T, class Container = std::deque<T>>
class MyStack
{
public:
C++中stack是一个容器适配器,底层默认使用deque容器
容器适配器是对容器进行的封装,对很多容器的接口进行了限制屏蔽,通过底层容器的接口对底层容器里存的数据进行操作
2. deque的简介
deque是一种双端队列,队列两边都可以做平均O(1)级别的插入或删除
底层是一块一块连续空间,所以支持下标作为索引进行O(1)级别访问
3. 选用deque而不用vector的原因
在stack中,deque比起vector,不需要连续虚拟内存空间,且能进行无用内存的释放
4. stack的top方法返回引用
cpp
T& top() {
return _con.back();
}
const T& top() const {
return _con.back();
}
top方法返回左值引用,若MyStack的对象没加const,外部可以直接对top()进行修改
5. const对象调用const或者非const成员方法,是否可行
cpp
void push(const T &x) {
_con.push_back(x);
}
void pop() {
_con.pop_back();
}
T& top() {
return _con.back();
}
const T& top() const {
return _con.back();
}
size_t size() const {
return _con.size();
}
bool empty() const {
return _con.empty();
}
若类中两个函数同名,参数相同,返回值相同,区别只是隐藏的this指针是否被const修饰
那么如果对象是const修饰,调用该函数,会自动调用const成员函数
如果对象没有const修饰,会自动调用非const成员函数
此外,如果对象没有const修饰,既可以调用非const成员函数,也可以调用const成员函数
如果对象有const修饰,只能调用调用const成员函数
6. std中swap的两个参数类型
cpp
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
std::swap传入的两个参数类型必须是左值,swap的两个形参都是对实参的左值引用
因为交换就意味着对参数的值进行修改了
7. stack的swap实现时,使用Container的swap而不使用stl的swap
cpp
void swap(MyStack<T, Container> &st) noexcept(noexcept(_con.swap(st._con))) {
_con.swap(st._con);
}
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
若是使用std自带的swap,发生一次拷贝,两次赋值,赋值底层可能是对象的深拷贝,效率极低
而使用底层容器的swap,可以尽可能地降低时间复杂度,因为像vector这样的容器,它的swap方法仅将交换三个内置类型(即指针),时间复杂度O(1)
8. noexcept的作用
cpp
void swap(MyStack<T, Container> &st) noexcept(noexcept(_con.swap(st._con))) {
_con.swap(st._con);
}
noexcept放在函数的参数列表后面,若是noexcept()括号中的值为true,在生成MyStack类中swap汇编相关代码时,不会生成抛异常的代码,从而节省了二进制程序的体积
函数在编译期间就开始计算noexcept的结果
9. 把stl的swap也做重载
cpp
template<class T, class Container = std::deque<T>>
void swap(MyStack<T, Container> &a, MyStack<T, Container> &b)
noexcept(noexcept(a.swap(b)))
{
a.swap(b);
}
结合第7. 点,为了防止用户调用std的swap产生低效率的交换,对于swap两个MyStack模板类对象进行了函数重载
总体实现
cpp
#pragma once
#include <deque>
template<class T, class Container = std::deque<T>>
class MyStack
{
public:
void push(const T &x) {
_con.push_back(x);
}
void pop() {
_con.pop_back();
}
T& top() {
return _con.back();
}
const T& top() const {
return _con.back();
}
size_t size() const {
return _con.size();
}
bool empty() const {
return _con.empty();
}
void swap(MyStack<T, Container> &st) noexcept(noexcept(_con.swap(st._con))) {
_con.swap(st._con);
}
private:
Container _con;
};
template<class T, class Container = std::deque<T>>
void swap(MyStack<T, Container> &a, MyStack<T, Container> &b)
noexcept(noexcept(a.swap(b)))
{
a.swap(b);
}
测试代码
cpp
#include <iostream>
#include <vector>
#include "MyStack.hpp" // 你的栈头文件
using namespace std;
int main()
{
// ==========================
// 测试 1:基础功能
// ==========================
MyStack<int> st1;
cout << "===== 测试 1:基础功能 =====" << endl;
cout << "空栈? " << boolalpha << st1.empty() << endl; // true
st1.push(10);
st1.push(20);
st1.push(30);
cout << "size: " << st1.size() << endl; // 3
cout << "栈顶: " << st1.top() << endl; // 30
st1.pop();
cout << "pop 后栈顶: " << st1.top() << endl; // 20
cout << "空栈? " << st1.empty() << endl; // false
cout << endl;
// ==========================
// 测试 2:swap 核心功能
// ==========================
MyStack<int> st2;
st2.push(100);
st2.push(200);
cout << "===== 测试 2:swap 交换 =====" << endl;
cout << "交换前:" << endl;
cout << "st1 顶: " << st1.top() << endl; // 20
cout << "st2 顶: " << st2.top() << endl; // 200
// 两种 swap 都能跑
st1.swap(st2); // 成员 swap
// swap(st1, st2); // 非成员 swap(也能跑)
cout << "交换后:" << endl;
cout << "st1 顶: " << st1.top() << endl; // 200
cout << "st2 顶: " << st2.top() << endl; // 20
cout << endl;
// ==========================
// 测试 3:const 对象
// ==========================
cout << "===== 测试 3:const 栈 =====" << endl;
MyStack<int> st3;
st3.push(666);
const MyStack<int>& st_const = st3;
cout << "const 栈顶: " << st_const.top() << endl;
cout << "const size: " << st_const.size() << endl;
cout << "const empty: " << st_const.empty() << endl;
cout << endl;
// ==========================
// 测试 4:底层容器换成 vector
// ==========================
cout << "===== 测试 4:vector 做底层容器 =====" << endl;
MyStack<int, vector<int>> st_vec;
st_vec.push(111);
st_vec.push(222);
cout << "size: " << st_vec.size() << endl;
cout << "栈顶: " << st_vec.top() << endl;
cout << "\n===== 所有测试全部通过!=====" << endl;
return 0;
}
测试结果
./test
===== 测试 1:基础功能 =====
空栈? true
size: 3
栈顶: 30
pop 后栈顶: 20
空栈? false
===== 测试 2:swap 交换 =====
交换前:
st1 顶: 20
st2 顶: 200
交换后:
st1 顶: 200
st2 顶: 20
===== 测试 3:const 栈 =====
const 栈顶: 666
const size: 1
const empty: false
===== 测试 4:vector 做底层容器 =====
size: 2
栈顶: 222
===== 所有测试全部通过!=====