跟我学C++中级篇——STL的容器Deque

223 篇文章 92 订阅

 

一、顺序容器Deque

在使用vector时,发现它如果在头部进行操作的话,有点复杂,array也是如此。其实在实现一个标准的C数组时,使用的人都会发现,如果在尾部增加或者删除一个元素,其实都是很简单的。可是如果想在头部增加一个,那是真麻烦。STL库是啥?不就是为解决劳苦大众的福祉而诞生的么。没有,就创建一个嘛。deque就横空出世了,它可以解决头尾都需要操作的场景。
那它为啥能头尾都进行操作呢?做一个连续的空间?其实看deque源码就会发现,它其实并不是一个真正的像数组一样的整体的连续空间。而是分组或者说分段式的连续的空间。那么分段连续的空间怎么能让上层应用看起来像一个没分组的呢?那就需要设计一个map来管理他,通过重载迭代器的指针来实现连续的操作。看下图:

 

其实看到这儿,应该对STL中一定要拆成分配器、算法等的原因了吧。高度重用,快速扩展。

二、源码

老样子看一下它的代码:

template <class _Ty, class _Alloc = allocator<_Ty>>
class deque {
private:
    friend _Tidy_guard<deque>;
    static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Ty, typename _Alloc::value_type>,
        _MISMATCHED_ALLOCATOR_MESSAGE("deque<T, Allocator>", "T"));

    using _Alty           = _Rebind_alloc_t<_Alloc, _Ty>;
    using _Alty_traits    = allocator_traits<_Alty>;
    using _Alpty          = _Rebind_alloc_t<_Alloc, typename _Alty_traits::pointer>;
    using _Alpty_traits   = allocator_traits<_Alpty>;
    using _Mapptr         = typename _Alpty_traits::pointer;//=============map指针扩展
    using _Alproxy_ty     = _Rebind_alloc_t<_Alty, _Container_proxy>;
    using _Alproxy_traits = allocator_traits<_Alproxy_ty>;

    using _Scary_val = _Deque_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Deque_simple_types<_Ty>,
        _Deque_iter_types<_Ty, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
            typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Ty&, const _Ty&, _Mapptr>>>;

    ......
    //C++11增加
    void shrink_to_fit() {
       size_type _Oldcapacity = _DEQUESIZ * _Mapsize();
       size_type _Newcapacity = _Oldcapacity / 2;

       if (_Newcapacity < _DEQUESIZ * _DEQUEMAPSIZ)
           _Newcapacity = _DEQUESIZ * _DEQUEMAPSIZ;

       if ((empty() && 0 < _Mapsize())
           || (!empty() && size() <= _Newcapacity && _Newcapacity < _Oldcapacity)) { // worth shrinking, do it
           deque _Tmp(_STD make_move_iterator(begin()), _STD make_move_iterator(end()));
           swap(_Tmp);
       }
   }

  ......
  using _Mybase::operator-;

  _NODISCARD _Deque_iterator operator-(const difference_type _Off) const {
      _Deque_iterator _Tmp = *this;
      return _Tmp -= _Off;
  }

  _NODISCARD reference operator[](const difference_type _Off) const {
      return const_cast<reference>(_Mybase::operator[](_Off));
  }
......
}

其实如果明白了讲过的组合的味道,基本就知道了这些源码的味道,不要看这么多的代码,其实有好多是显示的标志一些符号而做的,对于学习本身,用处不是很大。重点是掌握了整个源码的流程,这才是最重要的。
通过分析源码可以看出,deque的访问速度理论上讲要比vector要慢,毕竟有一个中间层的存在,获得便利的同时一定会有牺牲性能。也就是说,得到一些东西,就得舍弃一些东西,编程和实际生活一样,不可能事事完美,学会取舍,也就学会了设计的思想的入门。

三、例程

还是要看例子,毕竟重点是学习使用,而不是学习源码:

#include <deque>
#include <iostream>
void PrintDeque(const std::deque<int> & dq)
{
    std::cout << "cur deque print:" << std::endl;
    for (auto& d : dq)
    {
        std::cout << "item value:" << d << std::endl;
    }
    std::cout << "print end!" << std::endl;
}
void TestDeque()
{
    std::deque<int> d1;

    for (int num = 0; num < 10; num++)
    {
        d1.emplace_back(num);
        d1.emplace_front(10 - num);
    }

    PrintDeque(d1);
    std::deque<int>::iterator it;
    d1.erase(++d1.begin());
    int tmp = d1[9];

    std::cout << "tmp value is:" << tmp << std::endl;

    d1[9] = 1000;

    std::cout << "nine value is:" << d1[9] << std::endl;
    int f = 8;
    auto v = std::find(d1.begin(),d1.end(),f);
    std::cout << "find value:" << *v << std::endl;
    PrintDeque(d1);
}
int main()
{
    TestDeque();
    return 0;
}

运行结果是:

cur deque print:
item value:1
item value:2
item value:3
......
item value:8
item value:9
print end!
tmp value is:0
nine value is:1000
find value:8
cur deque print:
item value:1
item value:3
...
item value:10
item value:1000
...
item value:9
print end!

例程非常简单,这也是STL的目的,如果写得天花乱坠,估计更没人用了。

四、总结

std::deque单独使用还是比较少的,一般是使用它的适配器层的std::queue,这个会在适配器中进行学习。其实在于习一门技术或者具体到编程语言时,你会发现,语言的进步往往是实际应用推动的,设计时,可能觉得可有可无的,或者说干脆没有实现的,发现到实际应用中需求很大,不管出于什么原因,一般最后都会添加这个特性。这一点上,Java的模板就是一个很典型的例子。当然,包括Go语言,现在都是这样。c++/c语言自产生后,随着应用的不断的发展,对它们也提出了很多需求,以前的大佬们觉得无所谓,但是随着新语言的不断喷涌而出,大佬儿们发现如果不再进步,极有可能就被消灭,所以c++11,以及以后不断推出的版本更新,都是在拥抱变化。
你不变化,变化就淘汰你!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值