I've implemented a generic Stack
in C++. I would like a code review in regards to my code, especially on whether or not my implementation satisfy the following 4 points:
- My Stack class guarantees Strong Exception Safety, using the copy and swap idiom
- The container still works if T passed in is not default constructible
- Correctness of the implementation of the Rule of Five
- General implementation correctness and efficiency (i.e: No memory leaks, dangling pointers ... ...)
Additionally, I have also added 4 questions in the code as comments as my concerns in the correctness of my code's implementation/style. I would greatly appreciate if those questions are addressed in the review as well.
#pragma once
template <typename T>
class Stack
{
public:
Stack();
Stack(const Stack& other);
Stack(Stack&& other) noexcept;
~Stack();
Stack<T>& operator =(const Stack<T>& other);
Stack<T>& operator =(Stack<T>&& other) noexcept;
void swap(Stack<T>& other) noexcept;
friend void swap(Stack<T>& A, Stack<T>& B)
{
A.swap(B);
}
void pop();
T& top();
void push(T item); //Pass T by value? What if T is big and expensive?
private:
struct Node
{
Node* next;
T data; //Should data be stored as a reference instead? (i.e: T& data)
}; //Should I be adding constructors/destructors and such in this struct?
Node* head;
void reverse(Node** head); //reverse method is not specific to *this stack, should I make it inline or something?
};
template <typename T>
Stack<T>::Stack()
:head(nullptr)
{}
template <typename T>
Stack<T>::Stack(const Stack& other)
:Stack()
{
Node* curr = other.head;
while(curr != nullptr)
{
push(curr->data);
curr = curr->next;
}
reverse(&head);
}
template <typename T>
Stack<T>::Stack(Stack&& other) noexcept
:head(nullptr)
{
swap(*this, other);
}
template <typename T>
Stack<T>::~Stack()
{
Node* curr = head;
while(curr != nullptr)
{
Node* tmp = curr;
curr = curr->next;
delete tmp;
}
delete head;
}
template <typename T>
Stack<T>& Stack<T>::operator =(const Stack<T> &other)
{
Stack tmp(other);
swap(*this, tmp);
return *this;
}
template <typename T>
Stack<T>& Stack<T>::operator =(Stack<T>&& other) noexcept
{
swap(*this, tmp);
return *this;
}
template <typename T>
void Stack<T>::swap(Stack& other) noexcept
{
using std::swap;
swap(head, other.head);
}
template <typename T>
void Stack<T>::pop()
{
if(head == nullptr)
throw std::runtime_error("No item found in stack");
Node* curr = head;
head = head->next;
delete curr;
}
template <typename T>
void Stack<T>::push(T item)
{
Node* tmp = new Node;
tmp->next = head;
tmp->data = std::move(item);
head = tmp;
}
template <typename T>
T& Stack<T>::top()
{
if(head == nullptr)
throw std::runtime_error("No item found in stack");
return head->data;
}
template <typename T>
void Stack<T>::reverse(Node **head)
{
if(head == nullptr || *head == nullptr)
throw std::runtime_error("head is null or no head to reverse");
Node *prev = nullptr;
Node *next = *head->next;
while(*head != nullptr)
{
*head->next = prev;
prev = *head;
*head = next;
if(next != nullptr)
next = next->next;
}
*head = prev;
}
while(curr != nullptr) push(curr->data);
you are did notincrement curr and why do you need toreverse()
? \$\endgroup\$ – Jagannath Jul 29 '16 at 6:49