Implementing a Doubly Linked List Iterator

This lesson contains approximately 29 minutes of video content.

Set-up

Implementing a DoublyLinkedListIterator<T>

Updating DoublyLinkedList<T> to use DoublyLinkedListIterator<T>

Using DoublyLinkedList<T>'s iterators

Activity: Doubly linked list iterator implementation

Graded Playground Autograder

Activity Prompt:

In this programming problem, you will implement a forward iterator type DoublyLinkedListIterator<T> for a DoublyLinkedList<T>. To understand the DoublyLinkedList<T>'s structure, you should read includes/doubly_linked_list.hpp and includes/node.hpp. Understanding the data structure for which you're writing an iterator is important.

After reviewing the structure of DoublyLinkedList<T>, you will implement a forward iterator type DoublyLinkedListIterator<T> for it. The member functions that you are responsible for follow.

Member functions you must implement for DoublyLinkedListIterator<T>
public:
DoublyLinkedListIterator(Node<T>* ptr); Parameterized constructor. Initializes current_ with the value stored in ptr.
T& operator*(); Returns a reference to the data member of the node pointed to by current_.
DoublyLinkedListIterator<T>& operator++(); Pre-increment Operator (invoked as ++obj). This will move current_ one node forward in the sequence of nodes contained within the doubly linked list. This operator will return a self-reference (i.e., return *this) after the increment has been performed.
DoublyLinkedListIterator<T> operator++(int); Post-increment Operator (invoked as obj++). A temporary DoublyLinkedListIterator<T> tmp is initialized with the state of this iterator object. You can accomplish this using the copy constructor in conjunction with *this. After recording the state of this iterator object in tmp, this->current_ is moved forward to the next node in the sequence of nodes contained within the doubly linked list. You then return tmp, which represents the state of this iterator prior to the increment.
bool DoublyLinkedListIterator<T>::operator!=( const DoublyLinkedListIterator<T>& other); Returns true if the two iterators are not pointing to the same node in the sequence; false otherwise.
Member functions provided to you for DoublyLinkedListIterator<T>
public:
DoublyLinkedListIterator() = default; Default constructor.
DoublyLinkedListIterator<T>'s data members
private:
Node<T>* current_ Pointer to the node in the doubly linked list's sequence to which the iterator currently "points".

We will only transmit the contents of your doubly_linked_list_iterator.hpp file to the grader. Let's get started!

#include <iostream> #include "doubly_linked_list.hpp" int main() { DoublyLinkedList<int> list; list.push_back(11); list.push_back(12); for (auto i = list.begin(); i != list.end(); ++i) std::cout << *i << std::endl; }
#ifndef DOUBLY_LINKED_LIST_ITERATOR_HPP #define DOUBLY_LINKED_LIST_ITERATOR_HPP #include <iterator> #include "node.hpp" template <typename T> class DoublyLinkedListIterator : std::iterator<std::forward_iterator_tag, T> { public: DoublyLinkedListIterator() = default; DoublyLinkedListIterator(Node<T>* ptr); T& operator*(); DoublyLinkedListIterator<T>& operator++(); DoublyLinkedListIterator<T> operator++(int); bool operator!=(const DoublyLinkedListIterator<T>& other); private: Node<T>* current_ = nullptr; }; template <typename T> DoublyLinkedListIterator<T>::DoublyLinkedListIterator(Node<T>* ptr) { /* implement this function */ } template <typename T> T& DoublyLinkedListIterator<T>::operator*() { /* implement this function */ } template <typename T> DoublyLinkedListIterator<T>& DoublyLinkedListIterator<T>::operator++() { /* implement this function */ } template <typename T> DoublyLinkedListIterator<T> DoublyLinkedListIterator<T>::operator++(int) { /* implement this function */ } template <typename T> bool DoublyLinkedListIterator<T>::operator!=( const DoublyLinkedListIterator<T>& other) { /* implement this function */ } #endif
#ifndef DOUBLY_LINKED_LIST_HPP #define DOUBLY_LINKED_LIST_HPP #include <cstdlib> // provides definition for size_t, the maximum size of a theoretically possible object of any type #include "doubly_linked_list_iterator.hpp" #include "node.hpp" template <typename T> class DoublyLinkedList { public: DoublyLinkedList() = default; DoublyLinkedList(const DoublyLinkedList<T>& rhs); DoublyLinkedList<T>& operator=(const DoublyLinkedList<T>& rhs) = delete; ~DoublyLinkedList(); void push_back(T val); using iterator = DoublyLinkedListIterator<T>; iterator begin() { return DoublyLinkedListIterator<T>(head_); } iterator end() { return DoublyLinkedListIterator<T>(nullptr); } protected: Node<T>* head_ = nullptr; Node<T>* tail_ = nullptr; size_t size_ = 0; }; template <typename T> void DoublyLinkedList<T>::push_back(T val) { Node<T>* n = new Node<T>(val); if (head_ == nullptr) head_ = n; if (tail_ != nullptr) tail_->next = n; n->prev = tail_; tail_ = n; size_ += 1; } template <typename T> DoublyLinkedList<T>::DoublyLinkedList(const DoublyLinkedList<T>& rhs) { Node<T>* curr = rhs.head_; while (curr != nullptr) { push_back(curr->data); curr = curr->next; } } template <typename T> DoublyLinkedList<T>::~DoublyLinkedList() { while (head_ != nullptr) { Node<T>* tmp = head_->next; delete head_; head_ = tmp; } } #endif
#ifndef NODE_HPP #define NODE_HPP template <typename T> struct Node { Node() = default; Node(T data) : data(data) {} T data; Node *next = nullptr; Node *prev = nullptr; }; #endif