(转)Item 1. Iterators

Every programmer who uses the standard library has to be aware of these common and not-so-common iterator mistakes. How many of them can you find?

 

The following program has at least four iterator-related problems. How many can you find?

 

int main() 
{
  vector<Date> e;
  copy( istream_iterator<Date>( cin ),
        istream_iterator<Date>(),
        back_inserter( e ) );
  vector<Date>::iterator first =
        find( e.begin(), e.end(), "01/01/95" );
  vector<Date>::iterator last =
        find( e.begin(), e.end(), "12/31/95" );
  *last = "12/30/95";
  copy( first,
        last,
        ostream_iterator<Date>( cout, "/n" ) );
  e.insert( --e.end(), TodaysDate() );
  copy( first,
        last,
        ostream_iterator<Date>( cout, "/n" ) );
}
 
 

Solution

int main() 
{
  vector<Date> e;
  copy( istream_iterator<Date>( cin ),
        istream_iterator<Date>(),
        back_inserter( e ) );

This is fine so far. The Date class writer provided an extractor function with the signature operator>>( istream&, Date& ), which is what istream_iterator<Date> uses to read the Dates from the cin stream. The copy() algorithm just stuffs the Dates into the vector.

vector<Date>::iterator first = 
      find( e.begin(), e.end(), "01/01/95" );
vector<Date>::iterator last =
      find( e.begin(), e.end(), "12/31/95" );
*last = "12/30/95";

Error: This may be illegal, because last may be e.end() and therefore not a dereferenceable iterator.

The find() algorithm returns its second argument (the end iterator of the range) if the value is not found. In this case, if "12/31/95" is not in e, then last is equal to e.end(), which points to one-past-the-end of the container and is not a valid iterator.

copy( first, 
      last,
      ostream_iterator<Date>( cout, "/n" ) );

Error: This may be illegal because [first,last) may not be a valid range; indeed, first may actually be after last.

For example, if "01/01/95" is not found in e but "12/31/95" is, then the iterator last will point to something earlier in the collection (the Date object equal to "12/31/95") than does the iterator first (one past the end). However, copy() requires that first must point to an earlier place in the same collection as last—that is, [first,last) must be a valid range.

Unless you're using a checked version of the standard library that can detect some of these problems for you, the likely symptom if this happens will be a difficult-to-diagnose core dump during or sometime after the copy().

e.insert(--e.end(), TodaysDate() ); 

First error: The expression "--e.end()" is likely to be illegal.

The reason is simple, if a little obscure: On popular implementations of the standard library, vector<Date>::iterator is often simply a Date*, and the C++ language doesn't allow you to modify temporaries of builtin type. For example, the following plain-jane code is also illegal:

Date* f();    // function that returns a Date* 
p = --f();    // error, but could be "f() - 1"

Fortunately, we know that vector<Date>::iterator is a random-access iterator, so there's no loss of efficiency in writing this (more) correctly as:

e.insert( e.end() - 1, TodaysDate() ); 

Second error: Now you still have the other error, which is: If e is empty, any attempt to take "the iterator before e.end()" (whether you spell that "--e.end()" or "e.end()–1") will not be a valid iterator.

    copy( first, 
          last,
          ostream_iterator<Date>( cout, "/n" ) );
}

Error: first and last may not be valid iterators any more.

A vector grows in "chunks" so that it won't have to reallocate its buffer every time you insert something into it. However, sometimes the vector will be full, and adding something will trigger a reallocation.

Here, as a result of the e.insert() operation, the vector may or may not grow, which means its memory may or may not move. Because of this uncertainty, we must consider any existing iterators into that container to be invalidated. In this case, if the memory really did move, then the buggy copy() will again generally manifest as a difficult-to-diagnose core dump.

Guideline

 

Never dereference an invalid iterator.


To summarize: When using iterators, be aware of four main issues.

  1. Valid values: Is the iterator dereferenceable? For example, writing "*e.end()" is always a programming error.

  2. Valid lifetimes: Is the iterator still valid when it's being used? Or has it been invalidated by some operation since we obtained it?

  3. Valid ranges: Is a pair of iterators a valid range? Is first really before (or equal to) last? Do both really point into the same container?

  4. Illegal builtin manipulation: For example, is the code trying to modify a temporary of builtin type, as in "--e.end()" above? (Fortunately, the compiler can often catch this kind of mistake for you, and for iterators of class type, the library author will often choose to allow this sort of thing for syntactic convenience.)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值