Effective STL之迭代器

  标准STL容器提供了四种不同的迭代器:iterator、const_iterator、reverse_iterator和const_reverse_iterator。

一、尽量用iterator代替const_iterator,reverse_iterator和const_reverse_iterator

  每个标准容器类都提供四种迭代器类型。对于container而言,iterator的作用相当于T*,而const_iterator则相当于const T*;增加一个iterator或者const_iterator可以在一个从容器开头趋向尾部的遍历中让你移动到容器的下一个元素。
  reverse_iterator与const_reverse_iterator同样相当于对应的T*和const T*,所不同的是,增加reverse_iterator或者const_reverse_iterator会在从尾到头的遍历中让你移动到容器的下一个元素。

  迭代器使用的一个重要指导方针是:尽量使用iterator代替其他三种迭代器,原因有:

  • insert和erase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用const或reverse iterators。

  • 不可能把const_iterator隐式转换成iterator。

  • 从reverse_iterator转换而来的iterator在使用之前可能需要相应的调整。

二、用distance和advance把const_iterator转化成iterator

  如果你只有一个const_iterator,而你要在它所指向的容器位置上插入新元素呢?也就是如何把const_iterator转化为iterator呢?(并不存在从const_iterator到iterator之间的隐式转换)

typedef deque<int> IntDeque; // 和以前一样

typedef IntDeque::iterator Iter;

typedef IntDeque::const_iterator ConstIter;

IntDeque d;

ConstIter ci;

... // 让ci指向d

Iter i(d.begin()); // 初始化i为d.begin()

advance(i, distance <ConstIter> (i, ci)); // 把i移到指向ci位置

【Note】:
(1)distance返回两个指向同一个容器的iterator之间的距离;advance则用于将一个iterator移动指定的距离。如果i和ci指向同一个容器,那么表达式advance(i, distance(i, ci))会将i移动到与ci相同的位置上。

三、了解如何通过reverse_iterator的base得到iterator

  调用reverse_iterator的base成员函数可以产生“对应的”iterator,但这句话有些辞不达意。举个例子,看一下这段代码,我们首先把从数字1-5放进一个vector中,然后产生一个指向3的reverse_iterator,并且通过reverse_iterator的base初始化一个iterator:

vector<int> v;
v.reserve(5);     
for(int i = 0;i < 5; ++ i)  // 向vector插入1到5
{  
     v.push_back(i);
} 
vector<int>::reverse_iterator ri = find(v.rbegin(), v.rend(), 3);// 使ri指向3
vector<int>::iterator i(ri.base());   // 使i和ri的base一样

【Note】:
(1)要实现在一个reverse_iterator ri指出的位置上插入新元素,在ri.base()指向的位置插入就行了。对于insert操作而言,ri和ri.base()是等价的,而且ri.base()真的是ri对应的iterator。
(2)要实现在一个reverse_iterator ri指出的位置上删除元素,就应该删除ri.base()的前一个元素。对于删除操作而言,ri和ri.base()并不等价,而且ri.base()不是ri对应的iterator。

vector<int> v;
...      // 向v插入1到5,同上
vecot<int>::reverse_iterator ri =
 find(v.rbegin(), v.rend(), 3);  // 同上,ri指向3
v.erase(--ri.base());    // 尝试删除ri.base()前面的元素;
      // 对于vector,一般来说编译不通过

  C和C++都规定了不能直接修改函数返回的指针,所以在string和vector的迭代器是指针的STL平台上,像–ri.base()这样的表达式无法通过编译。要移植从一个由reverse_iterator指出的位置删除元素时,你应该尽量避免修改base的返回值。没问题。如果你不能减少调用base的返回值,只需要先增加reverse_iterator的值,然后再调用base!

...      // 同上
v.erase((++ri).base());    // 删除ri指向的元素;
      // 这下编译没问题了!

  因为这个方法适用于所有的标准容器,这是删除一个由reverse_iterator指出的元素时首选的技巧。

四、对于逐个字符的输入时考虑使用istreambuf_iterator

  istream_iterators所依靠的operator<<函数进行的是格式化输入,这意味着每次你调用的时候它们都必须做大量工作。它们必须建立和销毁岗哨(sentry)对象(为每个operator<<调用进行建立和清除活动的特殊的iostream对象),它们必须检查可能影响它们行为的流标志(比如skipws),它们必须进行全面的读取错误检查,而且如果它们遇到问题,它们必须检查流的异常掩码来决定是否该抛出一个异常。如果进行格式化输入,那些都是重要的活动,但如果你需要的只是从输入流中抓取下一个字符,那就过度了。

  一个更高效的方法是使用STL最好的秘密武器之一:istreambuf_iterators。你可以像istream_iterator一样使用istreambuf_iterator,但istream_iterator对象使用operator<<来从输入流中读取单个字符。istreambuf_iterator对象进入流的缓冲区并直接读取下一个字符。(更明确地说,一个istreambuf_iterator 对象从一个istream s中读取会调用s.rdbuf()->sgetc()来读s的下一个字符。)

ifstream inputFile("interestingData.txt");
string fileData((istreambuf_iterator<char>(inputFile)),
   istreambuf_iterator<char>());

【Note】:
(1)如果你需要一个一个地读取流中的字符,你不需要格式化输入的威力,你关心的是它们花多少时间来读取流,和明显的性能提高相比,为每个迭代器多键入三个字符的代价是微弱的。对于无格式的一个一个字符输入,你总是应该考虑使用istreambuf_iterator。

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/80010246
今日推荐