C++stack模拟实现

前言

作者今天来实现一下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

===== 所有测试全部通过!=====

相关推荐
张祥6422889042 小时前
导数与微分有啥区别
算法·数学建模
rayyy92 小时前
c++, sizeof(string)和string.size()有什么区别
c++
葳_人生_蕤2 小时前
hot100——图
数据结构·算法
Ulyanov2 小时前
《玩转QT Designer Studio:从设计到实战》 QT Designer Studio组件化开发与UI组件库构建
开发语言·python·qt·ui·雷达电子战系统仿真
词元Max2 小时前
2.8 pydantic 数据校验:AI 开发的隐形利器
开发语言·人工智能·python
特长腿特长2 小时前
LVS_DR 模式的原理
linux·运维·网络·云原生·centos·lvs
2401_865382502 小时前
各省政务信息化项目验收材料清单汇总及差异分析
java·开发语言·数据库
froginwe112 小时前
MySQL 删除数据库
开发语言
Rust研习社2 小时前
深入浅出 Rust 泛型:从入门到实战
开发语言·后端·算法·rust