第三章 迭代器 3.1 什么是迭代器

3.1 什么是迭代器

迭代器即指针,可以是所需要的任意类型,它的最大好处是可以使容器和算法分离

       为了提高C++编程的效率,STL中提供了许多容器,包括vector、list、map等。为统一访问方式,STL为每种容器在实现的时候设计了一个内嵌的iterator类,不同的容器有自己专属的迭代器,使用迭代器来访问容器中的数据

      迭代器对一些基本操作如*、–、++、==、!=进行了重载,使其具有了遍历复杂数据结构的能力,其遍历机制取决于所遍历的容器,迭代器的使用和指针的使用非常相似。通过begin,end函数获取容器的头部和尾部迭代器,当begin和end返回的迭代器相同时表示容器为空。简之,迭代器就是一个遍历的过程,像使用指针一样使用迭代器就可以访问这个容器,不因容器不同而异。迭代器有三成员,但都是指针。并且一个容器里面会存在两个迭代器,一个是起始迭代器(begin),一个是终止迭代器(end)

例如,有两个容器类:MyArray 是某类型数组集合;MyLink 是某类型链表集合。

它们都有显示、查询和排序等功能,常规思维是每个容器类中有自己的显示、查询和排序等函数,不同容器中完成相同功能代码的思路大体是相同的。把它们抽象出来,多个容器仅对应一个显示,一个查询、一个排序函数

 为数组容器、链表容器编制共同显示函数

这段代码实现了一个简单的动态数组类,通过模板参数 T 可以使用不同的数据类型。它提供了向数组中添加数据、获取数组长度和获取指定位置元素值的功能。

MyArray 单项链表类初始代码如下所示

#include<stdio.h>

template<class T>//模板参数 T 表示数组元素的类型

class MyArray{//定义了一个模板类 MyArray

private:

   int m nTotalsize;//数组总长度

   int m nValidSize;//数组有效长度

   T* m pData;//数据

public:

   MyArray(int nSize= 3) {//数组默认总长度是 3

     m_pData=new TInSize];

    m_nTotalSize=nSize;

    m_nValidSize=0;

  }

    void Add(T value)  {//向m_pData添加数据

       // 书上例二

}

int GetSize(){ //返回数组有效长度

  return m_nValidsize;//即返回实际存储的元素个数。

 }

T Get(int pos)//Get 函数用于获取指定位置 pos 处的元素值,并将其作为返回值返回

{

      return m_pData[pos];

    }

    virtual~MyArray()  {//析构函数 ~MyArray 负责释放动态分配的数组内存

     if(m_pData!=NULL){

 delete []m_pData;//如果已分配内存,则使用 delete[] 关键字释放内存

 m_pData=NULL;//m_pData 设置为 NULL,表示释放完成

}}

};

MyLink 单项链表类初始代码如下所示

template <class T>

//这里定义了一个结构体 Unit,表示链表中的单元。

//它包含了一个模板参数 T 表示单元的值类型

struct Unit  // 链表单元

{

    T value;       // value表示单元值

    Unit* next;    // 下一个单元的指针

};

template <class T>

//定义了一个链表类 MyLink,使用模板参数 T 表示链表单元的值类型

class MyLink{

    Unit<T>* head;  // 链表头指针

    Unit<T>* tail;  // 链表尾指针

    Unit<T>* prev;  // 前一个单元的指针

public:

//构造函数 MyLink() 用于初始化链表的头、尾和前一个单元指针,将它们都设为 NULL

    MyLink(){

        head = tail = prev = NULL;  }

    void Add(T& value)  //Add函数用来向链表中添加元素

    {

        Unit<T>* u = new Unit<T>();  // 创建一个新的链表单元

        u->value = value;            // 将值赋给新的单元

        u->next = NULL;              // 新的单元的下一个指针为空

        if (head == NULL)  // 链表为空

        {

            head = u;     // 将头指针指向新的单元

            prev = u;     // 将前一个单元指针指向新的单元

        }

        else {

            prev->next = u;  // 将前一个单元的下一个指针指向新的单元

            prev = u;        // 更新前一个单元指针为新的单元

        }

  tail = u;    // 更新尾指针tail 为新的单元

    }

    //析构函数 ~MyLink 负责释放链表中的内存

    virtual ~MyLink() {

        if (head != NULL)  {

//这两行代码声明并初始化两个指针变量 prev 和 next。

//prev 指向链表的头指针 head,next 初始化为 NULL。

            Unit<T>* prev = head;

            Unit<T>* next = NULL;

            while (prev != tail)  { // 循环的目的是释放链表中每个单元所占用的内存。

//循环会遍历链表中的每个单元,直到 prev 指针指向尾指针 tail。

                next = prev->next;  // 将当前单元的下一个指针赋值给 next。

//将 prev 指针指向的单元的下一个单元的地址赋值给 next

                delete prev;        // 删除当前单元prev 所占用的内存。

                prev = next;        // 更新 prev 指针为下一个单元的地址,

//即将 next 的值赋给 prev,使 prev 指针指向链表中的下一个单元

            }}}

};

MyLink 是模板元素 T的链表类,以 struct Unit 为一个个链表单元。

下面是以MyArray、MyLink 为基础完成一个共同显示函数

从需要出发,逆向考虑,写一个泛型显示函数。

其中display 函数与具体的容器无直接关联,间接关联是必需的。

对于 MyArray 来Init 相当于对 T* 的操作;对于 MyLink 来说,Init 相当于对 Unit<T> * 的操作。因此引出了迭代器类,它仍然是一个模板类,对于本示例而言,模板参数是 T*或 Unit<T>*

template<class Init>//模板参数类型 Init 是一个指针

//函数名为 display,接受两个参数 start 和 end,它们都是类型为 Init 的指针

void display(Init start, Init end)//start 是起始指针,end 是结束指针

{

   cout<<end1;

   for(Init mid=start; mid!=end; mid++){

      cout<< *mid<<"\t";}//输出指针 mid 所指向的值。*mid 表示解引用指针 mid,获取它所指向的值

      cout<<end;//输出end的值

  }

MyArray 对应的迭代器 ArrayIterator 类为下述代码

template<class Init>

class ArrayIterator{

   Init*init;

public:

   ArrayIterator(Init *init){

   this->init=init;

   }

   bool operator!=(ArrayIterator& it)

    {

return this->initl!=it.init;}

void operator++(int){

  init++;}

Init operator * ()

{

return *init;}};

ArrayIterator确实是对 Init * 的再封装,必须重载!=十十、* 操作符,这是由于 display 泛型显示函数用到这些操作符

需要在 MyArray 类中增加两个函数 Begin,End,用以获得起止送代指针

如下所示,过程可描述为:定义一个MyArray 对象 ary,向其中添加了 5个数据(1~5),获得起止。

迭代指针对象 startend,最后调用泛型显示函数 display 完成数据的输出。

创建了一个整数数组对象 ary,并使用 Begin 和 End 函数获取数组的起始和结束位置指针。

通过迭代器对象 start 和 end 指定了要遍历的范围,并调用 display 函数输出数组元素

//这是一个成员函数 Begin 的定义,返回类型为 T*,即指向类型 T 的指针。

//该函数用于返回数组的起始位置指针 m_pData

T* Begin() //起始选代指针

{

   return m_pData;

}

//该函数用于返回数组的结束位置指针,起始位置指针 m_pData 加上有效元素数量 m_nValidSize

T*End()//结束选代指针

{

  return m_pData+m_nValidsize;

}

int main(){

   MyArray<int>ary;//创建了一个名为 ary 的 MyArray 类型对象,表示一个整数数组

   for(int i=0;i<5;i++)

   {

     ary.Add(i+1):

   }

//创建了两个名为 start 和 end 的 ArrayIterator<int> 类型对象,分别使用 ary.Begin() 和 ary.End() 初始化。

//这些对象用于指定迭代的起始和结束位置

   ArrayIterator<int>start(ary.Begin()):

   ArrayIterator<int>end(ary.End()):

   cout<<"数组元素为:";

//使用 display(start, end) 函数来输出数组元素

   display(start,end);

   return 0;

   }

同理,可知完成MyLink 选代功能步骤

(1)在MyLink 类中增加 Begin()End(),用以获取起止选代指针

Unit<T>*Begin(){

    return head;//链表头指针

}

Unit<T>* End(){//链表尾指针

    return tail;

}

(2)增加链表选代器类 LinkIterator,重载!=++、* 运算符

template<class Init>//模板参数为 Init,可以是任意类型

class LinkIterator{

   Init * init;//这行代码声明了一个指针成员变量 init,类型为 Init*,即指向 Init 类型的指针。

public:

   LinkIterator(Init *init){//这是构造函数的定义,接受一个 Init* 类型的指针作为参数,

   //并将其赋值给成员变量 init

   this->init=init;}

   bool operator!= (LinkIterator& it){//重载了 != 运算符

     return this->init!=it.init;}//接受一个 LinkIterator 类型的引用 it 作为参数,并比较成员变量 init 和 it.init 的值是否不相等

   void operator++(int){//重载了后置递增运算符 ++ 的函数定义

     init=init->next;}

   Init operator * (){//重载了解引用运算符 * 的函数

      return * init;}

};

可以看出operator!=operator * 重截内容与 ArrayIterator 中的内容是相同的,只operator十十中的内容不同。

对于链表而言,不像数组那样内存是连续的,是指针的转向因此绝对不能写成init=init十十,只能写成 init-init一>next

(3)重载全局函数 operator <<。

template<class T>

ostreams operator<<(ostream& os,Unit<T>& s){//这是函数的定义,它接受两个参数:一个输出流对象 os 和一个 Unit<T> 类型的引用 s

    os<<s.value;//将 s.value 的值输出到输出流 os 中

    return os;}//返回支持链式输出

这是因为当执行 display 函数中的 cut<<*mid 指时,对链表而言表意形式相当cout<<* Unit<int>,即输出 Unit<int>对象。

由于 Unit 是复合数据类型,不能直接出,因此必须重载上述函数,在其中输出简单数据类型 svalue

(4)链表类测试函数如下:

int main(){

  int m=0;

  MyLink<int>ml;//创建一个 MyLink<int> 类型的对象 ml,表示一个整数链表

  for(int i=0;i<5;i++){//将整数 1 到 5 添加到链表中,通过调用 ml.Add(m) 实现

    m=i+1;

    ml.Add(m);}

使用 LinkIterator<Unit<int>> 类模板创建了两个迭代器对象 start 和 end,分别使用 ml.Begin() 和 ml.End() 初始化。

这些对象用于指定链表的迭代范围  

LinkIterator<Unit<int>>start (ml,Begin());

  LinkIterator<Unit<int>>end(ml.End());

  display(start,end);//调用 display(start, end) 函数来输出链表元素

  retrun 0;

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值