I'm trying to implement a Doubly Linked List data structure in C++. Please give me suggestions on how this code can be improved. Try to remain in C++11 because that's what I know atm.
#ifndef DOUBLY_LINKED_LIST_HPP
#define DOUBLY_LINKED_LIST_HPP
#include <iostream>
template <typename T>
class DoublyLinkedList {
template <typename T>
struct Node {
T val;
Node<T>* next;
Node<T>* prev;
};
public:
DoublyLinkedList() : len(0), head(nullptr), tail(nullptr)
{
}
DoublyLinkedList(const DoublyLinkedList& rhs) : DoublyLinkedList()
{
Node<T>* currNode = rhs.head;
while (currNode) {
this->PushBack(currNode->val);
currNode = currNode->next;
}
}
DoublyLinkedList(DoublyLinkedList&& rhs) : head(rhs.head), tail(rhs.tail), len(rhs.len)
{
rhs.head = rhs.tail = nullptr;
}
DoublyLinkedList& operator=(DoublyLinkedList rhs)
{
swap(*this, rhs);
return *this;
}
~DoublyLinkedList()
{
this->Clear();
}
const T& GetFront() const
{
return head->val;
}
const T& GetBack() const
{
return tail->val;
}
size_t GetLength() const
{
return len;
}
void PushFront(const T& val)
{
Node<T>* newNode = new Node<T>{ val, head, nullptr };
if (tail == nullptr) {
tail = newNode;
}
else {
head->prev = newNode;
}
head = newNode;
++len;
}
void PushBack(const T& val)
{
Node<T>* newNode = new Node<T>{ val, nullptr, tail };
if (head == nullptr /*&& tail == nullptr*/) {
head = newNode;
}
else {
tail->next = newNode;
}
tail = newNode;
++len;
}
void InsertAt(size_t idx, const T& val)
{
Node<T>* currNode = head;
while (idx--) {
currNode = currNode->next;
}
if (currNode == tail || currNode == nullptr) {
this->PushBack(val);
}
else if (currNode == head) {
this->PushFront(val);
}
else {
Node<T>* newNode = new Node<T>{ val, currNode, currNode->prev };
currNode->prev->next = newNode;
currNode->prev = newNode;
}
}
T PopFront()
{
T val = std::move(head->val);
Node<T>* newHead = head->next;
delete head;
if (newHead) {
newHead->prev = nullptr;
}
else {
tail = nullptr;
}
head = newHead;
--len;
return val;
}
T PopBack()
{
T val = std::move(tail->val);
Node<T>* newTail = tail->prev;
delete tail;
if (newTail) {
newTail->next = nullptr;
}
else {
head = nullptr;
}
tail = newTail;
--len;
return val;
}
T RemoveAt(size_t idx)
{
Node<T>* currNode = head;
while (idx--) {
currNode = currNode->next;
}
if (currNode == tail || currNode == nullptr) {
return this->PopBack();
}
else if (currNode == head) {
return this->PopFront();
}
else {
T val = std::move(currNode->val);
currNode->prev->next = currNode->next;
currNode->next->prev = currNode->prev;
delete currNode;
return val;
}
}
void Clear()
{
Node<T>* currNode = head;
while (currNode) {
Node<T>* nextNode = currNode->next;
delete currNode;
currNode = nextNode;
}
this->head = this->tail = nullptr;
this->len = 0;
}
friend std::ostream& operator<< (std::ostream& out, const DoublyLinkedList<T>& list)
{
out << "DoublyLinkedList{";
Node<T>* currNode = list.head;
while (currNode) {
std::cout << currNode->val;
if (currNode->next) std::cout << ", ";
currNode = currNode->next;
}
out << "}";
return out;
}
friend void swap(DoublyLinkedList<T>& lhs, DoublyLinkedList<T>& rhs)
{
std::swap(lhs.head, rhs.head);
std::swap(lhs.tail, rhs.tail);
std::swap(lhs.len, rhs.len);
return;
}
private:
Node<T>* head;
Node<T>* tail;
size_t len;
};
#endif
```