跳转至

迭代器模式

英文: Iterator pattern.

描述

迭代器类似自定义的指针, 拥有和指针类似的使用方式, 但却允许不同数据结构以相同的方法进行遍历.

以数组和链表为例, 数组的元素在内存中是紧密相连的, 但链表在内存中不是紧密相连的, 因此两者获取下一个元素的方法不同. 迭代器则用于封装这部分代码, 使不同数据结构可以使用相同的方法进行遍历.

下面简单实现了一个数组并提供了迭代器. 由于此时迭代器的行为和指针最接近, 所以代码最简单易懂.

#include <cstddef>
#include <iostream>
#include <numeric>

// 堆上的数组
class Array {
public:
    using value_type = int;
    class iterator {
    public:
        using value_type = Array::value_type; // 提供类系信息, 供 STL 算法使用

        iterator(value_type* ptr) : ptr(ptr) {}

        // 模拟指针的(部分)操作
        bool        operator==(const iterator& rhs) const { return ptr == rhs.ptr; }
        bool        operator!=(const iterator& rhs) const { return !(*this == rhs); }
        iterator&   operator++() { ptr++; return *this; }
        iterator    operator++(int) { auto t = *this; ++(*this); return t; }
        value_type& operator*() { return *ptr; }

    private:
        value_type* ptr;
    };

    explicit Array(std::size_t size)
        : data(new value_type[size * sizeof(value_type)]), size(size)
    {}

    virtual ~Array() { delete[] data; }

    iterator begin() { return iterator(data); }
    iterator end() { return iterator(data + size); }

private:
    value_type* data;
    std::size_t size;
};

int main() {
    Array arr(5);

    std::iota(arr.begin(), arr.end(), 0); // 使用 C++ STL 算法

    // 当作指针使用
    for(auto it = arr.begin(); it != arr.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << '\n';

    // 使用 C++ for-each
    for(auto it : arr) {
        std::cout << it << " ";
    }
    std::cout << '\n';

    return 0;
}

执行结果:

0 1 2 3 4 
0 1 2 3 4

可见实现了迭代器不仅能使遍历方法保持一致, 而且还能配合 C++ 的 STL 和 for-each 使用.

上面容器使用的数据结构是数组, 但可以换成链表等其他数据结构.

STL 算法

但上方这种完全自定义的迭代器却不能很好的适应 STL 算法.
下面提供一种迭代器算法的简单实现:

template <typename Iter>
auto distance(Iter first, Iter last) {
    typename Iter::value_type result = 0;
    for(; first != last; ++first) {
        ++result;
    }
    return result;
}

上面的代码即适用于数组, 又适用于链表等其他数据结构实现的容器. 使用迭代器进行遍历时, STL 算法无需在意容器的内部实现.
但若用在数组等结构上会存在效率问题, 因为数组元素紧密相连的特点, 实现 distance 有更加简单/高效的算法. 可以根据数据结构的特性对其迭代器进行分类, 以便采用合适的算法.
下面是 std::distance 的一种实现:

template<std::input_or_output_iterator Iter>
constexpr std::iter_difference_t<Iter> distance(Iter first, Iter last) {
    if constexpr(std::random_access_iterator<Iter>) {
        return last - first;
    } else {
        std::iter_difference_t<Iter> result{};
        for (; first != last; ++first)
            ++result;
        return result;
    }
}

可以看出改算法根据迭代器的类型选择了合适的具体算法.

参见

评论