user11505855
user11505855

Reputation: 21

How to Implement a Iterator class in a LinkedList class

New to c++ and I'm having trouble implementing an Iterator class in my LinkedList. I have a Iterator class defined in the private section of my LinkedList class as follows:

cs_linked_list.h

 #ifndef LINKED_LIST_H_
 #define LINKED_LIST_H_

 #include <initializer_list>
 #include <iostream>

 namespace cs {
template <typename T>
class LinkedList {
    struct Node;  // forward declaration for our private Node type

public:
    /**Constructs an empty list.**/
    LinkedList(){
        head_ = nullptr;
    }
    /**
    * @brief Constructs a list from a range.
    *
    */
    template <class InputIterator>
    LinkedList(InputIterator first, InputIterator last) {
        for (; first != last; ++first) this->push_back(*first);
    }

    /** Constructs a list with a copy of each of the elements in `init_list`, in the same order. */
    LinkedList(std::initializer_list<T> init_list) {
        this->operator=(init_list);  // call initializer list assignment
    }

    /**Constructs a container with a copy of each of the elements in another, in the same order**/
    LinkedList(const LinkedList<T>& another){
        //TODO
        this->operator=(another);


    }
    /** Destroys each of the contained elements, and deallocates all memory allocated by this list. */
    ~LinkedList() {
        while (this->head_) {
           Node* old_head = this->head_;
           this->head_ = old_head->next;
           delete old_head;
         }
    }
    /**Returns the number of elements in this list.Node *head**/
     size_t size() const{ //DONE
        //TODO
        Node *temp = head_;
        size_t len = 0;
        while (temp != nullptr){
            len++;
            temp = temp->next;
        }
        return len;
    }
    /**Returns whether the list container is empty (that is, whether its size is 0). **/
    bool empty() const{ //DONE
        if(size_ == 0){
            return true;
        } else{
            return false;
        }
    }
    /** Appends a copy of `val` to this list. */
    void push_back(const T& val) {
        Node* new_node = new Node{val};
        if (this->size_ == 0) {
            this->head_ = this->tail_ = new_node;
        } else {
            this->tail_->next = new_node;
            new_node->prev = this->tail_;
            this->tail_ = new_node;
        }
        ++this->size_;
    }
    /**  Prepends a copy of `val` to this list. */
    void push_front(const T& val) {
        Node* new_node = new Node{val};  
        if (this->size_ == 0) {
            this->head_ = this->tail_ = new_node;
        } else {
            new_node->next = this->head_;
            this->head_->prev = new_node;
            this->head_ = new_node;
        }
        ++this->size_;
    }

    /**Returns a reference to the value in the first element in this list.**/
    T& front() const{
        return head_->data;
    }

    /**Returns a reference to the value in the last element in this list. **/
    T& back() const{
        return tail_->data;
    }

    /**Deletes the first value in this list. **/
    void pop_front(){
        Node *temp = head_;
        if(empty()){
            return;
        }
        if(temp == tail_){
            return;
        }
        head_ = head_->next;
        if (head_ != nullptr) {
            head_->prev = nullptr;
        } else {
            tail_ = nullptr;
        }
        delete temp;

    }

    /**Deletes the last value in this list**/
    void pop_back(){
        if(empty()){
            return;
        }
        if(head_ == tail_){
            return;
        }
        Node *temp = head_;
        while(temp->next->next != nullptr){
            temp = temp->next;
        }
        tail_ = temp;
        delete tail_->next;
        tail_->next = nullptr;
        size_--;

    }

    /**resizes the list so that it contains n elements.**/
    void resize(std::size_t n){
        //TODO
        for (size_t i = 0; i < n; i++){
            push_back('\0');
        }

    }

    /**resizes the list so that it contains n elements**/
    void resize(std::size_t n, const T &fill_value){
        //TODO
        for (size_t i = 0; i < n; i++) {
            push_back(fill_value);
        }
    }
    /**Removes from the container all the elements that compare equal to val. **/
    void remove(const T &val){
        //TODO
        Node *p1 = head_;
        Node *p2 = nullptr;
        if(p1 != nullptr && p1->data == val){
            head_ = p1->next;
            delete p1;
        } else {
            while (p1 != nullptr && p1->data != val){
                p2 = p1;
                p1 = p1->next;
            }
            if (p1 == nullptr) {
                return;
            }
            p2->next = p1->next;
            delete p1;
        }
    }

    /**Removes duplicate values from this list**/
    void unique(){
        //TODO
        Node *temp = head_;
        while (temp != nullptr && temp->next != nullptr) {
            Node *temp2 = temp;
            while (temp2->next != nullptr) {
                if (temp->data == temp2->next->data) {
                    Node *temp3 = temp2->next;
                    temp2->next = temp2->next->next;
                    delete temp3;
                } else {
                    temp2 = temp2->next;
                }
            }
            temp = temp->next;
        }
    }
    /**Deletes all values in this list.**/
    void clear(){
        //TODO
        while (head_ != nullptr){
            Node *temp = head_;
            head_ = head_->next;
            delete temp;
        }
        tail_ = nullptr;
        size_ = 0;
    }

    /**Reverses the order of the elements in this list.**/
    void reverse(){
        //TODO
        Node *p1 = head_;
        Node *p2 = nullptr;
        while (p1 != nullptr) {
            Node *temp = p1->next;
            p1->next = p2;
            p2 = p1;
            p1 = temp;
        }
        head_ = p2;
    }
    /** Replaces the contents of this list with a copy of each element in `init_list`. */
    LinkedList& operator=(std::initializer_list<T> init_list) {
        this->size_ = 0;
        for (auto&& val : init_list)
            this->push_back(val);
        return *this;
    }

    /**Replaces the contents of this list with a copy of each element in another, in the same order.**/
     LinkedList& operator=(const LinkedList& another){
         //TODO
        if (this != &another) {
            this->clear();
            Node *temp = another.head_;
            this->size_ = 0;
            while (temp) {
                this->push_back(temp->data);
                temp = temp->next;
            }
        }
        return *this;
    }
    /**Compares this list with another for equality.**/
    bool operator==(const LinkedList &another){ //DONE
        //TODO
        auto comp = head_;
        auto comp2 = another.head_;
        while(comp != nullptr){
            if(comp != comp2){
                return false;
            }
            comp = comp->next;
            comp2 = comp2->next;
        }
        return true;

    }

    /**Compares this list with another for inequality. **/
    bool operator!=(const LinkedList &another){ //DONE
        //TODO
        auto comp = head_;
        auto comp2 = another.head_;
        while(comp != nullptr){
            if(comp != comp2){
                return true;
            }
            comp = comp->next;
            comp2 = comp2->next;
        }
        return false;

    }
    /** Inserts this list into an ostream, with the format `[element1, element2, element3, ...]` */
    friend std::ostream& operator<<(std::ostream& out, const LinkedList& list) {
        out << '[';
        for (Node* cur = list.head_; cur; cur = cur->next) {
            out << cur->data;
            if (cur->next)
                out << ", ";
        }
        out << ']';
        return out;
    }private:
    struct Node {
        T data;
        Node* next = nullptr;
        Node* prev = nullptr;
    };
    Node* head_ = nullptr;
    Node* tail_ = nullptr;
    std::size_t size_ = 0;
    class Iterator {
    public:
        using iterator_category = std::bidirectional_iterator_tag;
        using value_type = T;
        using difference_type = int;
        using pointer = T*;
        using reference = T&;
        // Default constructor
        Iterator() {
            //TODO
            n = nullptr;
        }
        // Copy constructor
        Iterator(const Iterator& other) {
            //TODO
            this->operator=(other);
        }
        //Destructor if needed
        ~Iterator() {
            //TODO
            while (this->list.head_){
                Node *old_head = this->list.head_;
                this->list.head_ = old_head->next;
                delete old_head;
            }
        }
        // Copy assign
        Iterator& operator=(const Iterator& that) {
            //TODO
            if(this != &that){
                this->list.clear();
                Node *temp = that.list.head_;
                this->list.size_ = 0;
                while (temp){
                    this->list.push_back(temp->data);
                    temp = temp->next;
                }
            }
            return *this;

        }
        // Prefix increment
        Iterator& operator++() {
            //TODO
            this->n = this->n->next;
            return *this;
        }
        // Postfix increment
        Iterator operator++(int) {
            Iterator tmp(*this);
            this->operator++();
            return tmp;
        }
        // Prefix decrement
        Iterator& operator--() {
            //TODO
            this->n = this->n->prev;
            return *this;
        }
        // Postfix decrement
        Iterator operator--(int) {
            Iterator tmp(*this);
            this->operator--();
            return tmp;
        }
        // Inequality
        bool operator!=(Iterator that) const {
            return !(this->operator==(that));
        }
        // Equality
        bool operator==(Iterator that) const {
            //TODO
            auto temp = list.head_;
            auto temp2 = that.list.head_;
            while(temp != nullptr){
                if(*temp != *temp2){
                    return false;
                }
                temp = temp->next;
                temp2 = temp2->next;
            }
            return true;
        }
        // lvalue dereference
        T& operator*() const {
            //TODO
            return this->n->data;

        }
        // referring
        Iterator* operator->() const {
            return this;
        }
        Iterator begin(){
        //TODO
        return Iterator(list.head_->next);
        }
        Iterator end(){
        //TODO
        return Iterator(list.tail_);
        }

    private:
        Node *n;
        LinkedList<T> list;
    };
};

}  // namespace cs

#endif  // LINKED_LIST_H_

Main:

#include "cs_linked_list.h"
#include <iostream>

int main() {
/***TESTING***/
cs::LinkedList<int> list;
// Add few items to the end of LinkedList
list.push_back(1);
list.push_back(2);
list.push_back(3);
std::cout << "Traversing LinkedList through Iterator" << std::endl;
for ( cs::LinkedList<int>::Iterator iter = list.begin();iter != list.end(); iter++) {
    std::cout << *iter << " ";

}
std::cout << std::endl;
return 0;
}

Since my Iterator class is private I can't seem to implement my Begin() and End() functions. Should I Make it public or am I missing one crucial step. Instructions say to define a Iterator class in the private section of my LinkedList class.

Upvotes: 0

Views: 767

Answers (1)

LoS
LoS

Reputation: 1833

Since C++20, a linked-list iterator can be implemented in the following way.

template <typename T, typename Ptr>
struct ListIterator
{
  using value_type         = T;
  using pointer            = Ptr;
  using node_pointer       = typename std::pointer_traits<Ptr>::rebind<Node<T>>;
  using reference          = std::iter_reference_t<Ptr>;
  using difference_type    = std::ptrdiff_t;
  using iterator_category  = std::bidirectional_iterator_tag;

  ListIterator() = default;

  explicit ListIterator(node_pointer x) noexcept
   : cur{x}
  {}

  template <std::convertible_to<PtrR> Ptr>
  ListIterator(const ListIterator<PtrR>& x) noexcept
   : cur{x.cur}
  {}

  reference operator*() const noexcept
  { return this->cur->value; }

  pointer operator->() const noexcept
  { return std::pointer_traits<pointer>::pointer_to(this->cur->value); }

  ListIterator& operator++() noexcept
  {
    this->cur = this->cur->next;
    return *this;
  }

  ListIterator operator++(int) noexcept
  {
    ListIterator tmp{*this};
    this->cur = this->cur->next;
    return tmp;
  }

  ListIterator& operator--() noexcept
  {
    this->cur = this->cur->prev;
    return *this;
  }

  ListIterator operator--(int) noexcept
  {
    ListIterator tmp{*this};
    this->cur = this->cur->prev;
    return tmp;
  }

  node_pointer  cur;
};

template <typename T, typename PtrL, typename PtrR>
bool operator==(const ListIterator<T, PtrL>& x, const ListIterator<T, PtrR>& y) noexcept
{ return x.cur == y.cur; }

It allows to merge the iterator and const-iterator classes into a single interface, which deduces the const-qualification from the Ptr template parameter. In this way, it also allows to support fancy pointers.

Upvotes: 0

Related Questions