迭代器
本文介绍四种迭代器的使用细节,已经相互转换的方法,从而提高对迭代的理解和使用。
1. iterator优于const_iterator, const_reverse_iterator, reverse_iterator
iterator几乎可以适用于所有需要迭代器为参数的函数调用。但其他的三种迭代却不一定。
以下这张图可以清晰地表明不同迭代器之间的转换关系。
我们需要知道的是,有些版本的insert和erase是没有给出const_iterator的重载版本的,所以如果要在相应位置做操作就必须把他们转换为iterator才能进行。请注意,不要考虑强制类型转换,这总是不太好的选择。后面我们将给出转换的方法。
2. const_iterator转换为iterator
之前提到,不要考虑强制类型转换,是因为这两个类型是完全不同的类型!(对vector和string可能不适用)所以把两个完全不同的类型相互转换是非常没有道理的。
(在vector和string里,迭代器会被实现为T* 和 const T*,他们之间是可以进行隐式类型转换的。)
所以,我们希望得到一种更加普遍可用的方法。
这里我们需要使用两个成员函数来解决这个问题。
#include <iostream>
#include <fstream>
#include <deque>
using namespace std;
int main() {
typedef deque<int> intDeq;
typedef intDeq::iterator iter;
typedef intDeq::const_iterator citer;
intDeq d;
citer ci;
iter i(d.begin());
advance(i, distance<citer>(i, ci));
return 0;
}
这里需要注意的是不能直接调用distance函数。正如我们前面说的,iterator和const_iterator是两个不一样的类型,所以必须给出确定的类型来做比较。
效率上来说,这种方法对于随机访问的容器是常数时间操作,对于双向迭代器来说是线性时间操作。所以效率上来说是不太好的。
3. 正确理解reverse_iterator的base函数
base函数并不是真正的把reverse_iterator转换iterator, 其中还有一个偏移量的问题。
这是base调用的偏移量。
因为reverse本来就是从右往左遍历,所以插入的操作理所应当在iter的右边,所以insert操作和我们预想的结果是一样的。
但是对于erase则不是这样的。因为base会自动往右移一个偏移量,所以我们需要修正这个编译量,例如,而这很简单。
v_i.erase((++iter).base());
4. istreambuf_iterator来作为字符的输入
除了传统的使用文件操作的方法,我们还可以使用迭代器来实现读取文件内容。
通过设定dataFile.unsetf(ios::skipws);
,我们可以实现忽略空格的输入。
#include <iostream>
#include <fstream>
using namespace std;
int main() {
fstream dataFile("data.txt");
string str((istream_iterator<char>(dataFile)), istream_iterator<char>());
dataFile.unsetf(ios::skipws);
copy(str.begin(), str.end(), ostream_iterator<char>(cout));
return 0;
}
但更简单的说法是:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
fstream dataFile("data.txt");
string str((istreambuf_iterator<char>(dataFile)), istreambuf_iterator<char>());
copy(str.begin(), str.end(), ostream_iterator<char>(cout));
return 0;
}