自定义迭代器开发
定义与作用
当需要让自定义数据结构支持 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 ¤tStr_; }
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() 返回相同迭代器类型 |