C++ 手写实现迭代器

C++ 迭代器:原理 + 使用方法

一、迭代器是什么?(原理)

迭代器 = 智能指针 = 容器的"通用遍历工具"

你可以把它理解成:

  • 数组的指针
  • 链表的节点指针
  • 字符串的字符指针
  • 甚至文件、流、数据库的"游标"

迭代器统一了所有容器的遍历方式

不管底层是数组、链表、树、哈希表,你遍历的写法都一样:

cpp 复制代码
for (迭代器 it = 容器.begin(); it != 容器.end(); ++it)
    使用 *it

这就是迭代器的核心价值屏蔽底层差异,提供统一接口


二、迭代器的原理

迭代器就是一个对象,它知道:

  1. 当前在哪(指向某个元素)
  2. 怎么走到下一个(++it)
  3. 怎么取出数据(*it)
  4. 什么时候结束(it == end())

它本质就是对指针的封装,让指针行为更安全、更通用。


三、迭代器长什么样?

这就是迭代器的灵魂结构

cpp 复制代码
template <typename T>
struct iterator {
    T* _ptr;           // 指向元素

    T& operator*() {   // 取内容
        return *_ptr;
    }

    iterator& operator++() { // 往前走
        _ptr++;
        return *this;
    }

    bool operator!=(const iterator& other) {
        return _ptr != other._ptr;
    }
};

是不是超级简单?

迭代器 = 指针 + 三个运算符


四、迭代器的使用方法(最常用 4 种)

1. 最标准遍历(所有容器通用)

cpp 复制代码
vector<int> v = {1,2,3};

for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it << endl;
}

规则:

  • begin() → 指向第一个元素
  • end() → 指向最后一个元素的下一个位置(不存数据)
  • ++itit++ 快(推荐用前置)
  • 判断用 != 不用 <(链表不能用 <)

2. C++11 范围for(最简单)

底层自动调用迭代器

cpp 复制代码
for (auto x : v) { ... }

等价于:

cpp 复制代码
for (auto it = v.begin(); it != v.end(); ++it)
    auto x = *it;

3. 只读遍历(const_iterator)

不想修改数据时用,更安全:

cpp 复制代码
for (auto it = v.cbegin(); it != v.cend(); ++it)

或:

cpp 复制代码
for (const auto& x : v)

4. 反向遍历(reverse_iterator)

从后往前:

cpp 复制代码
for (auto it = v.rbegin(); it != v.rend(); ++it)

五、迭代器的 5 种分类(必须懂)

按功能强弱排序:

  1. 输入迭代器:只读一次(istream)
  2. 输出迭代器:只写一次(ostream)
  3. 正向迭代器:只能 ++(单向链表)
  4. 双向迭代器:++ 和 --(list、set、map)
  5. 随机访问迭代器:最强,支持 +、-、\[\]、<(vector、string、deque)

越往下功能越强!


六、迭代器常用操作

cpp 复制代码
*it     取元素
it->    取成员(对象用)
++it    下一个
--it    上一个(双向迭代器以上)
it += n 跳 n 个(随机访问)
it - it 距离(随机访问)
it == it
it != it

七、为什么要用迭代器?

  1. 统一遍历语法

    数组、链表、map、set 遍历写法一样

  2. 不暴露容器内部结构

    你不用知道内部是数组还是链表,迭代器帮你搞定

  3. 配合 STL 算法

    sort、find、for_each 全都靠迭代器

cpp 复制代码
sort(v.begin(), v.end());
find(v.begin(), v.end(), 10);

总结

迭代器原理

迭代器是封装了指针的对象,提供统一遍历接口。

迭代器使用

cpp 复制代码
for (auto it = 容器.begin(); it != 容器.end(); ++it)
    *it 访问元素

核心运算符(必须实现)

  • *it 取元素
  • ++it 下一个
  • != 判断结束

C++ 手写迭代器(练手版)

练手写迭代器,最简单的方式是给一个自定义数组/容器写一个「正向迭代器」 ,不用搞复杂功能,先把迭代器必须的接口、类型、运算符写对就行。

目标

我们手写一个:

  1. 自定义容器 MyVector(简单动态数组)
  2. 给它写一个自定义迭代器 MyIterator
  3. 支持:*it++it==!=->
  4. 支持 C++11 范围for for(auto x : vec)

手写一个迭代器以及自定义容器类

cpp 复制代码
#include<iostream>
#include<cstring>

using namespace std;


template<typename T>
struct MyIterator
{
    using value_type = T;
    using pointer = T*;
    using reference = T&;
    using difference_type = ptrdiff_t;
    using iterator_category = random_access_iterator_tag; 

    pointer _ptr;

    MyIterator(pointer p = nullptr) : _ptr(p) {}
    //解引用
    reference operator*(){
        return *_ptr;
    }
    //成员访问 it->xxx
    pointer operator->(){
        return _ptr;
    }
    //前置自增 ++it
    MyIterator operator++(){
        _ptr++;
        return *this;
    }
    // 后置自增 it++
    MyIterator operator++(int) {
        MyIterator temp = *this;
        _ptr++;
        return temp;
    }
    bool operator== (const MyIterator& other) const{
        return _ptr == other._ptr;
    }
    // 判不等 it1 != it2
    bool operator!=(const MyIterator& other) const{
        return !(*this == other);
    }

};
/**
 * 自定义容器:搭配迭代器使用
 */
template<typename T>
class MyVector
{
private:
    T* _data;
    int _size;
    int _capacity;
public:
    // 给容器暴露迭代器类型
    using iterator = MyIterator<T>;
    // 构造函数
    MyVector(): _data(nullptr), _size(0), _capacity(0){}
    // 添加元素
    void push_back(const T& val){
        //当容器没有空间时
        if(_size == _capacity){
            //当容量不足时,按指数级预先分配空间
            int newCap = (_capacity == 0) ? 1 : _capacity * 2;
            T* newData = new T[newCap];
            if(_data){
                //把之前的数据搬到新的空间上,这里用循环拷贝
                for (int i = 0; i < _size; ++i) {
                    newData[i] = _data[i];
                }
                delete[] _data;
            }
            _data = newData;
            _capacity = newCap;
        }
        _data[_size++] = val;
    }
    // ======================================
    // 容器必须提供:begin() + end()
    // ======================================
    iterator begin(){
        return iterator(_data);
    }

    iterator end(){
        return iterator(_data + _size);
    }

    int size() const{
        return _size;
    }
};

int main() {
    MyVector<int> vec;
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 1. 普通迭代器遍历
    cout << "iterator: ";
    for (MyVector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 2. C++11 范围for(依赖正确的 begin/end/*/++/!=)
    cout << "range-for: ";
    for (auto x : vec) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

运行输出:

复制代码
iterator: 10 20 30
range-for: 10 20 30

手写一个双向迭代器以及自定义容器类

双向迭代器 = 支持 ++it(往后) + --it(往前)

最经典的场景:链表(list),因为链表不能像数组那样随机访问,只能一步步前后跳。

核心规则(双向迭代器)

  • 必须实现:*、->、++、--、==、!=
  • 必须写 5 个标准类型别名
  • 迭代器类型标记:bidirectional_iterator_tag
  • 容器必须提供 begin()、end()
cpp 复制代码
#include <iostream>
using namespace std;

// 链表节点结构
template <typename T>
struct Node {
    T val;
    Node* prev; // 前驱(支持 --)
    Node* next; // 后继(支持 ++)
    Node(T v) : val(v), prev(nullptr), next(nullptr) {}
};

// ==============================
// 手写 双向迭代器
// ==============================
template <typename T>
struct MyListIterator {
    // ==============================
    // 1. 必须写的 5 个标准类型
    // ==============================
    using value_type = T;
    using pointer = T*;
    using reference = T&;
    using difference_type = ptrdiff_t;
    using iterator_category = bidirectional_iterator_tag; // 双向迭代器!

    // 迭代器持有:当前节点指针
    Node<T>* _node;

    // 构造
    MyListIterator(Node<T>* p = nullptr) : _node(p) {}

    // ==============================
    // 2. 核心接口
    // ==============================
    // 解引用
    reference operator*() const {
        return _node->val;
    }

    // 成员访问
    pointer operator->() const {
        return &(_node->val);
    }

    // 前置 ++:往后走
    MyListIterator& operator++() {
        _node = _node->next;
        return *this;
    }

    // 前置 --:往前走(双向迭代器关键!)
    MyListIterator& operator--() {
        _node = _node->prev;
        return *this;
    }

    // 判断相等
    bool operator==(const MyListIterator& other) const {
        return _node == other._node;
    }

    bool operator!=(const MyListIterator& other) const {
        return !(*this == other);
    }
};

// ==============================
// 简易链表容器(配合双向迭代器)
// ==============================
template <typename T>
class MyList {
private:
    Node<T>* _head;
    Node<T>* _tail;

public:
    // 暴露迭代器类型
    using iterator = MyListIterator<T>;

    MyList() : _head(new Node<T>(0)), _tail(new Node<T>(0)) {
        _head->next = _tail;
        _tail->prev = _head;
    }

    // 尾插
    void push_back(const T& val) {
        Node<T>* newNode = new Node<T>(val);
        Node<T>* prev = _tail->prev;

        prev->next = newNode;
        newNode->prev = prev;
        newNode->next = _tail;
        _tail->prev = newNode;
    }

    // ==============================
    // 容器必须提供 begin / end
    // ==============================
    iterator begin() {
        return iterator(_head->next);
    }

    iterator end() {
        return iterator(_tail);
    }
};

// ==============================
// 测试双向迭代器
// ==============================
int main() {
    MyList<int> lst;
    lst.push_back(10);
    lst.push_back(20);
    lst.push_back(30);

    // 1. 正向遍历(++)
    cout << "正向 ++: ";
    for (auto it = lst.begin(); it != lst.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 2. 反向遍历(--)【双向迭代器特色】
    cout << "反向 --: ";
    auto it = lst.end();
    --it; // 从最后一个元素开始
    while (it != lst.begin()) {
        cout << *it << " ";
        --it;
    }
    cout << *it << endl;

    return 0;
}

手写一个只读迭代器

只读迭代器

  • 只能 *it 读,不能修改元素
  • 返回 const T&、const T*

随机访问迭代器

  • 支持 ++、--
  • 支持 it + n、it - n、itn
  • 支持 <、>、<=、>=
  • 类型标记:random_access_iterator_tag
cpp 复制代码
#include<iostream>
using namespace std;

template <typename T>
struct MyConstRandomIterator{
    // 1.标准五种类型
    using value_type = T;
    using pointer = const T*;
    using referece = const T&;
    using difference_type = ptrdiff_t;
    using iterator_category = random_access_iterator_tag;
    // 核心:持有指针
    pointer _ptr;

    //构造
    MyConstRandomIterator(const T* p = nullptr) : _ptr(p){}
    // ==============================
    // 2. 基础接口(只读)
    // ==============================
    referece operator*() const{
        return *_ptr;
    }
    pointer operator->() const{
        return _ptr;
    }
    //前置++
    MyConstRandomIterator& operator++(){
        _ptr++;
        return *this;
    }
    //后置++
    MyConstRandomIterator operator++(int){
        auto tmp = *this;
        _ptr++;
        return tmp;
    }
    //前置--
    MyConstRandomIterator& operator--(){
        _ptr--;
        return *this;
    }
    //后置--
    MyConstRandomIterator operator--(int){
        auto tmp = *this;
        _ptr--;
        return tmp;
    }
    // ==============================
    // 3. 随机访问核心功能
    // ==============================
    // 前进 n 步
    MyConstRandomIterator& operator+=(difference_type n){
        _ptr += n;
        return *this;
    }
    // 后退 n 步
    MyConstRandomIterator& operator-=(difference_type n) {
        _ptr -= n;
        return *this;
    }
    // it + n
    MyConstRandomIterator operator+(difference_type n)const{
        return MyConstRandomIterator(_ptr + n);
    }
    // it - n
    MyConstRandomIterator operator-(difference_type n) const{
        return MyConstRandomIterator(_ptr - n);
    }
    // 两个迭代器相差多少个元素
    difference_type operator-(const MyConstRandomIterator& other) const{
        return _ptr - other._ptr;
    }
    // 下标 访问 it[n]
    referece operator[](difference_type n) const{
        return _ptr[n];
    }
    // ==============================
    // 4. 比较运算符(随机访问需要)
    // =============================
    bool operator==(const MyConstRandomIterator& other) const{
        return _ptr==other._ptr;
    }
    bool operator!=(const MyConstRandomIterator& other) const{
        return !(*this==other);
    }
    bool operator<(const MyConstRandomIterator& other) const {
        return _ptr < other._ptr;
    }

    bool operator>(const MyConstRandomIterator& other) const {
        return _ptr > other._ptr;
    }

    bool operator<=(const MyConstRandomIterator& other) const {
        return _ptr <= other._ptr;
    }

    bool operator>=(const MyConstRandomIterator& other) const {
        return _ptr >= other._ptr;
    }
};
template<typename T>
class MyArray{
private:
    T* _data;
    int _size;

public:
    using const_iterator = MyConstRandomIterator<T>;

    MyArray(const T* arr, int n){
        _size = n;
        _data = new T[n];
        for (int i = 0; i < n; ++i)
            _data[i] = arr[i];
    }
    ~MyArray(){
        delete []_data;
    }
    int size(){
        return _size;
    }
    const_iterator begin() const{
        return const_iterator(_data);
    }
    const_iterator end() const{
        return const_iterator(_data + _size);
    }

};

// ==============================
// 测试:只读 + 随机访问
// ==============================
int main() {
    int arr[] = {10, 20, 30, 40, 50};
    MyArray<int> ma(arr, 5);

    // 1. 普通遍历
    cout << "遍历: ";
    for (auto it = ma.begin(); it != ma.end(); ++it) {
        cout << *it << " ";
        // *it = 100;  ❌ 报错!只读迭代器不能修改
    }
    cout << endl;

    // 2. 随机访问 it[n]
    cout << "it[2] = " << ma.begin()[2] << endl;

    // 3. it + 3
    auto it = ma.begin() + 3;
    cout << "begin + 3 = " << *it << endl;

    // 4. 比较大小
    if (it > ma.begin())
        cout << "it 在 begin 后面" << endl;

    return 0;
}

你必须掌握的迭代器核心

手写迭代器只需要做好这几件事

1. 必须写 5 个标准类型别名

cpp 复制代码
using value_type = T;
using pointer = T*;
using reference = T&;
using difference_type = ptrdiff_t;
using iterator_category = random_access_iterator_tag;

不写会导致:不能和 STL 算法兼容、范围for可能报错

2. 迭代器内部存一个指针

几乎所有手写迭代器都是:包一层指针

cpp 复制代码
pointer _ptr;

3. 必须实现 4 个核心运算符

  1. operator*() → 解引用
  2. operator->() → 成员访问
  3. operator++() → 移动到下一个元素
  4. operator== / != → 判断是否结束

4. 容器必须提供 begin()end()

这是迭代器能用的前提

cpp 复制代码
iterator begin() { return iterator(_data); }
iterator end() { return iterator(_data + _size); }

5. 容器必须写 using iterator = 你的迭代器

让外部能拿到迭代器类型:

cpp 复制代码
using iterator = MyIterator<T>;

6. 范围for 自动依赖

只要你写对:

  • begin()
  • end()
  • *it
  • ++it
  • it != it

范围for for(auto x : container) 自动生效!


你可以一步步扩展练手(由浅到深)

你可以按这个顺序加功能,逐步变强:

  1. 先跑通上面代码 ✅
  2. operator--() 实现双向迭代
  3. operator+= / operator[] 实现随机访问
  4. const_iterator(只读迭代器)
  5. std::sort 等算法使用(需要完整随机访问)

总结

手写迭代器其实非常简单:

  1. 迭代器 = 包装指针 + 标准运算符
  2. 容器 = 数据 + begin() + end()
  3. 5 个类型别名 + 4 个运算符 + 2 个容器方法 = 可用迭代器
  4. 写对就能支持范围forSTL 习惯用法
相关推荐
智者知已应修善业12 小时前
【51单片机按键加减1若不释放自动加减】2023-11-24
c++·经验分享·笔记·算法·51单片机
萧戈12 小时前
c++条件变量的使用
开发语言·c++
之歆12 小时前
Day22_CSS 函数完全指南:从变量到数学计算的现代样式编程
开发语言·前端·javascript·css·tensorflow·less
此生决int12 小时前
C++快速上手java备战期末考——运算符,输入输出和数组
java·c++·期末复习
咩咦12 小时前
C++学习笔记29:友元函数和输入输出运算符重载
c++·学习笔记·cin·运算符重载·友元函数·cout·friend
爱学习的程序媛12 小时前
C 语言全景指南:从底层原理到工业级实战
c++·c#·c
十五年专注C++开发12 小时前
C++ 序列化 Protocol Buffers:高效数据交换
开发语言·c++·序列化·反序列化·protobuf
神仙别闹13 小时前
基于QT(C++)+SQL Server 2008 实现相机租赁系统
开发语言·c++·数码相机
Stzzfntty13 小时前
嵌软c八股刷题记录
c语言·开发语言·算法