在 C++ 标准库中,容器、算法以及其他工具都依赖于迭代器提供的一组通用操作。要让自定义容器与 STL 兼容,必须实现符合迭代器概念的接口。下面以双向迭代器为例,说明实现步骤、关键细节以及常见陷阱。
1. 迭代器概念回顾
STL 定义了若干个迭代器层级:
- Input Iterator(单向读取)
- Output Iterator(单向写入)
- Forward Iterator(可多次遍历,单向)
- Bidirectional Iterator(前后移动)
- Random Access Iterator(支持算术运算)
每一层级都继承前一层级的接口。例如,双向迭代器必须满足所有前向迭代器的需求,并额外提供 -- 操作。
2. 关键类型别名
实现迭代器时,需要在类内部声明若干类型别名:
using iterator_category = std::bidirectional_iterator_tag; // 迭代器类别
using value_type = T; // 元素类型
using difference_type = std::ptrdiff_t; // 差值类型
using pointer = T*; // 指向元素的指针
using reference = T&; // 对元素的引用
这些别名使得 std::iterator_traits 能够从迭代器类型提取必要信息。
3. 基本操作符
下面给出一个最小实现,假设我们有一个简单的链表节点结构:
template<typename T>
struct Node {
T data;
Node* prev;
Node* next;
};
迭代器类:
template<typename T>
class ListIterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
// 构造
explicit ListIterator(Node <T>* ptr = nullptr) : cur(ptr) {}
// 解引用
reference operator*() const { return cur->data; }
pointer operator->() const { return &(cur->data); }
// 前缀 ++ / --
ListIterator& operator++() { cur = cur->next; return *this; }
ListIterator& operator--() { cur = cur->prev; return *this; }
// 后缀 ++ / --
ListIterator operator++(int) { ListIterator tmp = *this; ++(*this); return tmp; }
ListIterator operator--(int) { ListIterator tmp = *this; --(*this); return tmp; }
// 等价
bool operator==(const ListIterator& other) const { return cur == other.cur; }
bool operator!=(const ListIterator& other) const { return cur != other.cur; }
private:
Node <T>* cur;
};
4. 与容器配合
在自定义链表类中提供 begin()、end() 等成员函数:
template<typename T>
class MyList {
public:
using iterator = ListIterator <T>;
MyList() : head(nullptr), tail(nullptr) {}
void push_back(const T& value) {
Node <T>* n = new Node<T>{value, tail, nullptr};
if (tail) tail->next = n; else head = n;
tail = n;
}
iterator begin() { return iterator(head); }
iterator end() { return iterator(nullptr); }
private:
Node <T>* head;
Node <T>* tail;
};
5. 典型错误与排查
| 常见错误 | 说明 | 解决办法 |
|---|---|---|
迭代器不提供 iterator_category |
std::iterator_traits 访问不到类别 |
按照上面示例添加别名 |
operator++ 只返回右值 |
前缀返回 void |
必须返回 *this |
operator* 返回副本 |
会导致修改无效 | 返回引用 (reference) |
end() 返回非 nullptr 的指针 |
会导致无限循环 | 统一使用 nullptr 作为终止标识 |
| 迭代器与容器生命周期不匹配 | 容器被销毁后迭代器悬空 | 仅在容器存活期间使用迭代器 |
6. 进一步扩展
- 随机访问迭代器:需要实现算术运算 (
operator+,operator-,operator[]) 并维护内部索引或指针差值。 - const_iterator:在
const成员函数中返回不允许修改的迭代器。可以通过std::add_const生成对应类型。 - 逆向迭代器:可以包装已有迭代器并反转前后移动逻辑。
7. 小结
实现自定义迭代器的核心是:
- 声明标准的类型别名,使
iterator_traits正确工作。 - 实现所有必需的运算符(取决于所需的迭代器类别)。
- 确保容器与迭代器生命周期匹配,避免悬空引用。
- 进行充分测试:使用 STL 算法(如
std::for_each,std::find_if)验证兼容性。
只要按上述步骤细心实现,即可让自定义容器在 C++ 标准库生态中与现有算法无缝协作。