I am a student, trying to learn C++11 and data structures. I would like to ask you to to review my code. I have spent some time and analyse similar questions on Code Review and it gave me basic idea what I should pay attention to.
I have learnt about the rule of three, but I am not sure if my implementation is correct, especially = operator.
I would be grateful if someone more experienced than me could check it. I would like to improve my programming skills.
EDIT:
I didn't expect that stdexcept is required. On my local machine it compiles without that library... (Linux, eclipse, c++11 flag added). I am confused, sorry.
Code:
#include <iostream>
#include <stdexcept>
template <typename T>
class SingleLinkedList{
private:
struct Node{
T data;
Node * next;
Node() : next(nullptr) {}
Node(const T num) : data(num), next(nullptr) {}
};
private:
Node * head;
Node * tail;
public:
SingleLinkedList(); // constructor
~SingleLinkedList(); // destructor
SingleLinkedList(const SingleLinkedList &oldList); // copy constructor
SingleLinkedList& operator=(const SingleLinkedList &oldList); // copy assignment operator
void insert(T const& value);
void displayList(std::ostream& stream = std::cout) const;
void pushFront(T value);
void pushBack(T value);
void pushAtPosition(int position, T value);
void popFront();
void popBack();
void popAtPosition(int position);
int getLength();
int getValue(int position);
Node * search(T value);
};
template <typename T>
SingleLinkedList<T>::SingleLinkedList() : head(nullptr), tail(nullptr){
std::cout << "Constructor called..." << std::endl;
}
template <typename T>
SingleLinkedList<T>::~SingleLinkedList(){
std::cout << "Destructor called..." << std::endl;
int index = 1;
Node * temp = nullptr;
while(head!=nullptr){
temp = head;
head = head->next;
delete temp;
//std::cout << "Node number: " << index << " destroyed" << std::endl;
index++;
}
}
template <typename T>
SingleLinkedList<T>::SingleLinkedList(const SingleLinkedList<T> &oldList){
std::cout << "Copy constructor..." << std::endl;
// is it necessary? my constructor by default initializes head and tail with nulls
this->head = nullptr;
this->tail = nullptr;
Node * temp = nullptr;
temp = oldList.head;
while(temp!=nullptr){
this->insert(temp->data);
temp = temp->next;
}
}
template <typename T>
SingleLinkedList<T>& SingleLinkedList<T>::operator=(const SingleLinkedList &oldList){
std::cout << "Copy assignment operator..." << std::endl;
if(this != &oldList){
// delete previous content
this->~SingleLinkedList();
// copy all nodes from oldList to newList
Node * temp = nullptr;
temp = oldList.head;
while(temp!=nullptr){
this->insert(temp->data);
temp = temp->next;
}
}
return *this;
}
template <typename T>
void SingleLinkedList<T>::insert(T const& value){
Node * temp = new Node(value);
//temp->data = value;
//temp->next = nullptr;
if(head==nullptr){
head = temp;
tail = temp;
}
else{
tail->next = temp;
tail = temp;
}
}
template <typename T>
void SingleLinkedList<T>::displayList(std::ostream& stream) const{
Node * temp = nullptr;
temp = head;
while(temp!=nullptr){
stream << temp->data << " -> ";
temp = temp->next;
}
stream << std::endl;
}
template <typename T>
void SingleLinkedList<T>::pushFront(T value){
Node * temp = new Node;
temp->next = head;
temp->data = value;
head = temp;
}
template <typename T>
void SingleLinkedList<T>::pushBack(T value){
Node * temp = new Node;
tail->next = temp;
tail = temp;
temp->data = value;
}
template <typename T>
void SingleLinkedList<T>::pushAtPosition(int position, T value){
Node * temp = new Node;
Node * previous = nullptr;
Node * current = nullptr;
if(position < 1){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
if(position > getLength()){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
if(position == 1){
pushFront(value);
return;
}
if(position == getLength()){
pushBack(value);
return;
}
current = head;
for(int i=1; i<position; i++){
previous = current;
current = current->next;
}
previous->next = temp;
temp->next = current;
temp->data = value;
}
template <typename T>
void SingleLinkedList<T>::popFront(){
Node * temp = nullptr;
temp = head;
head = temp->next;
delete temp;
}
template <typename T>
void SingleLinkedList<T>::popBack(){
Node * current = nullptr;
Node * previous = nullptr;
current = head;
while(current->next != nullptr){
previous = current;
current = current->next;
}
tail = previous;
previous->next = nullptr;
delete current;
}
template <typename T>
void SingleLinkedList<T>::popAtPosition(int position){
Node * previous = nullptr;
Node * current = nullptr;
if(position < 1){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
if(position > getLength()){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
if(position == 1){
popFront();
return;
}
if(position == getLength()){
popBack();
return;
}
current = head;
for(int i=1; i<position; i++){
previous = current;
current = current->next;
}
previous->next = current->next;
}
template <typename T>
int SingleLinkedList<T>::getLength(){
Node * current = head;
int len = 0;
while(current != nullptr){
len++;
current = current->next;
}
//std::cout << "The list length is: " << len;
return len;
}
template <typename T>
int SingleLinkedList<T>::getValue(int position){
Node * current = head;
if(position < 1){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
if(position > getLength()){
std::cout << "Invalid index" << std::endl;
throw std::out_of_range("Invalid index");
}
for(int i=1; i<position; i++){
current = current->next;
}
std::cout << "In index: " << position << ", the value is: " << current->data << "." << std::endl;
return current->data;
}
template <typename T>
typename SingleLinkedList<T>::Node * SingleLinkedList<T>::search(T value){
if(head==nullptr){
return nullptr;
}
Node * current = head;
while(current->next != nullptr){
if(current->data == value)
return current;
current = current->next;
}
return nullptr;
}
template <typename T>
std::ostream& operator<<(std::ostream& os, const SingleLinkedList<T>& list){
list.displayList(os);
return os;
}
int main(){
SingleLinkedList<int> myList;
myList.insert(10);
myList.insert(20);
myList.insert(30);
// test of copy constructor
SingleLinkedList<int> myList2(myList);
myList2.insert(40);
// test of copy assignment operator
SingleLinkedList<int> myList3;
myList3.insert(90);
myList3 = myList2;
myList3.insert(50);
std::cout << "myList:" << std::endl;
std::cout << myList;
std::cout << "myList2:" << std::endl;
std::cout << myList2;
std::cout << "myList3:" << std::endl;
std::cout << myList3;
// standard operations
std::cout << "-----------------------------------------------------" << std::endl;
std::cout << "push front: "<< std::endl;
myList.pushFront(5);
myList.pushFront(2);
std::cout << myList;
std::cout << "push back: "<< std::endl;
myList.pushBack(40);
myList.pushBack(50);
std::cout << myList;
std::cout << "push at position: "<< std::endl;
myList.pushAtPosition(7,4);
std::cout << myList;
std::cout << "pop front x2: "<< std::endl;
myList.popFront();
myList.popFront();
std::cout << myList;
std::cout << "pop back x2: "<< std::endl;
myList.popBack();
myList.popBack();
std::cout << myList;
//pop at position
std::cout << "pop at position: "<< std::endl;
myList.popAtPosition(2);
std::cout << myList;
//get value
std::cout << "get value: "<< std::endl;
myList.getValue(3);
// search
std::cout << "search: "<< std::endl;
auto result = myList.search(30);
std::cout << "Value: " << result->data << " found under " << result << std::endl;
return 0;
}