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.
-
Valid values: Is
the iterator dereferenceable? For example, writing
"*e.end()" is always a programming error.
-
Valid
lifetimes: Is the iterator still valid when it's being used? Or has
it been invalidated by some operation since we obtained it?
-
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?
-
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.)
|