从弱到强的操作符
上一次课中,我们学习了迭代器的原理和基本作用,了解了迭代器按定义方式进行的分类。
迭代器是为容器提供了统一访问方式的接口,我们之前学习了很多 STL 的容器模版类,是不是所有的容器都可以适用迭代器呢?
我们看一下代码示例:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> container{1, 2, 3};
// 迭代器的定义
vector<int>::iterator i;
// for 循环输出
cout << "--- for ---" << endl;
for (i = container.begin(); i != container.end(); i++) {
cout << *i << "\t";
}
return 0;
}
/*
output:
--- for ---
1 2 3
*/
这是我们上次课做过的关于 vector
的迭代器的使用,但并不意味着所有的容器都可以。
比如,我们使用容器 stack
,仿照上面的方式定义它的迭代器:
stack<int>::iterator i;
会报编译错误,因为 stack
不适用于迭代器。
C++ STL 容器模版类中,适用于迭代器的有以下类型:
vector
deque
list
map
multimap
set
multiset
以下容器模版类不适用于迭代器:
stack
queue
priority_queue
不同类型的迭代器,功能上的强弱也有所不同。
什么是功能上的强弱?就是迭代器所支持的操作种类的多与少,我们看一下代码示例:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> container{1, 2, 3};
vector<int>::iterator i;
// 注意循环条件:i < container.end()
for (i = container.begin(); i < container.end(); i++) {
cout << *i << "\t";
}
return 0;
}
/*
output:
1 2 3
*/
还是之前的例子,需要注意的是,在 for 循环中,我们的循环条件是:
i < container.end()
我们换一下容器,继续看:
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> container{1, 2, 3};
list<int>::iterator i;
// 注意循环条件:i != container.end()
for (i = container.begin(); i != container.end(); i++) {
cout << *i << "\t";
}
return 0;
}
/*
output:
1 2 3
*/
这里使用的是 list
,再来注意循环条件:
i != container.end()
如果在此处使用 <
进行判断是会报编译错误的,而对于 vector
容器,使用 <
与 !=
进行判断都是可以的,这就体现了不同容器使用迭代器功能上的强弱。
所以,按照功能上的强弱,迭代器可以划分为如下的类别:
input iterator
output iterator
forward iterator
bidirectional iterator
random access iterator
这五种类别的迭代器各自都支持哪些运算呢,我们分别看一下:
输入迭代器用于从容器中读取元素,它支持的的操作有:
*A
A -> member
A++
++A
A == B
A != B
代码示例:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vi = {1, 2, 3};
// 迭代器声明
vector<int>::iterator i;
for (i = vi.begin(); i != vi.end(); ++i) {
// 使用迭代器访问元素
cout << (*i) << "\t";
}
return 0;
}
/*
output
1 2 3
*/
这段代码就是我们使用输入迭代器遍历元素的示例。
输出迭代器除了具备输入迭代器的操作之外,还可以为元素赋值:
*A = n
A -> m = n
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> vi = {1, 2, 3};
// 迭代器声明
vector<int>::iterator i;
for (i = vi.begin(); i != vi.end(); ++i) {
// 使用迭代器为元素赋值
*i = 1;
}
// 所有元素都已经改变为 1
return 0;
}
/*
output
*/
在上面的代码中,我们使用输出迭代器为元素赋值。
前向迭代器同时具备了输入迭代器和输出迭代器的功能,既可以访问元素,又可以修改元素。
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> vi = {1, 2, 3};
// 迭代器声明
vector<int>::iterator i;
for (i = vi.begin(); i != vi.end(); ++i) {
// 使用迭代器为元素赋值
*i = 1;
}
for (i = vi.begin(); i != vi.end(); ++i) {
// 使用迭代器访问元素
cout << (*i) << "\t";
}
return 0;
}
/*
output
1 1 1
*/
上述代码中,我们既可以为元素赋值,又可以访问元素,是一个前向迭代器的示例。
双向迭代器,顾名思义,就是在前向迭代器的基础上更近一步,可以实现自减运算
A--
--A
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> vi = {1, 2, 3};
// 迭代器声明
vector<int>::iterator i;
for (i = vi.end(); i != vi.begin(); --i) {
// 使用迭代器访问元素
if (i != vi.end()) {
cout << (*i) << "\t";
}
}
cout << *i;
return 0;
}
/*
output
3 2 1
*/
上述代码使用的循环中,迭代器是进行的自减操作,是双向迭代器的示例。
这是所有迭代器中功能最强的一种,随机访问迭代器在双向迭代器的基础上,可以随机访问任意元素,它所支持的操作有:
A < B
A > B
A <= B
A >= B
A - B
A + n
B - n
A += n
B -= n
A[n]
代码示例:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> v1 = {1, 2, 3};
// 声明迭代器 1
vector<int>::iterator i1;
// 声明迭代器 2
vector<int>::iterator i2;
// i1 指向 begin
i1 = v1.begin();
// i2 指向 end
i2 = v1.end();
// 比较大小
if (i1 < i2) {
cout << "i1 < i2" << endl;
}
// 计算距离
int distance = i2 - i1;
cout << "distance = " << distance << endl;
// 算术运算
i1 = i1 + 1;
cout << "i1 + 1 = " << *i1 << endl;
// 二元赋值
i2 -= 2;
cout << "i2 -= 2 = " << *i2 << endl;
// 随机访问
cout << "i1[1] = " << i1[1] << endl;
return 0;
}
/*
output
i1 < i2
distance = 3
i1 + 1 = 2
i2 -= 2 = 2
i1[1] = 3
*/
上述代码展示了一个随机访问迭代器的多种运算。
最后,我们来看一下我们学过的各种容器类都适用于哪一类迭代器:
容器 | 迭代器功能 |
---|---|
vector | 随机访问迭代器 |
deque | 随机访问迭代器 |
list | 双向迭代器 |
set / multiset | 双向迭代器 |
map / multimap | 双向迭代器 |
stack | 不支持迭代器 |
queue | 不支持迭代器 |
priority_queue | 不支持迭代器 |
我们发现,只有 vector
和 deque
是支持随机访问迭代器的,所以我们上面的示例都是用的 vector
,而 list
,set
和 map
都是双向迭代器。