算法与容器联系的纽带:迭代器(Iterator)

1. 算法与容器之间的关系

算法看不见容器, 对其一无所知, 因此它需要的信息都必须从iterator中获取;
所以iterator必须能够回答algorithm的所有提问, 才能把容器和算法联系起来.
在这里插入图片描述

2. iterator的种类

iterator的种类一共有5种, 分别是:

  1. input_iterator_tag
  2. output_iterator_tag
  3. forward_iterator_tag
  4. bidirectional_iterator_tag
  5. random_access_iterator_tag

继承关系如下图:
在这里插入图片描述
可以用下面代码进行实验测试:

#include <iostream> // std::cout
#include <iterator> // std::iterator_traits
#include <typeinfo> // typeid
#include <array>
#include <vector>
#include <list>
#include <forward_list>
#include <deque>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;

namespace sanjay
{
//以下5个方法都是重载, 虽然有些是继承关系, 但是也视为函数签名不一样, 同时只需要cout, 因此不需要拿到参数, 不需要在形参中声明变量
void display_category(random_access_iterator_tag)
{
    cout << "random_access_iterator" << endl; //随机访问迭代器, 继承自bidirectional_iterator
}
void display_category(bidirectional_iterator_tag)
{
    cout << "bidirectional_iterator" << endl; //双向迭代器, 继承于前向迭代器
}
void display_category(forward_iterator_tag)
{
    cout << "forward_iterator" << endl; // 前向迭代器, 继承于input_iterator
}
void display_category(output_iterator_tag)
{
    cout << "output_iterator" << endl;
}
void display_category(input_iterator_tag)
{
    cout << "input_iterator" << endl;
}

template <typename I>
void display_category(I itr)
{
    typename iterator_traits<I>::iterator_category cagy; //使用typename的原因, 使用typename 说明后面的是类型, 
    display_category(cagy); //调用前面5个函数

    cout << "typeid(itr).name()= " << typeid(itr).name() << endl;
    cout << endl;
}

void test_iterator_category()
{
    cout << "\ntest_iterator_category().......... \n";
    cout << endl;

    display_category(array<int, 10>::iterator());    //random
    display_category(vector<int>::iterator());       //random
    display_category(list<int>::iterator());         //bidirectional
    display_category(forward_list<int>::iterator()); //forward
    display_category(deque<int>::iterator());        //random

    display_category(set<int>::iterator());
    display_category(map<int, int>::iterator());
    display_category(multiset<int>::iterator());
    display_category(multimap<int, int>::iterator());
    display_category(unordered_set<int>::iterator()); //forward
    display_category(unordered_map<int, int>::iterator());
    display_category(unordered_multiset<int>::iterator());
    display_category(unordered_multimap<int, int>::iterator());

    display_category(istream_iterator<int>());
    display_category(ostream_iterator<int>(cout, ""));
}
} // namespace sanjay

int main()
{
    sanjay::test_iterator_category();

    return 0;
}

输出结果如下:

test_iterator_category().......... 

random_access_iterator
typeid(itr).name()= Pi

random_access_iterator
typeid(itr).name()= N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE

bidirectional_iterator
typeid(itr).name()= St14_List_iteratorIiE

forward_iterator
typeid(itr).name()= St18_Fwd_list_iteratorIiE

random_access_iterator
typeid(itr).name()= St15_Deque_iteratorIiRiPiE

bidirectional_iterator
typeid(itr).name()= St23_Rb_tree_const_iteratorIiE

bidirectional_iterator
typeid(itr).name()= St17_Rb_tree_iteratorISt4pairIKiiEE

bidirectional_iterator
typeid(itr).name()= St23_Rb_tree_const_iteratorIiE

bidirectional_iterator
typeid(itr).name()= St17_Rb_tree_iteratorISt4pairIKiiEE

forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorIiLb1ELb0EEE

forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEE

forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorIiLb1ELb0EEE

forward_iterator
typeid(itr).name()= NSt8__detail14_Node_iteratorISt4pairIKiiELb0ELb0EEE

input_iterator
typeid(itr).name()= St16istream_iteratorIicSt11char_traitsIcElE

output_iterator
typeid(itr).name()= St16ostream_iteratorIicSt11char_traitsIcEE

需要注意的是:

  1. unordered类型的容器的迭代器类型是forward_iterator类型的
  2. istream_iterator<int>()ostream_iterator<int>(cout, "")都不是从标准容器实例化出来的对象, 但是也属于迭代器种类的两种, 后面会详细说明
  3. typename放在迭代器萃取机前面是因为还未能确定类型, 加了typename是给编译器说明这是个类型, 而不是变量, 具体参见这个博客

3. iterator_categore对算法的影响

如下面的distance算法, 如果iterator是random类型的, 可以直接相减, 很快就可以得到结果; 而如果不是随机访问的迭代器类型, 则需要全部遍历一遍. 如果容器内有一千万个元素, 两者之间的效率就相差很多了.
在这里插入图片描述
下面的advance算法同上:
在这里插入图片描述

3.1 新出现的type traits

标准库里面的copy算法如下, 左边的代码是传入参数为InputIterator泛化模板, 然后流程是这样的:

  1. copy函数传入的参数如果是const char*或者是const wchar_t*类型, 则进入特化流程, 调用底层库memmove算法进行复制

  2. 如果不是上面情况, 进入泛化, 泛华之中又包含3个流程

    • 如果为T*或者const T*, 通过type traits来获取赋值运算符是否重要(重要与否决定后续是否需要调用析构函数), 这个过程又称为强化
    • 如果是InputIterator的话, 再分两个流程, 如果是random的话采用for(...;n < last-first;...), 这样速度更快; 否则用for(...; first!=last;...)
      在这里插入图片描述

类似的例子还有destroy函数:
在这里插入图片描述
在这里插入图片描述
还有__unique_copy:
在这里插入图片描述

4. 算法源码中对iterator_category的"暗示"

比如标准库算法中的sort, 采用的是快速排序, 而快排需要随机访问迭代器(因为快排需要交换不同位置的元素), 如下图所示, sort算法的类模板中的模板参数命名为RandomAccessIterator, 虽然换成其他名字也可以, 但是这是在告诉你需要传入随机访问迭代器.
很显然, list这种就不能用sort算法, 只能用其自身实现的sort.
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zedjay_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值