【STL】模拟实现stack & queue & priority_queue & reverse_iterator {容器适配器,反向迭代器,迭代器的分类;阶段小结}

一、stack

#include <deque>
using namespace std;

template<class T, class Container = deque<T>> //deque为默认底层容器
class Stack{
  Container _con; //内部封装了底层容器

public:
  //提供一组特定的成员函数访问其成员,以此来体现数据结构的特性。
  Stack(){}

  template<class InputIterator>
  Stack(InputIterator first, InputIterator last)
  {
    while(first != last)
    {
      _con.push_back(*first);
      ++first;
    }
  }

  void push(const T &val) //弹栈
  {
    _con.push_back(val); 
  }

  void pop() //压栈
  {
    _con.pop_back(); 
  }

  T& top() //取栈顶元素
  {
    return _con.back();
  }

  const T& top() const 
  {
    return _con.back();
  }

  bool empty() const{
    return _con.empty();
  }

  size_t size() const{
    return _con.size();
  }
};


二、queue

#include <deque>
using namespace std;

template<class T, class Container=deque<T>> //deque为默认底层容器
class Queue{
  Container _con;

public:
  Queue(){} //需要提供默认构造函数
	
  template<class InputIterator>
  Queue(InputIterator first, InputIterator last)
  {
    while(first != last)
    {
      _con.push_back(*first);
      ++first;
    }
  }

  void push(const T& val) //队尾入队
  {
    _con.push_back(val);
  }

  void pop() //队头出队
  {
    _con.pop_front();
  }

  T& front(){ //取队头元素
    return _con.front();
  }

  T& back(){ //取队尾元素
    return _con.back();
  }

  const T& front() const{
    return _con.front();
  }

  const T& back() const{
    return _con.back();
  }

  bool empty(){
    return _con.empty();
  }

  size_t size(){
    return _con.size();
  }
};


三、priority_queue

#include <vector>
using namespace std;

//此处定义了两个仿函数/函数对象,用于比较大小:
template <class T>
struct Less{
  bool operator()(const T& l, const T& r) const{
    return l < r;
  }
};

template <class T>
struct Greater{
  bool operator()(const T& l, const T& r) const{
    return l > r;
  }
};

//模拟实现优先级队列:
template<class T, class Container=vector<T>, class Compare = Less<T>> //vector为默认底层容器,less为默认仿函数
class Priority_queue{
  Container _con; //底层容器
  Compare _com; //函数对象
public:
  Priority_queue(){} 

  template<class InputIterator>
  Priority_queue(InputIterator first, InputIterator last)
  {
    while(first != last)
    {
      _con.push_back(*first);
      ++first;
    }

    //从最后一个非叶子节点开始向下调整建堆
    for(int i = (_con.size()-2)/2; i>=0; --i)
    {
      adjust_down(i);
    }

  }

  void push(const T& val)
  {
    _con.push_back(val);
    adjust_up(_con.size()-1);
  }

  //向上调整
  void adjust_up(size_t child)
  {
    size_t parent = (child-1)/2;
    while(child > 0)
    {
      //if(_con[child] > _con[parent])
      if(_com(_con[parent], _con[child])) //利用函数对象进行比较
      {
        std::swap(_con[child], _con[parent]);
        child = parent;
        parent = (child-1)/2;
      }
      else
      {
        break;
      }
    }
  }

  void pop()
  {
    std::swap(_con[0], _con[_con.size()-1]);
    _con.pop_back();
    adjust_down(0);
  }

  //向下调整
  void adjust_down(size_t parent)
  {
    size_t child = parent*2+1;
    while(child < _con.size())
    {
      if(child+1<_con.size() && _com(_con[child], _con[child+1]))
      {
        ++child;
      }
      if(_com(_con[parent], _con[child]))
      {
        std::swap(_con[child], _con[parent]);
        parent = child;
        child = parent*2+1;
      }
      else
      {
        break;
      }
    }
  }

  const T& top() const{ //取堆顶元素,此处返回值必须加const不允许修改,否则会破坏堆结构
   return _con.front(); 
  }

  bool empty() const{
    return _con.empty();
  }

  size_t size() const{
    return _con.size();
  }
};




四、reverse_iterator

4.1 反向迭代器的模拟实现

如何实现反向迭代器?

  1. 反向迭代器的起点和终点和正向迭代器相反。
  2. 反向迭代器的遍历移动方向和正向迭代器相反。
  3. 解引用*和成员访问->操作相同。

综上,由于反向迭代器的方法类似于正向迭代器,只是方向相反。我们可以将反向迭代器设计成适配器,底层分装一个正向迭代器。
复用正向迭代器的成员函数,翻转调用其++和--的重载函数即可:

template <class iterator, class Ref, class Ptr>
class Reverse_iterator{
   iterator _it; //内部封装了一个正向迭代器

public:
   Reverse_iterator(const iterator &it) //用一个正向迭代器进行构造
     :_it(it)
   {}

   Reverse_iterator& operator++(){
      --_it; //翻转调用其++和--的重载函数
      return *this;
   }

   Reverse_iterator& operator--(){
     ++_it;
     return *this;
   }

   Reverse_iterator operator++(int){
     Reverse_iterator tmp(*this);
      --_it;
      return tmp;
   }

   Reverse_iterator operator--(int){
     Reverse_iterator tmp(*this);
     ++_it;
     return tmp;
   }

   bool operator!=(const Reverse_iterator &rit) const {
     return _it != rit._it; 
   }

   bool operator==(const Reverse_iterator &rit) const {
     return _it == rit._it; 
   }
   
   //......
};

现在我们要解决起点和终点的问题了:
vector的反向迭代器起点和终点:
在这里插入图片描述
list的反向迭代器起点和终点:
在这里插入图片描述
我们可以看到,源码中采用了一个对称的方案:反向的起点rbegin就是正向的终点end;反向的终点rend就是正向的起点begin;
我们跟随源码也使用这种方案进行设计:

template<class T>
class Myvector{
  public:
    //正向迭代器
    typedef T *iterator;
    typedef const T *const_iterator;
	//反向迭代器
	//第一个模版参数是迭代器类型,适用于多种迭代器
    typedef Reverse_iterator<iterator, T&, T*> reverse_iterator; 
    //后两个模版参数用于解决const对象的迭代器访问问题 
    typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator; 
    
    iterator begin(){
      return _start;
    }

    iterator end(){
      return _finish;
    }

    reverse_iterator rbegin(){
      return reverse_iterator(end());
    }

    reverse_iterator rend(){
      return reverse_iterator(begin());
    }
//......
};

template <class T>
class Mylist{
  typedef list_node<T> Node;
  Node *_phead;

  public:
  //正向迭代器
  typedef list_iterator<T, T&, T*> iterator;
  typedef list_iterator<T, const T&, const T*> const_iterator;
  //反向迭代器
  //第一个模版参数是迭代器类型,适用于多种迭代器
  typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
  //后两个模版参数用于解决const对象的迭代器访问问题 
  typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

  iterator begin(){
    return iterator(_phead->_next);
  }

  iterator end(){
    return iterator(_phead);
  }

  reverse_iterator rbegin(){
    return reverse_iterator(end());
  }

  reverse_iterator rend(){
    return reverse_iterator(begin());
  }
  //......
};

使用此方案设计出的迭代器不能够直接进行解引用,因为rbegin指向一段非法空间直接解引用会导致越界访问。
因此要进行错位访问,即访问迭代器指向的前一个位置

template <class iterator, class Ref, class Ptr>
class Reverse_iterator{
   iterator _it; //内部封装了一个正向迭代器

public:
   //......
   
   Ref operator*() const{
     iterator tmp = _it; //需要临时拷贝,解引用操作不能移动迭代器
     return *--tmp; //访问迭代器的上一个位置
   }
   
   Ptr operator->() const{
     return &(operator*()); //复用operator*()
   }
};

4.2 迭代器的分类

所有的迭代器都能适配出反向迭代器吗?不一定!

迭代器器的分类:

迭代器种类迭代器功能对应容器的迭代器
正向迭代器 forward_iterator++<forward_list> <unordered_map> <unordered_set>
双向迭代器 bidirectional_iterator++ --<list> <map> <set>
随机访问迭代器 random_access_iterator++ -- + -<vector> <deque>

只有支持++ 和 --操作的迭代器才能适配出反向迭代器。即双向迭代器和随机访问迭代器可以,而正向迭代器不可以。

迭代器是数据结构和算法的粘合剂。不同的算法对迭代器功能的要求也是不同的,比如:
在这里插入图片描述

  1. find函数模版的模版参数是InputIterator,没有指明迭代器的种类表示适用于所有迭代器。
  2. reverse函数模版的模版参数是BidirectionalIterator,表示使用reverse进行逆置的迭代器至少需要是双向迭代器,当然随机迭代器也行。
  3. sort函数模版的模版参数是RandomAccessIterator,表示使用sort进行排序的迭代器必须是随机迭代器。

五、阶段小结

在这里插入图片描述
STL六大组件我们已经了解学习了其中的5个,现在做一个小小的总结:

  1. 容器:就是将数据结构对应数据结构的方法封装在一起。容器一般是类模版,可以存储不同类型的数据。
  2. 算法:通过迭代器遍历访问容器,并对容器中的数据进行排序、逆置等操作。一般是函数模版,可以用于多种容器。
  3. 迭代器:迭代器是几乎所有容器都支持的遍历访问方式,且各容器迭代器的使用方法相同。迭代器是数据结构和算法的粘合剂,通用算法需要通过迭代器访问修改不同容器中的数据。
  4. 适配器/配接器:分为容器适配器和迭代器适配器。总的来说,适配器就是将原有的容器、迭代器等进行封装,并提供一组特定的成员函数来访问其元素,以此来适配出新的容器或迭代器。这种适配器的设计模式增强了代码的复用性;并且同一个适配器可以采用多种底层结构以适应实际的应用需求;
  5. 仿函数:又叫函数对象。其实就是重载了operator()的类,仿函数实例化出的对象可以像函数一样使用,用于替代函数指针。

通过这段时间的学习和以上的总结,我们可以看出模版类封装在C++中发挥的巨大作用:

  • 模版使我们的代码摆脱了数据类型的束缚,同一份代码适用于多种数据类型。
  • 类将底层结构和方法封装在一起,使用时只需要调用其成员函数即可不需要关注其底层。我们甚至可以进行多层封装如适配器。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

52Hertz_Echo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值