Show Lecture.IteratorInterface as a slide show.
CS253 Iterator Interface
VGA, RJ-45 Ethernet, DisplayPort, USB 2.0 interfaces
Big Picture
- How do you write a container? Something like vector, or list?
- This is CS253, so let’s just assume the Data Structures aspect.
- Specifically, I want a container that works with a for loop:
deque<int> con = {11, 22, 33, 44, 55};
for (auto value : con)
cout << value << ' ';
11 22 33 44 55
- The container must have these attributes:
- a type
iterator
, with *
, !=
, and ++
.
- methods
.begin()
and .end()
that return an iterator
- Really, that’s it! The devil is in the details.
- This has nothing to do with templates.
That’s a whole separate aspect.
Why
- Why does a for loop need
iterator
, .begin()
, and .end()
?
- Because a for loop gets translated, like this:
deque<int> con = {11, 22, 33, 44, 55};
for (auto value : con)
cout << value << ' ';
11 22 33 44 55
deque<int> con = {11, 22, 33, 44, 55};
for (auto it=con.begin(); it != con.end(); ++it) {
auto value = *it;
cout << value << ' ';
}
11 22 33 44 55
- Really, it uses the begin() & end() free functions, which call the
methods, and it saves the end() value in a variable to prevent
computing it multiple times. Details!
Typical structure
class Container {
public:
using value_type = whatever type this contains;
class iterator {
public:
iterator(…); // establish a location
value_type &operator*(); // get value based on state
iterator &operator++(); // go to the next item
bool operator!=(const iterator &) const; // compare two iterators
private:
declare sufficient data to access elements in the container;
}
iterator begin() {
return iterator(enough data to indicate start of container);
}
iterator end() {
return iterator(enough data to indicate end of container);
}
};
Design
- Don’t just start coding like a chicken with its head cut off.
- Stop. Plan.
- Know what you’re doing before you do it.
Don’t be an Underwear Gnome.
- It’s all about the iterator type, which encapsulates the current
position within the container.
- The essential questions are:
- What data does the iterator need to establish a position?
- What iterator value indicates
.begin()
?
- What iterator value indicates
.end()
?
- How do you get the value?
- How does the iterator increment?
Answers, for a string
Since a string s
is essentially a dynamic array of char,
the answers are easy:
- What data does the iterator need to establish a position?
- What iterator value indicates
.begin()
?
- What iterator value indicates
.end()
?
- How do you get the value?
- How does the iterator increment?
Different answers, for a string
Here’s another way to handle a string s
:
- What data does the iterator need to establish a position?
string *pointer; size_t index;
- What iterator value indicates
.begin()
?
- What iterator value indicates
.end()
?
pointer=&s; index=s.size();
- How do you get the value?
- How does the iterator increment?
Answers, for a list
A list is a doubly-linked list of nodes, each of which is a struct
containing the data and a pointer to the next struct or nullptr.
- What data does the iterator need to establish a position?
- What iterator value indicates
.begin()
?
pointer =
the first node in the list, or nullptr
- What iterator value indicates
.end()
?
- How do you get the value?
- How does the iterator increment?
Summary
- Sometimes, all you need is a pointer to the data.
For contiguous storage, this is usually sufficient.
- Sometimes, you need a pointer to the container itself,
and an index into the data.
- For error-checking, a pointer to the data isn’t good enough.
How would you know if you
++
right past the end of the
container, or --
before the start? You need the information
in the container itself to detect that.
- Don’t just copy the container’s data itself into the iterator.
The iterator doesn’t need the actual data,
and that would be slow. Instead, the iterator only needs a pointer.