上篇聊了stack和queue,STL容器的部分基本讲完了。今天聊一个贯穿整个STL的设计思想——迭代器。

迭代器是STL的灵魂。没有迭代器,容器和算法就是割裂的。有了迭代器,任何算法都能作用于任何容器。这就是STL最精妙的设计。

面试里考迭代器,通常从"什么是迭代器"开始,然后深入到迭代器的分类、traits、失效问题。

迭代器的本质

说白了,迭代器就是一个"智能指针"。它指向容器里的某个元素,支持解引用(*it)获取元素,支持移动(++it)到下一个元素。

std::vector<int> v = {1, 2, 3, 4, 5};

// 用迭代器遍历
for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";
}
// 输出: 1 2 3 4 5

有了迭代器,算法就不需要关心底层容器是什么:

// 同一个find算法,可以用于vector、list、deque...
auto it1 = std::find(v.begin(), v.end(), 3);     // vector
auto it2 = std::find(lst.begin(), lst.end(), 3);  // list

这就是迭代器的价值——解耦容器和算法。

在机器人开发里,迭代器的这个特性特别有用。比如你写了一个处理点云的算法,参数传迭代器而不是具体的容器类型,那这个算法就能处理vector存的点云、list存的点云、甚至自定义容器存的点云。

// 通用的数据处理函数,不依赖具体容器类型
template<typename Iterator>
double computeMean(Iterator begin, Iterator end) {
    double sum = 0;
    int count = 0;
    for (auto it = begin; it != end; ++it) {
        sum += *it;
        count++;
    }
    return sum / count;
}

vector<double> v = {1.0, 2.0, 3.0};
list<double> l = {1.0, 2.0, 3.0};
cout << computeMean(v.begin(), v.end());  // 都能用
cout << computeMean(l.begin(), l.end());

五种迭代器分类

面试高频考点。STL把迭代器分成五类,能力递增:

输入迭代器(Input Iterator):只读,只能向前。*it读取,++it前进。比如istream_iterator。

输出迭代器(Output Iterator):只写,只能向前。*it = value写入。比如ostream_iterator。

前向迭代器(Forward Iterator):可读可写,只能向前。比如unordered_map的迭代器。

双向迭代器(Bidirectional Iterator):可读可写,可以前后移动。多了--it。比如list、map的迭代器。

随机访问迭代器(Random Access Iterator):可读可写,可以跳跃式访问。支持it + n、it - n、it[n]、it1 - it2。比如vector、deque的迭代器。

// vector的迭代器是随机访问迭代器
auto it = v.begin();
it += 3;       // 跳到第4个元素
cout << it[1]; // 访问第5个元素
auto dist = v.end() - v.begin();  // 距离,O(1)

// list的迭代器是双向迭代器
auto it2 = lst.begin();
++it2;   // 可以
--it2;   // 可以
// it2 += 3;  // 编译错误!不支持随机访问

为什么分这么细?因为不同的算法需要不同能力的迭代器。std::sort需要随机访问迭代器,所以不能用于list(list有自己的sort成员函数)。std::find只需要输入迭代器,所以几乎可以用于所有容器。

迭代器失效:再强调一次

前面讲vector的时候提过迭代器失效,这里系统总结一下。

迭代器失效意味着迭代器指向的元素被删除或移动了,继续使用就是未定义行为。

vector:插入可能导致所有迭代器失效(扩容)。删除让被删元素之后的迭代器全部失效。

deque:中间插入所有迭代器失效,两端插入只有end()失效。删除只让被删元素的迭代器失效。

list/forward_list:插入不会使任何迭代器失效。删除只让被删元素的迭代器失效。

map/set/unordered_map:插入不会使任何迭代器失效。删除只让被删元素的迭代器失效。

关联容器的迭代器稳定性是最好的,这也是它们的优势之一。

反向迭代器和辅助函数

STL还提供了反向迭代器,从容器的末尾向前遍历:

vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.rbegin(); it != v.rend(); ++it) {
    cout << *it << " ";
}
// 输出: 5 4 3 2 1

还有两个很实用的辅助函数:

std::advance(it, n):把迭代器向前移动n步。对于随机访问迭代器是O(1),对于双向迭代器是O(N)。

std::distance(it1, it2):计算两个迭代器之间的距离。同样,随机访问迭代器O(1),其他O(N)。

list<int> lst = {1, 2, 3, 4, 5};
auto it = lst.begin();
std::advance(it, 3);  // 移动到第4个元素
cout << *it;  // 4

auto dist = std::distance(lst.begin(), it);  // 3

在机器人开发里,如果你需要对传感器数据做"回看"操作(比如看前3帧的数据),advance就很好用。

手写迭代器

面试偶尔会让你给自己的容器写迭代器。实现一个简单的前向迭代器:

class IntArray {
    int* data_;
    size_t size_;
public:
    IntArray(int* data, size_t size) : data_(data), size_(size) {}
    
    class Iterator {
        int* ptr;
    public:
        Iterator(int* p) : ptr(p) {}
        int& operator*() { return *ptr; }
        Iterator& operator++() { ++ptr; return *this; }
        bool operator!=(const Iterator& other) const { 
            return ptr != other.ptr; 
        }
    };
    
    Iterator begin() { return Iterator(data_); }
    Iterator end() { return Iterator(data_ + size_); }
};

IntArray arr(data, 5);
for (auto val : arr) {  // 支持range-based for
    cout << val << " ";
}

关键点:实现operator*、operator++和operator!=,提供begin()和end(),就能支持range-based for循环。

实战中还有一个容易踩坑的地方:迭代器失效的调试。在release模式下迭代器失效通常不会立刻崩溃,而是读出脏数据或者跑到错误的位置,这种bug特别难排查。我的经验是在开发阶段开启STL的调试模式——GCC下编译时加-D_GLIBCXX_DEBUG,这样迭代器失效会直接报运行时错误而不是默默出错。MSVC在Debug模式下也有类似的迭代器检查。另外在代码review时,看到在循环里对容器做insert或erase操作就要特别警惕,先确认用的是哪个版本的迭代器。养成这个习惯能帮你避免很多隐蔽的迭代器失效bug。

给正在准备面试的你一点建议

迭代器在面试里考得比较灵活。

必须掌握的:五种迭代器分类及区别、迭代器失效规则、begin/end的用法。

加分项:能手写一个简单的迭代器、理解iterator_traits的作用、知道为什么sort不能用于list。

iterator_traits是迭代器特征萃取,它能获取迭代器指向的值类型、距离类型等。这是STL算法能针对不同迭代器类型做优化的基础。这个比较深入,面试偶尔会提到。

下篇进入现代C++的世界——shared_ptr,智能指针的第一篇。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。

「机器人软件开发面试·从入门到精通」连载系列 上一篇:第33篇 STL之stack与queue——BFS/DFS的标配数据结构 下一篇预告:第35篇 智能指针shared_ptr——现代C++内存管理的答案

有任何问题欢迎评论区留言,我会尽量回复。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐