目录
一、引言
栈(Stack)和队列(Queue)是两种最基础的数据结构,它们分别遵循 LIFO(后进先出)和 FIFO(先进先出)的原则。在 C++ STL 中,std::stack 和 std::queue 被设计为容器适配器(Container Adapter),默认底层基于 std::deque 实现。
但是,仅仅知道 push()、pop()、top() 这些基本操作是远远不够的。真正体现功力的,是能否在复杂的实际场景中灵活运用栈和队列的核心思想来解决难题。
今天这篇文章将会深入两个经典的进阶应用场景:
- 逆波兰表达式计算器 :栈在表达式求值中的经典运用,涵盖中缀转后缀、优先级处理、括号嵌套等硬核知识点;
- 反向迭代器的设计与实现 :迭代器适配器模式的精妙体现,通过分析 SGI-STL 源码,手把手带你实现一个通用的反向迭代器。
读完这篇文章,你不仅能够彻底搞懂计算器和反向迭代器的实现原理,更能深入理解 STL 的设计哲学,真正做到举一反三。
二、栈的经典应用:逆波兰表达式与计算器实现
2.1 表达式的前世今生:中缀、前缀与后缀
我们日常书写的数学表达式都是中缀表达式(Infix Notation),即运算符在两个运算数中间:
1 - 2 * (3 - 4) + 5
这种写法对人来说直观易懂,但对计算机来说却是个麻烦——你从左往右读,遇到 - 和 * 都不能马上计算,因为后面可能有括号优先级更高。
为了解决这个问题,波兰逻辑学家 J·卢卡西维兹(Jan Łukasiewicz)在 1929 年提出了逆波兰表示法(Reverse Polish Notation, RPN),也就是后缀表达式(Postfix Notation)。
三种表达式对比:
| 类型 | 别称 | 格式 | 示例 |
|---|---|---|---|
| 中缀表达式 | Infix | 运算符在中间 | 1 + 2 |
| 前缀表达式 | 波兰表达式(PN) | 运算符在前面 | + 1 2 |
| 后缀表达式 | 逆波兰表达式(RPN) | 运算符在后面 | 1 2 + |
后缀表达式的核心优势:它天然去除了括号和优先级的困扰!因为表达式本身的排列顺序就已经确定了运算次序,计算机可以严格从左到右读取并计算,不需要回溯和优先级判断。
2.2 后缀表达式求值——栈的天然舞台
后缀表达式的求值算法出奇地简洁——这正是栈结构大显身手的地方。
算法思路
- 建立一个栈,用于存储运算数;
- 从左到右依次读取后缀表达式中的每个元素:
- 如果是运算数,直接入栈;
- 如果是运算符,从栈顶弹出两个运算数(先出栈的是右操作数,后出栈的是左操作数),进行计算,将结果压回栈中;
- 读取结束后,栈中剩下的唯一元素就是最终结果。
画个图理解一下:假设后缀表达式为
["2", "1", "+", "3", "*"],即中缀的(2+1)*3:读 "2" → 栈: [2] 读 "1" → 栈: [2, 1] 读 "+" → 弹出 1, 2 → 2+1=3 → 栈: [3] 读 "3" → 栈: [3, 3] 读 "*" → 弹出 3, 3 → 3*3=9 → 栈: [9] 结果 = 9 ✅
代码实现
class Solution {
public:
int evalRPN(const vector<string>& tokens) {
stack<int> s;
for (size_t i = 0; i < tokens.size(); ++i) {
const string& str = tokens[i];
// 不是运算符 → 是运算数,入栈
if (!("+" == str || "-" == str || "*" == str || "/" == str)) {
s.push(stoi(str));
} else {
// 是运算符 → 弹出两个运算数进行计算
int right = s.top();
s.pop();
int left = s.top();
s.pop();
switch (str[0]) {
case '+': s.push(left + right); break;
case '-': s.push(left - right); break;
case '*': s.push(left * right); break;
case '/': s.push(left / right); break;
}
}
}
return s.top();
}
};
这段代码清晰展示了栈在表达式求值中的核心作用:暂存操作数,按需取出计算,结果继续参与后续运算。
2.3 中缀转后缀——算法的核心难点
后缀表达式求值很简单,但问题在于——用户输入的永远是中缀表达式。如何将中缀表达式转换成后缀表达式,才是整个计算器实现中最考验思维的部分。
转换思路详解
我们逐条梳理转换规则:
规则一:遇到运算数,直接输出。
这个好理解,运算数的相对顺序在中缀和后缀表达式中是不变的。
规则二:建立一个栈存储运算符,遇到运算符时与栈顶比较优先级。
-
如果栈为空,或者当前运算符优先级 > 栈顶运算符优先级,则将当前运算符入栈。
- 为什么?因为栈里存的是"之前的运算符",如果当前运算符优先级更高,说明前面的还不能算,当前的也不能算(后面可能还有更高优先级的),所以先存着。
-
如果当前运算符优先级 ≤ 栈顶运算符优先级,则说明栈顶的运算符已经可以计算了。此时,将栈顶运算符出栈并输出,然后继续用当前运算符与新的栈顶比较(这就是一个循环/递归判断的过程)。
规则三:遇到括号,递归处理子表达式。
将括号内的内容视为一个独立的子表达式,递归调用转换函数。遇到 ) 时,将栈中剩余运算符全部输出,然后返回。
规则四:表达式读取结束后,将栈中剩余运算符全部输出。
运算符优先级
对于四则运算,我们定义两级优先级:
| 运算符 | 优先级 |
|---|---|
+ - | 1 |
* / | 2 |
代码实现
class Solution {
public:
// 获取运算符优先级
int operatorPrecedence(char ch) {
struct OpPD {
char _op;
int _pd;
};
static OpPD arr[] = {
{ '+', 1 }, { '-', 1 },
{ '*', 2 }, { '/', 2 }
};
for (auto& e : arr) {
if (e._op == ch) {
return e._pd;
}
}
assert(false);
return -1;
}
// 中缀表达式 → 后缀表达式(RPN)
// s: 原始中缀表达式字符串
// i: 当前处理的字符下标(引用传递,保证递归时位置同步)
// v: 输出的后缀表达式 token 数组
void toRPN(const string& s, size_t& i, vector<string>& v) {
stack<char> st; // 运算符栈
while (i < s.size()) {
if (isdigit(s[i])) {
// === 情况1: 遇到运算数,直接输出 ===
string num;
while (i < s.size() && isdigit(s[i])) {
num += s[i];
++i;
}
v.push_back(num);
} else {
if (s[i] == '(') {
// === 情况2: 遇到左括号,递归处理子表达式 ===
++i;
toRPN(s, i, v);
} else if (s[i] == ')') {
// === 情况3: 遇到右括号,输出栈中所有运算符,结束递归 ===
++i;
while (!st.empty()) {
v.push_back(string(1, st.top()));
st.pop();
}
return;
} else {
// === 情况4: 遇到运算符,比较优先级 ===
if (st.empty() ||
operatorPrecedence(s[i]) > operatorPrecedence(st.top())) {
// 当前优先级更高 → 入栈
st.push(s[i]);
++i;
} else {
// 栈顶优先级更高或相等 → 栈顶出栈输出
v.push_back(string(1, st.top()));
st.pop();
// 注意:这里不自增 i,当前运算符需要继续和新的栈顶比较!
}
}
}
}
// === 表达式结束,输出栈中所有运算符 ===
while (!st.empty()) {
v.push_back(string(1, st.top()));
st.pop();
}
}
};
关键细节:遇到运算符时,如果当前优先级不高于栈顶,出栈输出后不自增 i。这是因为当前运算符需要继续和新的栈顶进行比较——这是一个最容易出错的地方,一定要注意!
2.4 完整计算器——把所有零件组装起来
有了 evalRPN 和 toRPN 两个核心函数,我们已经能处理标准的四则运算表达式了。但在实际 OJ 题目中,还有两个隐藏的坑需要填:
- 空格处理:输入字符串可能包含空格,需要统一去除;
- 负数与减号的区分:
-3 + 2和5 - 3中的-含义完全不同。
解决方案:将所有表示负数的 -x 转换为 0-x,这样负数就变成了一个合法的"0减x"表达式。
判断条件:如果 - 出现在字符串开头,或者 - 前面的字符不是数字且不是 ),那它就是负号。
完整代码
class Solution {
public:
// 获取运算符优先级
int operatorPrecedence(char ch) {
struct OpPD {
char _op;
int _pd;
};
static OpPD arr[] = {
{ '+', 1 }, { '-', 1 },
{ '*', 2 }, { '/', 2 }
};
for (auto& e : arr) {
if (e._op == ch) return e._pd;
}
assert(false);
return -1;
}
// 中缀表达式转后缀表达式(RPN)
void toRPN(const string& s, size_t& i, vector<string>& v) {
stack<char> st;
while (i < s.size()) {
if (isdigit(s[i])) {
string num;
while (i < s.size() && isdigit(s[i])) {
num += s[i];
++i;
}
v.push_back(num);
} else {
if (s[i] == '(') {
++i;
toRPN(s, i, v); // 递归处理子表达式
} else if (s[i] == ')') {
++i;
while (!st.empty()) {
v.push_back(string(1, st.top()));
st.pop();
}
return;
} else {
if (st.empty() ||
operatorPrecedence(s[i]) > operatorPrecedence(st.top())) {
st.push(s[i]);
++i;
} else {
v.push_back(string(1, st.top()));
st.pop();
}
}
}
}
while (!st.empty()) {
v.push_back(string(1, st.top()));
st.pop();
}
}
// 后缀表达式求值
int evalRPN(const vector<string>& tokens) {
stack<int> s;
for (size_t i = 0; i < tokens.size(); ++i) {
const string& str = tokens[i];
if (!("+" == str || "-" == str || "*" == str || "/" == str)) {
s.push(stoi(str));
} else {
int right = s.top(); s.pop();
int left = s.top(); s.pop();
switch (str[0]) {
case '+': s.push(left + right); break;
case '-': s.push(left - right); break;
case '*': s.push(left * right); break;
case '/': s.push(left / right); break;
}
}
}
return s.top();
}
// 完整计算器入口
int calculate(string s) {
// Step 1: 去除所有空格
std::string news;
news.reserve(s.size());
for (auto ch : s) {
if (ch != ' ') news += ch;
}
s.swap(news);
news.clear();
// Step 2: 将负数 "-x" 转换为 "0-x"
for (size_t i = 0; i < s.size(); ++i) {
if (s[i] == '-' &&
(i == 0 || (!isdigit(s[i - 1]) && s[i - 1] != ')'))) {
news += "0-"; // 负号 → 补 0
} else {
news += s[i];
}
}
// Step 3: 中缀 → 后缀
size_t i = 0;
vector<string> v;
toRPN(news, i, v);
// Step 4: 后缀求值
return evalRPN(v);
}
};
执行流程示例
以 "1-2*(3-4)+5" 为例(这是一个非常经典的测试用例):
Step 1: 去空格 → "1-2*(3-4)+5"
Step 2: 处理负数 → "1-2*(3-4)+5" (没有负数,不变)
Step 3: 中缀转后缀 → ["1","2","3","4","-","*","-","5","+"]
Step 4: 后缀求值
1 → 栈: [1]
2 → 栈: [1, 2]
3 → 栈: [1, 2, 3]
4 → 栈: [1, 2, 3, 4]
- → 3-4=-1 → 栈: [1, 2, -1]
* → 2*(-1)=-2 → 栈: [1, -2]
- → 1-(-2)=3 → 栈: [3]
5 → 栈: [3, 5]
+ → 3+5=8 → 栈: [8]
结果: 8 ✅
🎯 小结:这个计算器实现充分利用了栈的后进先出特性——运算符栈保证了优先级高的运算符先出栈被计算;运算数栈保证了计算时先弹出的作为右操作数、后弹出的作为左操作数。栈在这里不仅仅是一个数据结构,更是整个算法正确性的基石。
三、反向迭代器:迭代器适配器模式的精妙运用
3.1 从一个简单需求说起
假设你有一个 vector<int> v = {1, 2, 3, 4},现在你想从后往前遍历它。STL 给了我们现成的工具:
vector<int> v = {1, 2, 3, 4};
// 使用反向迭代器从后往前遍历
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
cout << *rit << " "; // 输出: 4 3 2 1
}
看起来很简单对吧?但是你有没有想过——反向迭代器到底是怎么实现的? rbegin() 返回的是什么?为什么 ++rit 实际上在往"前"走?
这些问题的答案,正是适配器设计模式(Adapter Pattern)在 STL 中的经典应用。反向迭代器本质上是一个迭代器适配器(Iterator Adapter),它包装(wrap)了一个正向迭代器,通过对正向迭代器的操作进行"反向"映射,实现了反向遍历的效果。
3.2 SGI-STL 源码探秘——站在巨人的肩膀上
我们以 SGI-STL 30 版本源码为例,来一探究竟。反向迭代器的核心实现位于 stl_iterator.h 中。
容器如何使用反向迭代器
首先看看 list 和 vector 是如何定义反向迭代器的:
// ==================== stl_list.h ====================
template <class T, class Alloc = alloc>
class list {
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
#ifdef __STL_CLASS_PARTIAL_SPECIALIZATION
// 偏特化版本:借助 iterator_traits 萃取类型信息
typedef reverse_iterator<const_iterator> const_reverse_iterator;
typedef reverse_iterator<iterator> reverse_iterator;
#else
// 旧版本:需要显式传递数据类型
typedef reverse_bidirectional_iterator<const_iterator, value_type,
const_reference, difference_type> const_reverse_iterator;
typedef reverse_bidirectional_iterator<iterator, value_type,
reference, difference_type> reverse_iterator;
#endif
// ⚠️ 注意这个关键设计!!!
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
};
// ==================== stl_vector.h ====================
template <class T, class Alloc = alloc>
class vector {
public:
typedef value_type* iterator;
// ... 类似结构的反向迭代器定义 ...
// ⚠️ 同样的对称设计!!!
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
};
关键发现:rbegin() 封装的是 end() 位置的迭代器,rend() 封装的是 begin() 位置的迭代器!这是一种巧妙的对称设计。
reverse_iterator 的核心源码
// ==================== stl_iterator.h ====================
template <class Iterator>
class reverse_iterator {
protected:
Iterator current; // 被包装的正向迭代器
public:
// 类型萃取 —— 从 Iterator 中提取各种类型信息
typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
typedef typename iterator_traits<Iterator>::value_type value_type;
typedef typename iterator_traits<Iterator>::difference_type difference_type;
typedef typename iterator_traits<Iterator>::pointer pointer;
typedef typename iterator_traits<Iterator>::reference reference;
typedef Iterator iterator_type;
typedef reverse_iterator<Iterator> self;
public:
// 构造函数
reverse_iterator() {}
explicit reverse_iterator(iterator_type x) : current(x) {}
reverse_iterator(const self& x) : current(x.current) {}
// 获取底层正向迭代器的 base()
iterator_type base() const { return current; }
// ===== ⚠️ 最核心、最反直觉的部分!!! =====
reference operator*() const {
Iterator tmp = current;
return *--tmp; // 解引用的是 current 的【前一个】位置!
}
pointer operator->() const {
return &(operator*());
}
// operator++ 实际在底层做 --current
self& operator++() {
--current;
return *this;
}
self operator++(int) {
self tmp = *this;
--current;
return tmp;
}
// operator-- 实际在底层做 ++current
self& operator--() {
++current;
return *this;
}
self operator--(int) {
self tmp = *this;
++current;
return tmp;
}
// 随机访问操作
self operator+(difference_type n) const {
return self(current - n); // 注意:+n 对应底层 -n
}
self& operator+=(difference_type n) {
current -= n;
return *this;
}
self operator-(difference_type n) const {
return self(current + n); // 注意:-n 对应底层 +n
}
self& operator-=(difference_type n) {
current += n;
return *this;
}
reference operator[](difference_type n) const {
return *(*this + n);
}
};
// 全局比较运算符
template <class Iterator>
inline bool operator==(const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y) {
return x.base() == y.base();
}
template <class Iterator>
inline bool operator<(const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y) {
return y.base() < x.base(); // ⚠️ 注意:比较方向也反了!
}
template <class Iterator>
inline typename reverse_iterator<Iterator>::difference_type
operator-(const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y) {
return y.base() - x.base(); // ⚠️ 同样反了!
}
3.3 源码中最"反直觉"的设计
为什么 operator* 访问的是前一个位置?
这是整个反向迭代器设计中最让人困惑的地方。让我用一张图来解释:
正向迭代器视角:
[1] [2] [3] [4] [ ]
↑ ↑
begin() end()
↑
(指向最后一个元素之后的位置,
即"尾后"位置,不存储有效数据)
反向迭代器包装:
rbegin() 包装的是 end()
rend() 包装的是 begin()
当用户调用 *rit (rit 指向 rbegin()):
- rit 内部的 current 实际上指向 end()(也就是元素 4 后面的位置)
- 但我们想要的第一个反向元素是 4!
- 所以需要 *--tmp,即先回退一步再解引用
图解对称性:
begin() end()
↓ ↓
┌───┬───┬───┬───┐
│ 1 │ 2 │ 3 │ 4 │
└───┴───┴───┴───┘
↑ ↑
rend() rbegin()
(包装begin) (包装end)
rend() 包装 begin() —— 这代表"反向遍历的终点"(不包含 begin 指向的元素)
rbegin() 包装 end() —— 这代表"反向遍历的起点"(实际第一个反向元素是 end()-1)
这样就形成了一个完美对称的结构:
begin()↔rend()end()↔rbegin()- 正向区间
[begin(), end())对应反向区间[rbegin(), rend())
这种设计的优雅之处在于——反向遍历的条件和使用方式与正向遍历完全一致:
// 正向遍历
for (auto it = v.begin(); it != v.end(); ++it) { ... }
// 反向遍历 —— 语法完全对称!
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) { ... }
operator++ 和 operator-- 的对称操作
由于"方向相反"这个核心设定,所有的移动操作都要取反:
| 反向迭代器操作 | 底层正向迭代器操作 |
|---|---|
++rit | --current |
--rit | ++current |
rit + n | current - n |
rit - n | current + n |
rit1 < rit2 | rit2.base() < rit1.base() |
3.4 手把手实现反向迭代器
理解了 SGI-STL 的源码思路,我们来自己动手实现一个简洁版的反向迭代器。我们将采用模板参数传递数据类型的方式(与 SGI-STL 旧版本类似),相比 iterator_traits 版本更直观,更适合理解核心原理。
ReverseIterator 模板类
// ==================== ReverseIterator.h ====================
// 所有容器的通用反向迭代器
// 迭代器适配器
namespace bit {
template <class Iterator, class Ref, class Ptr>
struct ReverseIterator {
typedef ReverseIterator<Iterator, Ref, Ptr> Self;
Iterator _it; // 被包装的正向迭代器
// 构造函数
ReverseIterator(Iterator it) : _it(it) {}
// ===== 解引用:访问当前位置的前一个元素 =====
Ref operator*() {
Iterator tmp = _it;
return *(--tmp);
}
// ===== 箭头操作符 =====
Ptr operator->() {
return &(operator*());
}
// ===== 前置 ++ → 底层 -- =====
Self& operator++() {
--_it;
return *this;
}
// ===== 前置 -- → 底层 ++ =====
Self& operator--() {
++_it;
return *this;
}
// ===== 后置 ++ =====
Self operator++(int) {
Self tmp(*this);
--_it;
return tmp;
}
// ===== 后置 -- =====
Self operator--(int) {
Self tmp(*this);
++_it;
return tmp;
}
// ===== 比较运算符 =====
bool operator!=(const Self& s) const {
return _it != s._it;
}
bool operator==(const Self& s) const {
return _it == s._it; // 注意课件中的源码有个小笔误,这里是 == 才对
}
};
} // namespace bit
3.5 将反向迭代器集成到容器中
有了通用的 ReverseIterator 适配器,我们可以轻松地将其集成到任何支持双向迭代器的容器中。
集成到 vector
// ==================== vector.h ====================
#include "ReverseIterator.h"
namespace bit {
template <class T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*>
const_reverse_iterator;
// ===== 正向迭代器 =====
iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }
// ===== 反向迭代器:包装正向迭代器的对称位置 =====
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// ... vector 的其他成员函数 ...
private:
iterator _start = nullptr; // 起始位置
iterator _finish = nullptr; // 结束位置(尾后)
iterator _endofstorage = nullptr; // 容量末尾
};
} // namespace bit
集成到 list
// ==================== list.h ====================
#include "ReverseIterator.h"
namespace bit {
template <class T>
class list {
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
typedef ReverseIterator<const_iterator, const T&, const T*>
const_reverse_iterator;
// ===== 正向迭代器(带头双向循环链表) =====
iterator begin() { return _head->_next; } // 头结点的下一个
iterator end() { return _head; } // 头结点本身就是"尾后"
const_iterator begin() const { return _head->_next; }
const_iterator end() const { return _head; }
// ===== 反向迭代器 =====
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// ... list 的其他成员函数 ...
private:
Node* _head; // 头结点(哨兵结点)
size_t _size;
};
} // namespace bit
测试验证
// ==================== test.cpp ====================
#include "list.h"
#include "vector.h"
#include <iostream>
using namespace std;
int main() {
// ===== 测试 list 的反向迭代器 =====
bit::list<int> lt = { 1, 2, 3, 4 };
bit::list<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend()) {
cout << *rit << " "; // 预期输出: 4 3 2 1
++rit;
}
cout << endl;
// ===== 测试 vector 的反向迭代器 =====
bit::vector<int> v = { 1, 2, 3, 4 };
bit::vector<int>::reverse_iterator vrit = v.rbegin();
while (vrit != v.rend()) {
cout << *vrit << " "; // 预期输出: 4 3 2 1
++vrit;
}
cout << endl;
return 0;
}
运行结果:
4 3 2 1
4 3 2 1
完美!🎉 无论是链表还是数组,同一个 ReverseIterator 模板类都能正确适配,这就是模板编程 + 适配器模式的强大之处。
四、总结与思考
本文通过两个硬核案例带领大家深入理解了 C++ 中栈与迭代器适配器的高级应用。让我们做一次回顾和升华:
关于栈
栈不仅仅是一个 push/pop/top 的简单结构。在表达式求值中,栈承担了两个关键角色:
- 运算符栈(中缀转后缀阶段):利用 LIFO 特性天然地实现了运算符优先级的延迟处理——优先级低的操作符被压在栈底,等高优先级的先出来算完;
- 运算数栈(后缀求值阶段):暂存中间计算结果,"弹出两个、算完压回一个"的节奏恰好匹配后缀表达式的求值顺序。
更深一层想,凡是涉及 “暂存、等待、按序取回” 的场景——括号匹配、函数调用栈、浏览器的前进后退、撤销重做(Undo/Redo)——都有栈的身影。理解栈就是理解"后进先出"这种时间上的嵌套关系。
关于反向迭代器
反向迭代器是适配器设计模式的教科书级案例:
- 不修改原有接口——正向迭代器的所有操作保持不变;
- 通过包装(Wrap)而非继承来实现功能反转——
ReverseIterator内部持有一个正向迭代器,所有操作都委托给它,只是方向取反; - 对称设计的优雅——
rbegin()↔end()、rend()↔begin()的映射关系,使得反向遍历的使用方式与正向遍历完全一致:for (auto it = rbegin(); it != rend(); ++it); - 模板的泛化能力——一个
ReverseIterator模板可以适配任何满足双向迭代器要求的容器(vector、list、deque、set、map……),这就是 Generic Programming(泛型编程) 的威力。
从这两个案例看 STL 设计哲学
细心的读者可能已经发现,本文的两个主题有一个共同的底层思想:通过"包装"实现功能的增强和转变。
- 栈 = 包装
deque/vector/list→ 限制接口,提供 LIFO 语义 - 反向迭代器 = 包装正向迭代器 → 改变遍历方向,保持接口一致
这就是 STL 中无处不在的**适配器(Adapter)**思想——不是重新发明轮子,而是在已有组件基础上,通过巧妙的封装来实现新的抽象。理解这一点,你对整个 STL 的认知会上升到一个新的层次。
2117



被折叠的 条评论
为什么被折叠?



