I'm a first year computer engineering student and would appreciate feedback on some code.
I've created a linked-list with an iterator to be able to range for loops like: for (int i : list) {}
where list is Linked_List<int> list;
.
Flaws I already know of but choose to ignore because of how the teacher in class implements stuff:
- Use of raw pointers
Linked_List.h
namespace Util
{
template<typename T>
class Linked_List
{
public:
struct Iterator;
private:
struct Node;
public:
Linked_List();
~Linked_List() noexcept(false);
Linked_List(const Linked_List&) = delete;
Linked_List(Linked_List&&) = delete;
Linked_List& operator=(const Linked_List&) = delete;
Linked_List& operator=(Linked_List&&) = delete;
// Modifiers
void push_back(T);
void push_front(T);
void pop_back();
void pop_front();
// Capacity
bool empty() const;
unsigned int size() const;
// Element access
T back() const;
T front() const;
Iterator begin() const;
Iterator end() const;
// TODO:
//Iterator insert(const Iterator, T);
//Iterator erase(const Iterator);
private:
Node* _head;
Node* _tail;
unsigned int _size;
};
};
Linked_List.cpp
#include "pch.h"
#include "Linked_List.h"
namespace Util
{
template<typename T>
struct Linked_List<T>::Node
{
Node() : prev(nullptr), next(nullptr) {}
Node(T t) : value(t), prev(nullptr), next(nullptr) {}
Node* prev;
Node* next;
T value;
};
template<typename T>
struct Linked_List<T>::Iterator
{
Iterator() : _current(nullptr)
{
//
}
T& operator*() const
{
return _current->value;
}
Iterator& operator++()
{
_current = _current->next;
return *this;
}
bool operator!=(const Iterator& rhs)
{
return _current != rhs._current;
}
private:
friend class Linked_List<T>;
Iterator(Node* n) : _current(n) {}
Node* _current;
};
template<typename T>
Linked_List<T>::Linked_List() : _size(0)
{
_head = new Node();
_tail = new Node();
_head->next = _tail;
_tail->prev = _head;
}
template<typename T>
Linked_List<T>::~Linked_List() noexcept(false)
{
while (!empty())
{
pop_back();
}
delete head;
delete tail;
}
template<typename T>
void Linked_List<T>::push_back(T t)
{
Node* n = new Node(t);
n->prev = _tail->prev;
n->next = _tail;
_tail->prev->next = n;
_tail->prev = n;
++_size;
}
template<typename T>
void Linked_List<T>::push_front(T t)
{
Node* n = new Node(t);
n->next = _head->next;
n->prev = _head;
_head->next->prev = n;
_head->next = n;
++_size;
}
template<typename T>
void Linked_List<T>::pop_back()
{
if (empty()) throw Error("pop_back(): on empty list");
Node* n = _tail->prev;
_tail->prev->prev->next = _tail;
_tail->prev = _tail->prev->prev;
--_size;
delete n;
}
template<typename T>
void Linked_List<T>::pop_front()
{
if (empty()) throw Error("pop_front(): on empty list");
Node* n = _head->next;
_head->next->next->prev = _head;
_head->next = _head->next->next;
--_size;
delete n;
}
template<typename T>
bool Linked_List<T>::empty() const
{
//return (_head->next == _tail) && (_tail->prev == _head);
return size() == 0;
}
template<typename T>
T Linked_List<T>::back() const
{
if (empty()) throw Error("back(): on empty list");
return _tail->prev->value;
}
template<typename T>
T Linked_List<T>::front() const
{
if (empty()) throw Error("front(): on empty list");
return _head->next->value;
}
template<typename T>
unsigned int Linked_List<T>::size() const
{
return _size;
}
template<typename T>
typename Linked_List<T>::Iterator Linked_List<T>::begin() const
{
return Iterator(_head->next);
}
template<typename T>
typename Linked_List<T>::Iterator Linked_List<T>::end() const
{
return Iterator(_tail);
}
};
I haven't yet figured out how to implement:
Iterator insert(const Iterator, T);
Iterator erase(const Iterator);
const_iterator
yet? It can be helpful to do them together. \$\endgroup\$