乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

自定义迭代器开发

定义与作用

当需要让自定义数据结构支持 STL 算法和范围 for 循环时,需为其编写迭代器。一个合格的迭代器需提供 iterator_traits 所需的全部嵌套类型,并实现对应的操作符。

// 自定义容器 + 自定义迭代器 → 支持标准算法
MyContainer<int> data{1, 2, 3, 4, 5};
std::sort(data.begin(), data.end());
for (auto x : data) std::cout << x << " ";

核心原理

迭代器最小接口

完整示例

示例一:循环缓冲区迭代器

场景说明:小崔为飞翔科技的日志系统实现一个环形缓冲区,并编写迭代器支持 STL 算法。

#include <iostream>
#include <iterator>
#include <algorithm>
#include <stdexcept>

// 固定大小环形缓冲区
template<typename T, size_t N>
class RingBuffer {
public:
    // ---------- 迭代器定义 ----------
    class iterator {
    public:
        // 嵌套类型(必须)
        using iterator_category = std::random_access_iterator_tag;
        using value_type        = T;
        using difference_type   = std::ptrdiff_t;
        using pointer           = T*;
        using reference         = T&;

        iterator() : buf_(nullptr), pos_(0) {}
        iterator(RingBuffer* buf, size_t pos) : buf_(buf), pos_(pos) {}

        reference operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
        pointer   operator->() const { return &buf_->data_[(buf_->head_ + pos_) % N]; }

        iterator& operator++() { ++pos_; return *this; }
        iterator  operator++(int) { auto tmp = *this; ++pos_; return tmp; }
        iterator& operator--() { --pos_; return *this; }
        iterator  operator--(int) { auto tmp = *this; --pos_; return tmp; }

        iterator& operator+=(difference_type n) { pos_ += n; return *this; }
        iterator& operator-=(difference_type n) { pos_ -= n; return *this; }

        iterator  operator+(difference_type n) const { return iterator(buf_, pos_ + n); }
        iterator  operator-(difference_type n) const { return iterator(buf_, pos_ - n); }
        difference_type operator-(const iterator& other) const {
            return static_cast<difference_type>(pos_) - other.pos_;
        }

        reference operator[](difference_type n) const { return *(*this + n); }

        bool operator==(const iterator& other) const { return pos_ == other.pos_; }
        bool operator!=(const iterator& other) const { return pos_ != other.pos_; }
        bool operator<(const iterator& other)  const { return pos_ < other.pos_; }
        bool operator>(const iterator& other)  const { return pos_ > other.pos_; }
        bool operator<=(const iterator& other) const { return pos_ <= other.pos_; }
        bool operator>=(const iterator& other) const { return pos_ >= other.pos_; }

    private:
        RingBuffer* buf_;
        size_t pos_;
    };

    // ---------- RingBuffer 成员函数 ----------
    void push_back(const T& value) {
        if (size_ < N) {
            data_[tail_++] = value;
            if (tail_ == N) tail_ = 0;
            ++size_;
        } else {
            // 满时覆盖最旧的
            data_[tail_] = value;
            tail_ = (tail_ + 1) % N;
            head_ = (head_ + 1) % N;
        }
    }

    iterator begin() { return iterator(this, 0); }
    iterator end()   { return iterator(this, size_); }

    size_t size() const { return size_; }

private:
    T data_[N]{};
    size_t head_ = 0, tail_ = 0, size_ = 0;
};

int main() {
    std::cout << "=== 飞翔科技日志环形缓冲区 ===\n\n";

    RingBuffer<std::string, 5> logBuf;

    logBuf.push_back("2026-06-14 09:00 系统启动");
    logBuf.push_back("2026-06-14 09:05 用户登录: 孔蓝");
    logBuf.push_back("2026-06-14 09:10 数据库连接池初始化");
    logBuf.push_back("2026-06-14 09:15 缓存预热完成");
    logBuf.push_back("2026-06-14 09:20 支付模块自检通过");

    std::cout << "缓冲区大小: " << logBuf.size() << "\n";

    // 支持范围 for(依赖 begin/end)
    std::cout << "所有日志:\n";
    for (const auto& log : logBuf)
        std::cout << "  " << log << "\n";

    // 支持 STL 算法
    auto it = std::find_if(logBuf.begin(), logBuf.end(),
        [](const std::string& s) { return s.find("缓存") != std::string::npos; });
    if (it != logBuf.end())
        std::cout << "\n找到缓存相关日志: " << *it << "\n";

    // 支持随机访问
    std::cout << "第 2 条日志: " << logBuf.begin()[1] << "\n";

    // 支持 distance
    auto d = std::distance(logBuf.begin(), logBuf.end());
    std::cout << "元素数量 (distance): " << d << "\n";

    // 环形覆盖测试
    std::cout << "\n追加 2 条新日志(将覆盖最旧的):\n";
    logBuf.push_back("2026-06-14 09:25 推送服务重连");
    logBuf.push_back("2026-06-14 09:30 监控告警恢复");
    for (const auto& log : logBuf)
        std::cout << "  " << log << "\n";
}

预期输出:

=== 飞翔科技日志环形缓冲区 ===

缓冲区大小: 5
所有日志:
  2026-06-14 09:00 系统启动
  2026-06-14 09:05 用户登录: 孔蓝
  2026-06-14 09:10 数据库连接池初始化
  2026-06-14 09:15 缓存预热完成
  2026-06-14 09:20 支付模块自检通过

找到缓存相关日志: 2026-06-14 09:15 缓存预热完成
第 2 条日志: 2026-06-14 09:05 用户登录: 孔蓝
元素数量 (distance): 5

追加 2 条新日志(将覆盖最旧的):
  2026-06-14 09:10 数据库连接池初始化
  2026-06-14 09:15 缓存预热完成
  2026-06-14 09:20 支付模块自检通过
  2026-06-14 09:25 推送服务重连
  2026-06-14 09:30 监控告警恢复

逐段分析:

  • 五个嵌套类型是 iterator_traits 正常工作的前提
  • iterator_category 设为 random_access_iterator_tag,声明随机访问能力
  • pos_ 是逻辑位置,(head_ + pos_) % N 映射到物理存储
  • 由于实现了随机访问接口,支持 []、advance、sort 等操作

示例二:范围生成器迭代器

场景说明:白歌实现一个懒求值的日期范围迭代器,用于生成飞翔科技周报日期列表。

#include <iostream>
#include <iterator>
#include <string>
#include <sstream>
#include <iomanip>

// 输入迭代器:按天生成日期
class DateIterator {
public:
    using iterator_category = std::input_iterator_tag;
    using value_type        = std::string;
    using difference_type   = std::ptrdiff_t;
    using pointer           = const std::string*;
    using reference         = const std::string&;

    DateIterator(int year, int month, int day)
        : current_(toDate(year, month, day)) {}

    DateIterator() : current_(0), done_(true) {}  // 结束标记

    reference operator*() const { return currentStr_; }
    pointer   operator->() const { return &currentStr_; }

    DateIterator& operator++() {
        advance();
        return *this;
    }

    DateIterator operator++(int) {
        auto tmp = *this;
        advance();
        return tmp;
    }

    bool operator==(const DateIterator& other) const {
        return (done_ && other.done_) || current_ == other.current_;
    }
    bool operator!=(const DateIterator& other) const { return !(*this == other); }

private:
    static int toDate(int y, int m, int d) { return y * 10000 + m * 100 + d; }

    void advance() {
        int y = current_ / 10000;
        int m = (current_ / 100) % 100;
        int d = current_ % 100;

        // 简单的日期递增(假设每月30天,仅演示)
        ++d;
        if (d > 30) { d = 1; ++m; }
        if (m > 12) { m = 1; ++y; }

        current_ = toDate(y, m, d);

        std::ostringstream oss;
        oss << y << "-" << std::setw(2) << std::setfill('0') << m
            << "-" << std::setw(2) << std::setfill('0') << d;
        currentStr_ = oss.str();
    }

    int current_;
    std::string currentStr_;
    bool done_ = false;
};

int main() {
    std::cout << "=== 飞翔科技第 24 周日报日期 ===\n\n";

    // 生成 2026-06-08 到 2026-06-14 的日期范围
    DateIterator start(2026, 6, 7);    // 从 6月7日开始(++ 后首次即为 6月8日)
    DateIterator end;                   // 默认构造 = 结束

    int count = 0;
    for (auto it = ++start; count < 7 && it != end; ++it, ++count) {
        std::cout << "  " << *it;
        if (count == 6) std::cout << " ← 今天";
        std::cout << "\n";
    }

    std::cout << "\n共生成 " << count << " 天日期(输入迭代器:单次遍历)\n";
}

预期输出:

=== 飞翔科技第 24 周日报日期 ===

  2026-06-08
  2026-06-09
  2026-06-10
  2026-06-11
  2026-06-12
  2026-06-13
  2026-06-14 ← 今天

共生成 7 天日期(输入迭代器:单次遍历)

逐段分析:

  • input_iterator_tag 声明为输入迭代器,最轻量级约束
  • operator++ 触发懒求值——只有在递增时才计算下一个日期
  • 输入迭代器只能单次遍历,不能回退或多遍读取
  • 这种模式适合流式数据源(网络流、文件流、生成器)

易错场景与面试考点

易错场景

1. 缺少 iterator_traits 所需类型

// 如果迭代器没有 value_type 等嵌套类型
// std::iterator_traits<MyIter>::value_type 将无法编译

2. end() 语义错误

// end() 应指向最后一个元素之后,不能指向最后一个元素
iterator end() { return iterator(data_ + size_ - 1); }  // ❌
iterator end() { return iterator(data_ + size_); }      // ✅

3. C++17 前继承 std::iterator 的方式已废弃

// ❌ C++17 废弃
class MyIter : public std::iterator<std::forward_iterator_tag, int> { ... };

// ✅ 直接写嵌套类型
class MyIter {
    using iterator_category = std::forward_iterator_tag;
    using value_type = int;
    // ...
};

面试考点

考点要点
iterator_traits 工作原理通过直接获取嵌套类型或对指针偏特化
输入迭代器 vs 前向迭代器输入迭代器 rvalue-only,前向迭代器可多次通过
双向迭代器额外要求operator--
随机访问迭代器额外要求+=、+、-=、-、<、>、[]
最小接口五类嵌套类型 + 基本操作符
std::iterator 废弃C++17 起废弃,直接手写嵌套类型
与范围 for 兼容需要 begin() 和 end() 返回相同迭代器类型
上一页
std::random 随机数库