I hope this is the right way of doing things: I've previously posted a request for a code review and gotten feedback. Now I have new improved code that I would like a code review on again. Old: C++ linked list iterator implementation
Known issues:
- Sentinel node contains a value
- I haven't yet learned how to implement rule of five.
- I don't understand the point of
const_iterator
Node.h
#pragma once
namespace Util
{
template<typename T>
class Node
{
public:
template<typename T> friend class Iterator;
template<typename T> friend class List;
private:
Node(T);
~Node();
void push_back(Node*);
void unlink();
T value;
Node<T>* next;
Node<T>* prev;
};
template<typename T>
Node<T>::Node(T t) : value(t), next(this), prev(this)
{
// ...
}
template<typename T>
Node<T>::~Node()
{
unlink();
}
template<typename T>
void Node<T>::push_back(Node* n)
{
n->next = this;
n->prev = prev;
prev->next = n;
prev = n;
}
template<typename T>
void Node<T>::unlink()
{
next->prev = prev;
prev->next = next;
next = this;
prev = this;
}
}
Iterator.h
#pragma once
#include "Node.h"
namespace Util
{
template<typename T>
class Iterator
{
public:
template<typename T> friend class List;
T& operator*() const;
Iterator& operator++();
Iterator& operator--();
Iterator& operator++(int);
Iterator& operator--(int);
bool operator==(const Iterator& rhs) const;
bool operator!=(const Iterator& rhs) const;
private:
Iterator(Node<T>* n);
Node<T>* node;
};
template<typename T>
Iterator<T>::Iterator(Node<T>* n) : node(n)
{
// ...
}
template<typename T>
T& Iterator<T>::operator*() const
{
return node->value;
}
template<typename T>
Iterator<T>& Iterator<T>::operator++()
{
node = node->next;
return *this;
}
template<typename T>
Iterator<T>& Iterator<T>::operator--()
{
node = node->prev;
return *this;
}
template<typename T>
Iterator<T>& Iterator<T>::operator++(int)
{
Iterator<T> it(*this);
operator++();
return it;
}
template<typename T>
Iterator<T>& Iterator<T>::operator--(int)
{
Iterator<T> it(*this);
operator--();
return it;
}
template<typename T>
bool Iterator<T>::operator==(const Iterator& rhs) const
{
return node == rhs.node;
}
template<typename T>
bool Iterator<T>::operator!=(const Iterator& rhs) const
{
return node != rhs.node;
}
}
List.h
#pragma once
#include "Iterator.h"
namespace Util
{
template<typename T>
class List
{
public:
typedef Iterator<T> Iterator;
typedef Node<T> Node;
List();
~List();
List(const List&) = delete;
List(List&&) = delete;
List& operator=(const List&) = delete;
List& operator=(List&&) = delete;
// Capacity
bool empty() const;
int size() const;
// Modifiers
void push_back(const T&);
void push_front(const T&);
void pop_back();
void pop_front();
void clear();
Iterator insert(Iterator it, const T&);
Iterator erase(Iterator it);
// Element access
T& front() const;
T& back() const;
Iterator begin() const;
Iterator end() const;
private:
Node* head;
int list_size;
};
template<typename T>
List<T>::List() : head(new Node(0)), list_size(0)
{
// ...
}
template<typename T>
List<T>::~List()
{
while (!empty())
{
pop_back();
}
delete head;
}
template<typename T>
bool List<T>::empty() const
{
return head->next == head;
}
template<typename T>
int List<T>::size() const
{
return list_size;
}
template<typename T>
void List<T>::push_back(const T& t)
{
head->push_back(new Node(t));
++list_size;
}
template<typename T>
void List<T>::push_front(const T& t)
{
head->next->push_back(new Node(t));
++list_size;
}
template<typename T>
void List<T>::pop_back()
{
delete head->prev;
--list_size;
}
template<typename T>
void List<T>::pop_front()
{
delete head->next;
--list_size;
}
template<typename T>
void List<T>::clear()
{
Iterator it = begin();
while (!empty())
{
it = erase(it);
}
}
template<typename T>
typename List<T>::Iterator List<T>::insert(Iterator it, const T& t)
{
it.node->push_back(new Node(t));
++list_size;
--it;
return Iterator(it);
}
template<typename T>
typename List<T>::Iterator List<T>::erase(Iterator it)
{
Node* n = it.node->next;
delete it.node;
--list_size;
return Iterator(n);
}
template<typename T>
T& List<T>::front() const
{
return head->next->value;
}
template<typename T>
T& List<T>::back() const
{
return head->prev->value;
}
template<typename T>
typename List<T>::Iterator List<T>::begin() const
{
return Iterator(head->next);
}
template<typename T>
typename List<T>::Iterator List<T>::end() const
{
return Iterator(head);
}
}
Possible bug: I've manually compared most of the methods of
std::list
and my list, and it seems coherent. Except for:
for (auto it = list.begin(); it != list.end(); ++it)
{
it = list.erase(it);
}
The std::list
deletes the all the elements and then errors because you cannot increment past it.end()
. But my list deletes every other element in a weird way, and I think loops around several times.