【C++】从特定容器的迭代器到实现统一的抽象迭代器接口类(Iterator)

迭代器(Iterator)概念详解

📌 什么是迭代器?

迭代器是一种抽象的设计模式,它提供了一种统一的方式来访问和遍历容器中的元素。一个迭代器可以是一个任意的对象,它指向某个范围内的元素(如数组、链表、树等),并通过实现一些基本操作来逐个访问这些元素。


🧩 迭代器的简单例子

示例 1:用下标变量模拟迭代器功能

int a[5] = {1, 2, 3, 4, 5};
int sum = 0;

for (int i = 0; i < 5; i++) {
    sum += a[i];
}
  • 变量 i 是一个整型索引,但它起到了类似迭代器的作用 —— 遍历数组中的每个元素。

示例 2:用指针模拟迭代器功能

Node *p = l.head;
while (p) {
    // 使用 p->data 访问当前节点的数据
    p = p->next;
}
  • 指针 p 遍历了一个链表中的所有节点,它也具有类似于迭代器的功能。

🛠️ 如何为特定容器实现迭代器?

对于一个特定的容器类型(自定义类型),我们可以设计并实现一个专门的迭代器类:

✅ 只能用于容器Array的迭代器实现

#include <iostream>
#include <ostream>

using namespace std;

// 模板类 Array:支持非类型参数 n,表示数组的固定容量
template<typename T, int n>		
class Array
{
private:
    T* arr;       // 动态分配的数组指针
    int capcity;  // 数组容量(最大可容纳元素个数)
    int size;     // 当前实际元素个数

public:
    // 构造函数:根据模板参数 n 分配空间
    Array() : arr(nullptr), capcity(n), size(0)
    {
        if (capcity > 0)
        {
            arr = new T[capcity];
        }
    }

    // 拷贝构造函数:深拷贝
    Array(const Array<T, n>& a) : arr(nullptr), capcity(a.capcity), size(a.size)
    {
        if (capcity > 0)
        {
            arr = new T[capcity];

            for (int i = 0; i < size; ++i)
            {
                (*this)[i] = a[i];  // 使用下标操作符赋值
            }
        }
    }

    // 析构函数:释放动态分配的空间
    ~Array()
    {
        if (arr)
        {
            delete[] arr;
        }
    }

    // 下标运算符重载(非 const 版本)
    T& operator[](const int& i)
    {
        if (i >= 0 && i < size)
        {
            return arr[i];
        }
    }

    // 下标运算符重载(const 版本)
    const T& operator[](const int& i) const
    {
        if (i >= 0 && i < size)
        {
            return arr[i];
        }
    }

    // 插入元素到数组末尾
    bool insert(const T& d)
    {
        if (size >= capcity)
        {
            return false;  // 容量已满,插入失败
        }

        arr[size++] = d;
        return true;
    }

    // 获取当前数组中实际元素个数
    int length(void) const
    {
        return size;
    }

    // << 输出运算符重载(友元函数)
    template<typename T1, int n1>
    friend ostream& operator<<(ostream& o, const Array<T1, n1>& a);
    
    // 迭代器类:用于遍历 Array 数组中的元素
    class ArrayIterator
    {
    private:
        T* _arr;      // 指向数组起始位置
        int _size;    // 遍历范围内的元素个数
        int index;    // 当前下标

    public:
        ArrayIterator(T* _start, int _s) : _arr(_start), _size(_s), index(0)
        {}

        // 将迭代器重置为指向第一个元素
        void begin(void)
        {
            index = 0;
        }

        // 返回当前元素引用
        T& operator*(void)
        {
            return _arr[index];
        }

        // 前缀自增运算符:移动到下一个元素
        ArrayIterator& operator++()
        {
            index++;
            return *this;
        }

        // 判断是否到达结尾
        bool is_end(void)
        {
            return index == _size;
        }
    };

    // 获取默认迭代器(从头开始,遍历全部元素)
    ArrayIterator get_Iterator(void)
    {
        return ArrayIterator(arr, size);
    }

    // 获取指定起点和数量的迭代器
    ArrayIterator get_Iterator(const int& first, const int& num)
    {
        return ArrayIterator(arr + first, num);
    }
};

// << 输出运算符重载实现
template<typename T, int n>
ostream& operator<<(ostream& o, const Array<T, n>& a)
{
    for (int i = 0; i < a.length(); ++i)
    {
        o << a[i] << "  ";
    }
    o << endl;
    return o;
}

int main()
{
    // 创建一个容量为10的整型数组
    Array<int, 10> a;

    // 插入7个元素
    a.insert(1);
    a.insert(3);
    a.insert(5);
    a.insert(7);
    a.insert(9);
    a.insert(11);
    a.insert(13);

    // 输出整个数组
    cout << a << endl;
    // 输出:
    // 1  3  5  7  9  11  13  

    cout << "--------------------" << endl;

    // 获取默认迭代器
    auto it = a.get_Iterator();

    // 使用迭代器遍历数组
    for (it.begin(); !it.is_end(); ++it)
    {
        cout << *it << "  ";
    }
    cout << endl;
    // 输出:
    // 1  3  5  7  9  11  13  

    // 获取偏移1、长度6的迭代器
    Array<int, 10>::ArrayIterator itr = a.get_Iterator(1, 6);
    for (itr.begin(); !itr.is_end(); ++itr)
    {
        cout << *itr << "  ";
    }
    cout << endl;
    // 输出:
    // 3  5  7  9  11  13  
}

🔁 迭代器的工作原理

迭代器的本质是封装了“如何访问数据”的逻辑。它不关心底层容器的具体结构(数组、链表、哈希表等),而是通过统一接口来访问元素。

  • 在软件构建中,集合对象的内部结构常常变化各异, 如:某种集合可能是数组实现,也可能是用链表实现的 …
  • 对于这些集合对象, 我们希望在不暴露其内部结构的同时, 可以让外部用户透明的去访问其中包含的元素
  • 同时 这种"透明遍历" 也为同一个算法在多个集合对象进行操作的提供了可能 ,使用面向对象技术将这种遍历机制抽象为 “迭代器对象” ,为遍历"变化中的集合对象"提供了一种很好的方式
  • 迭代器模式:
    提供一种方法 顺序访问一个聚合对象中的每一个元素,而又不暴露该对象的内部结构的表示

现在将之前实现的 ArrayIterator 类,改写为从一个统一的抽象迭代器接口类 Iterator<T> 派生出来。


纯抽象类的迭代器接口

✅ 第一步:定义基类

template<typename T>
class Iterator {
public:
    virtual void begin() = 0;           // 将迭代器重置到起始位置
    virtual bool end() const = 0;       // 判断是否到达结束位置
    virtual Iterator& operator++() = 0; // 前缀 ++ 运算符,移动到下一个元素
    virtual T& operator*() const = 0;   // 解引用操作符,返回当前元素的引用

    virtual ~Iterator() = default;      // 虚析构函数,确保派生类析构安全
};

✅ 第二步:修改 ArrayIterator `

我们将原来的嵌套类 ArrayIterator 改写为继承自 Iterator<T>,并实现所有纯虚函数。

📦 定义如下:

// 数组Array的"迭代器类",继承自 Iterator<T>
template<typename T>
class ArrayIterator : public Iterator<T> {
private:
    T* _arr;         // 指向遍历范围的首元素
    int _size;       // 遍历范围内元素个数
    int index;       // 当前下标

public:
    ArrayIterator(T* start, int size)
        : _arr(start), _size(size), index(0) {}

    // 重置为第一个元素
    void begin() override {
        index = 0;
    }

    // 是否到达末尾
    bool end() const override {
        return index == _size;
    }

    // 前缀 ++it
    Iterator<T>& operator++() override {
        ++index;
        return *this;
    }

    // *it 返回当前元素的引用
    T& operator*() const override {
        return _arr[index];
    }
};

✅ 第三步:更新 Array

ArrayIterator<T> get_Iterator() {
    return ArrayIterator<T>(arr, size);
}

✅ 第四步:使用

使用统一的接口:

int main() {
    Array<int, 10> a;

    a.insert(1);
    a.insert(3);
    a.insert(5);
    a.insert(7);
    a.insert(9);
    a.insert(11);
    a.insert(13);

    cout << a << endl;
    cout << "--------------------" << endl;

    // 使用统一的迭代器接口
    ArrayIterator<int> it = a.get_Iterator();

    for (it.begin(); !it.end(); ++it) {
        cout << *it << "  ";
    }
    cout << endl;
    // 输出: 1  3  5  7  9  11  13  

    // 使用偏移迭代器
    ArrayIterator<int> itr = a.get_Iterator(1, 6);

    for (itr.begin(); !itr.end(); ++itr) {
        cout << *itr << "  ";
    }
    cout << endl;
    // 输出: 3  5  7  9  11  13  
}

🎯 补充说明

特性说明
统一接口不同容器都可以使用相同的迭代器接口进行访问,便于泛型编程。
STL 支持C++ STL 中几乎所有容器都支持迭代器,例如 std::vector, std::list, std::map 等。
增强可维护性容器与算法分离,提高了代码的复用性和可维护性。
避免越界访问迭代器可以通过边界检查等方式提升安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值