EffectiveSTL5

条款26

尽量使用iterator 代替const_iterator, reverse_iterator, const_reverse_iterator

iterator insert(iterator position, const T& x);
iterator erase(iterator position);
iterator erase(iterator rangeBegin, iterator rangeEnd);

需要注意的是:这些方法只接受iterator类型的参数,而不是const_iterator、reverse_iterator或const_reverse_iterator。

                  ------>const_iterator  <-----------------base()

iterator     --------------------->reverse_iterator---------->const_reverse_iterator

                 <-----------base()   

  • insert和erase的一些版本要求iterator。如果你需要调用这些函数,你就必须产生iterator,而不能用const或reverse iterators。
  • 不可能把const_iterator隐式转换成iterator,我们将会在条款27中讨论从一个const_iterator产生一个iterator的技术并不普遍适用,而且不保证高效。
  • 从reverse_iterator转换而来的iterator在转换之后可能需要相应的调整,在条款28中我们会讨论何时需要调整以及调整的原因。

当在iterator和const_iterator之间作选择的时候,你有更充分的理由选择iterator,即使const_iterator同样可行而且即使你并不需要调用容器类的任何成员函数。

其中的令人讨厌的原因包括iterator与const_iterator之间的比较。避免这类问题的最简单的方法是减少混用不同类型的迭代器的机会,换句话说,又回到了尽量用iterator代替const_iterator。

条款27

用distance 和advance 把const_iterator 转化成iterator

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

typedef IntDeque::iterator Iter;

typedef IntDeque::const_iterator ConstIter;

IntDeque d;ConstIter ci;

Iter i(d.begin());

advance(i, distance(i, ci)); // 编译不过????

看distance?

template<typename InputIterator>
typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);
advance(i, distance(i, ci));			// 调整i,指向ci位置

有两个参数传递给distance,i和ci。i的类型是Iter,即deque<int>::iterator的typedef。对编译器来说,这表明调用distance的InputIterator是deque<int>::iterator。但ci是ConstIter,即deque<int>::const_iterator的typedef。这表明那个InputIterator是deque<int>::const_iterator。InputIterator不可能同时有两种不同的类型,所以调用distance失败。

要顺利地调用distance,你需要排除歧义。最简单的办法就是显式的指明distance调用的模板参数类型,从而避免编译器自己得出它们的类型:

advance(i, distance<ConstIter>(i, ci));

条款28

通过reverse_iterator 的base 得到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,一般来说编译不通过

修改后

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

条款29

需要一个一个字符输入时考虑使用istreambuf_iterator

ifstream inputFile("interestingData.txt");
string fileData((istream_iterator<char>(inputFile)),		// 把inputFile读入
			istream_iterator<char>());		// fileData;关于为什么
							// 它不是很正确请看下文
							// 关于这个语法的警告
							// 参见条款6

很快你就会发现这种方法无法把文件中的空格拷贝到字符串中。那是因为istream_iterators使用operator>>函数来进行真的读取,而且operator>>函数在默认情况下忽略空格。

ifstream inputFile("interestingData.txt");
inputFile.unset(ios::skipws);				// 关闭inputFile的
							// 忽略空格标志
string fileData((istream_iterator<char>(inputFile)), istream_iterator<char>());

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

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

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

注意这里不需要“unset”skipws标志,istreambuf_iterator不忽略任何字符。它们只抓取流缓冲区的下一个字符。

            

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值