Abstraction is quite likely the most powerful tool in programming. We've seen it applied as "procedural abstraction" (i.e. functions) and in "abstract data types" (i.e. classes), and we'll add another today - abstracting the process of "iteration" or "traversal" over a sequence or a container.
To do this, we'll first define a common interface for iteration. But not all containers will naturally conform to this interface - traversing over an array looks a whole lot different than traversing over a linked list. So, we'll define custom objects called "iterators" for each different kind of sequence or container that act as the "tour guide" that conforms to our common interface but handles the container-specific details behind the scenes.
We briefly covered the use of iterators from the standard library in a previous lecture. We hadn't done that in previous terms, so in some of the older videos here it will seem like I'm introducing them for the first time. That said, it's been a while, and the review is probably useful anyway. Plus, much of the material here is new, since it concerns actually implementing our own iterators, not just using provided ones.
In some of the videos, I might refer to implementing a linked list and its iterators on project 4. That's project 5 this term.
1: Warm Up Exercise
1.1
1.1 Exercise: Warm Up
Let's take a look at two functions that traverse and print out different kinds of containers:
Briefly answer the following questions. (A word or short phrase is sufficient!)
|
2: Iterators: The Big Idea
It would be nicer if we could write a single version of
What is this doing? At a high level, we've got a function template that can be flexible to accommodate different The last piece of the puzzle is how we get the
In essence, we presume that containers have objects called iterators that we can get by calling |
3: Linked List Iterators
3.1
Let's fill in some more details and work through an example of actually creating an iterator for our linked list class… In the previous video, we implemented the prefix increment operator, i.e. Okay, what about the Now that we've covered the data representation and operator implmentation, we'll look at constructing iterators and how the linked list provides them via the Finally, writing out the type of an iterator like
3.1 Exercise: Linked List Memory Management
As we finish building the Let's work through a couple exercises to assess the situation. First, let's think about shallow vs. deep copies. Consider the code below, and draw out a memory diagram, tracing through the code to the final state of memory (assuming the built-in implementation of the copy constructor for
Consider your diagram…does everything look as it should, even though the copy of the iterator did not also result in a deep copy of the node it was pointing to? Explain your reasoning. We can also consider whether
Consider the same You're welcome to check your solution with this walkthrough video: |
4: Generalizable Function Templates Using Iterators
4.1
Finally, we'll take a look back at our original goal - write flexible functions that treat iteration via iterators as an abstraction so that they aren't fixed to work with only a single kind of container.
4.1 Exercise: Generic
length() Function Template with Iterators
Consider a generic For example, we could use it like this:
Or like this!
Determine which of the following potential implementations of
You're welcome to check your solution with this walkthrough video: |
5: Iterator Validity
One last thing… Iterators are kind of like "fancy pointers", and we've got the concept of a "dangling pointer" (a pointer to an object that's no longer safe to use). We have a parallel concept for iterators, referred to as an "invalid", "invalidated", or "dangling" iterator. |