C++ 一文看懂“迭代器”!

关注“杜明c”,每天进步一点点!

 

我们常常可以在各个面向对象的语言里看到迭代器这个词,听起来抽象,用起来简单,通过使用迭代器就能简单的提取出一个集合中的元素,而不必关心数据的底层储存方式。这往往却让人对他的本质模糊不清,那么迭代器究竟是什么东西,本文就来探究这个问题。

 

 

摘要

 

  • 迭代器的应用

  • 猜测

  • 探究

  • 总结

 

 

 

迭代器的应用

 

我们先看两个使用迭代器的例子。

python

 # python
 # nums称为一个可迭代对象
 nums = [1,2,3,4]
 for i in nums:
     print(i)# 历遍nums,输出数据
     
 it = iter(nums)
 print(next(it))
 print(next(it))
 ----out----
 1
 2
 3
 4
 1
 2
 

C++

// C++
list<int> nums = {1,2,3,4};  //创建一个容器装int元素
list<int>::iterator iter;  //创建相应的迭代器
for(iter = nums.begin();iter!=nums.end();iter++)
{
    cout<<*iter<<endl;  //历遍输出元素
}
----out----
1
2
3
4

 

猜测

 

看到上面两个例子,你认为迭代器是什么?

  1. 迭代器是一个容纳元素的容器

  2. 迭代器是一个指针

在最初接触这个概念的时候,我们往往想到迭代器可能就是一个容器,但是对指针比较熟悉的话,首先会认为迭代器就是一个指针。那么究竟是不是呢?我们可以试验一下。

 

 

探究

 

在的连续的,线性的储存结构里,迭代器是怎么工作的呢?

//我们看一下vector容器,这是一个连续的,线性的容器,如同数组
vector<int> nums = {1,2,43,1};  //创建一个容器装int元素
vector<int>::iterator iter;  //创建相应的迭代器
for(iter = nums.begin();iter!=nums.end();iter++)
{
    cout<<*iter<<endl;  //历遍输出元素
}

 

他在内存中的储存结构表示如下

 

我们可以看到,vector容器在内存中的储存方式和数组一样,我们在访问数据的时候可以直接通过下标,通过地址来访问。在这样的储存结构下,迭代器的本质就是一个指针。

我们可以浏览一下,vector容器中迭代器的实现。

//可以自己试着写一个简化的vector容器
template<typename T>
class vector_dm
{
public:
  vector_dm(){  //数据初始化
    max_number = 10;
    head = (T*)malloc(sizeof(T) * max_number);
    tail = head;
    size = 0;
  }
  
  void push_back(T num);  //元素入栈
  int pop_back();  //元素出栈
  void remove(T* iter);
  T* begin();  //迭代器返回一个数据类型的指针
  T* end();   //迭代器返回一个数据类型的指针
  typedef T* iterator;  //迭代器直接使用指针实现
private:
  int size;  //储存元素的数量
  T* head;//头部指针
  T* tail;//结束指针
  int max_number;//当前最大可储存的元素数量
  void expansion();//内存扩展函数
};
​
template<typename T> T* vector_dm<T>::end()
{
  return head + size;
}
template<typename T> T* vector_dm<T>::begin()
{
  return head;
}

 

但是,在链式储存结构里,迭代器使用指针来描述,就不是特别的准确。

我们可以看到链式储存结构,元素以一个个结点作为单位,每个结点除了包含数据本身,还包括指向前后结点的指针。

 

那么迭代器在链式结构的list中具体代表什么呢?

list<int> nums;
list<int>::iterator iter;  //创建相应的迭代器
iter = nums.begin();
iter = nums.end();

 

 

我们可以看到这时候迭代器还是一个指针,只是从指向数据变成了指向数据结构体,但是他又不同于寻常意义上的指针,因为我们使用解引用运算符,就可以直接提取出数据,而不是提取出一个结构体。

了解了他的原理,我们可以同样尝试使用指针去描述迭代器。

struct node {
  T node_data;//特定的数据类型
  struct node* next;
  struct node* previous;
};
struct node*iterator = nums.begin();//指向一个结点
​
//提取出结构体中的数据
T NEW_T = *(T*)iterator;//强制类型转换
​
//这里有一个小技巧,结构体的首地址和首元素的地址相同,所以直接使用指针强制转换可以得到数据本身。

 

但是这显然不是我们想要的,一个优秀的开发者应该把对象的特性封装好,提取出相关的函数和特性,而不是期望用户了解中间的实现原理。

所以最后一步很简单,我们需要把指针封装成对象,那他就是一个真正意义上的迭代器了,这是一个简单的list容器内部迭代器的实现。重新封装了关于迭代器的方法。

  • 解引用*——返回特定类型的数据

  • +——迭代器的加法,指向下一个结点

  • ==,!= ——逻辑判断

//c++
struct node {
  T node_data;
  struct node* next;
  struct node* previous;
};
class iterator {
public:
  struct node* p_node;
  iterator(struct node* new_p_node)
  {
    p_node = new_p_node;
  }
  iterator()
  {
    p_node = NULL;
  }
  bool operator==(const iterator& x) { return p_node == x.p_node; }
  bool operator!=(const iterator& x) { return p_node != x.p_node; }
  iterator operator+(int n)
  {
    iterator iter;
    iter.p_node = this->p_node->next;
    this->p_node = this->p_node->next;
    return iter;
  }
  iterator operator++(int)
  {
    iterator iter;
    if (this->p_node == NULL)    return iter;
    iter.p_node = this->p_node->next;
    return iter;
  }
  iterator operator++()
  {
    iterator iter;
    if (this->p_node == NULL)    return iter;
    iter.p_node = this->p_node->next;
    return iter;
  }
  T operator*()
  {
    if (!p_node)  return NULL;
    return p_node->node_data;
  }
private:
};
iterator begin()
{
  iterator iter = iterator(head);
  return iter;
}
iterator end()
{
  iterator iter = iterator(NULL);
  return iter;
}

 

 

总结

 

那么迭代器是不是指针呢,其实是,也不是,这取决于集合元素的数据存储方式。迭代器更应该描述为一个指针运算集,他是一个对象,不只是简单意义上的指针,通过迭代器内置的运算实现历遍集合,提取元素这一系列的功能,

 

 

End

    杜明C语言

   专注C/C++

每天进步一点点

微信关注

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值