c++迭代器

迭代器的本质是类似指针的对象 ,但不等于指针。具体来说:

  • 指针是最简单的迭代器 (随机访问迭代器)

  • 迭代器是更智能的指针抽象 (可能是类对象)

  • 迭代器是:智能下标

    从用法上看:迭代器完全可以看作访问容器元素的下标,作用一模一样;
    从本质上看:它不是整数下标,而是指向元素的指针抽象;
    从能力上看:它是所有容器通用的下标,数组下标做不到这一点

指针与迭代器的关系

cpp 复制代码
// 是包含关系:
迭代器
├── 指针迭代器(vector, array, deque 等)
├── 类对象迭代器(list, map, set 等)
├── 特殊迭代器(reverse_iterator, move_iterator 等)
└── 自定义迭代器


// 都能运输(访问数据),但内部不同

"迭代器的本质是智能指针抽象。指针是最简单的迭代器,但迭代器可以是更复杂的类对象。关键在于所有迭代器都提供一致的接口(*、++、->等),让算法不关心具体实现。vector迭代器通常是指针,list/map迭代器是类对象。"

关键理解

  1. 指针 ∈ 迭代器:指针是迭代器的子集

  2. 接口一致性:用法相同,内部实现不同

  3. 多态行为:通过操作符重载实现

  4. 零开销:简单迭代器优化后与指针性能相同

  5. 设计精髓:隐藏实现细节,暴露统一接口


1. 指针 vs 迭代器

指针是迭代器的一种
cpp 复制代码
int arr[] = {1, 2, 3, 4, 5};
int* ptr = arr;  // 指针

// 指针具有迭代器的所有基本操作:
*ptr;       // 解引用
/*
解引用是"获取指针指向的值"的操作。符号 *有两种含义:

定义指针时:int* ptr→ 声明 ptr 是指针

使用指针时:*ptr→ 获取 ptr 指向的值
*/
++ptr;      // 前进
ptr[2];     // 随机访问
ptr1 - ptr2;// 计算距离
ptr1 < ptr2;// 比较
但迭代器不一定是指针
cpp 复制代码
#include <list>
#include <iostream>

std::list<int> lst = {1, 2, 3, 4, 5};
auto it = lst.begin();  // 迭代器(是类对象)

// 用法像指针
std::cout << *it;  // 1
++it;              // 前进
std::cout << *it;  // 2

// ❌ 但不是指针
// it[2];       // 错误!list迭代器不支持随机访问
// it + 3;      // 错误!
// it - lst.begin();  // 错误!

list是标准库的内置类模板。

lst.begin()返回的是 list<int>::iterator类型的对象,这是一个嵌套在 list 类内的迭代器类。


2. 迭代器的三种实现形式

① 指针(最简单)
复制代码
// vector 的迭代器通常是原始指针
template<typename T>
class vector {
    T* data;  // 动态数组
public:
    typedef T* iterator;  // 迭代器就是指针
    
    iterator begin() { return data; }
    iterator end() { return data + size; }
};
② 类对象(复杂容器)
复制代码
// list 的迭代器是类对象
template<typename T>
class list {
    struct Node {
        T data;
        Node* next;
        Node* prev;
    };
    
    class iterator {
        Node* current;  // 内部维护指针
    public:
        // 重载操作符
        T& operator*() { return current->data; }
        iterator& operator++() { 
            current = current->next; 
            return *this; 
        }
        // ...
    };
};
③ 智能指针包装
复制代码
// 检查迭代器(调试模式)
class CheckedIterator {
    int* ptr;
    int* begin;
    int* end;
public:
    // 安全检查
    int& operator*() {
        if (ptr < begin || ptr >= end) 
            throw std::out_of_range("迭代器越界");
        return *ptr;
    }
    // ...
};

3. 编译器视角

vector 迭代器通常优化为指针
cpp 复制代码
std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();

// 编译器优化后:
int* it = vec.data();  // 直接是原始指针
// 零开销抽象
调试模式下的迭代器
复制代码
// 调试版本:添加检查
class DebugVectorIterator {
    int* ptr;
    int* start;
    int* finish;
    int* end_of_storage;
    
    void check_validity() {
        if (ptr < start || ptr >= finish)
            throw "迭代器无效";
    }
public:
    int& operator*() {
        check_validity();
        return *ptr;
    }
    // ...
};

5. 迭代器的核心:接口一致性

不管实现如何,接口相同

接口相同指的是迭代器都支持相同的操作符*++!=等),尽管底层实现不同。就像不同品牌汽车都有方向盘、油门、刹车,操作方法相同,但内部机械结构完全不同。

复制代码
// 所有迭代器都支持这些操作
template<typename Iterator>
void process(Iterator it) {
    auto value = *it;   // 都能解引用
    ++it;               // 都能前进
    // 不关心里面是什么
}

就像不同汽车都有方向盘

  • 机械方向盘

  • 电子方向盘

  • 线控方向盘

  • 接口相同:转动控制方向

  • 实现不同:内部机制完全不同

例如不同手机,相同充电接口,就是接口相同


6. 迭代器的内存布局

vector 迭代器(通常是连续指针)
复制代码
// 内存布局:[ptr]
+--------+
| 0x1000 | → 指向数组元素
+--------+
list 迭代器(包含节点指针)
复制代码
// 内存布局:[node_ptr]
+--------+
| 0x2000 | → 指向链表节点
+--------+
       ↓
+------------+    +------------+
| data: 1    |    | data: 2    |
| next: 0x2008| → | next: ...  |
| prev: ...  |    | prev: 0x2000|
+------------+    +------------+
map 迭代器(树节点指针)
复制代码
// 内存布局:[node_ptr, container_ptr, ...]
+--------+
| 0x3000 | → 指向红黑树节点
+--------+

7. 展示迭代器如何通过重载操作符来提供统一接口

(了解即可)
复制代码
template<typename T>
class ListIterator {
    Node<T>* current;
public:
    // 重载 * 操作符
    T& operator*() { 
        return current->data; 
    }
    
    // 重载 ++ 操作符
    ListIterator& operator++() {
        current = current->next;
        return *this;
    }
    
    // 重载 -> 操作符
    T* operator->() { 
        return &current->data; 
    }
    
    // 重载比较操作符
    bool operator!=(const ListIterator& other) {
        return current != other.current;
    }
};

8. 迭代器和指针在实际使用中感知不到差异

复制代码
// 使用起来完全一样
std::vector<int> vec = {1, 2, 3};
std::list<int> lst = {1, 2, 3};
std::map<int, std::string> m = {{1, "a"}};

// 相同操作
auto v_it = vec.begin();
auto l_it = lst.begin();
auto m_it = m.begin();

int v_val = *v_it;  // 解引用
std::string m_val = m_it->second;  // 箭头操作符

++v_it;  // 前进
++l_it;  // 前进
++m_it;  // 前进

// 用户不关心里面是指针还是对象
相关推荐
zhangzeyuaaa2 小时前
Python 闭包详解
开发语言·python
ん贤2 小时前
Go GC 非玄学,而是 CPU 和内存的权衡
开发语言·后端·golang·性能调优·gc
G探险者2 小时前
LiteFlow 技术介绍
java·开发语言
rqtz2 小时前
【C++】ROS2捕获Ctrl+C信号+原子操作与线程生命周期控制
c++·多线程·原子
FuckPatience4 小时前
Visual Studio C# 项目中文件后缀简介
开发语言·c#
老四啊laosi10 小时前
[C++进阶] 24. 哈希表封装unordered_map && unordered_set
c++·哈希表·封装·unordered_map·unordered_set
014-code11 小时前
订单超时取消与库存回滚的完整实现(延迟任务 + 状态机)
java·开发语言
妙为11 小时前
银河麒麟V4下编译Qt5.12.12源码
c++·qt·国产化·osg3.6.5·osgearth3.2·银河麒麟v4
lly20240611 小时前
组合模式(Composite Pattern)
开发语言