I have an implementation of Linked List
with push
, pop
, size
, print
and search
functionalities. This is also an attempt to learn smart pointers. I have only used shared_pointers
in this implementation.
#include <iostream>
#include <memory>
#include <cstddef>
// Class for individual nodes in the list
template<typename T>
class Node
{
T val;
std::shared_ptr<Node<T>> next = nullptr;
public:
Node(T value)
{
val = value;
}
T get_val() const;
void set_next(std::shared_ptr<Node<T>>);
std::shared_ptr<Node<T>> get_next() const;
};
template<typename T>
T Node<T>::get_val() const
{
return val;
}
template<typename T>
void Node<T>::set_next(std::shared_ptr<Node<T>> node)
{
next = node;
}
template<typename T>
std::shared_ptr<Node<T>> Node<T>::get_next() const
{
return next;
}
// Class for the Linked List
template<typename T>
class LinkedList
{
/*std::shared_ptr<Node<T>> head = std::make_shared<Node<T>>(nullptr);
std::shared_ptr<Node<T>> tail = std::make_shared<Node<T>>(nullptr);*/
std::shared_ptr<Node<T>> head;
std::shared_ptr<Node<T>> tail;
std::size_t size_var = 0;
public:
LinkedList()
{
}
void push(T);
T pop();
int search(T) const;
void print() const;
std::size_t size() const;
};
// Insert node at head
template<typename T>
void LinkedList<T>::push(T value)
{
auto new_node = std::make_shared<Node<T>>(value);
// Check if the list already has values and push values at the head accordingly
if(head == nullptr)
{
head = new_node;
tail = new_node;
}
else
{
auto temp = head;
head = new_node;
new_node -> set_next(temp);
}
size_var++;
}
// Remove node at tail
template<typename T>
T LinkedList<T>::pop()
{
T value = tail -> get_val();
auto node = head;
std::shared_ptr<Node<T>> prev;
// Set the tail to second last value and set its next to nullptr.
while(node)
{
if(node -> get_next() == nullptr)
{
//std::cout << "Tail count first: " << node.use_count() << "\n";
tail = prev;
//std::cout << "Tail count second: " << node.use_count() << "\n";
tail -> set_next(nullptr);
//std::cout << "Tail count third: " << node.use_count() << "\n";
break;
}
prev = node;
node = node -> get_next();
}
size_var--;
return value;
}
//Returns the index of the value if found, else returns zero.
template<typename T>
int LinkedList<T>::search(T value) const
{
auto node = head;
int index = 0;
while(node)
{
if(node -> get_val() == value)
{
return index;
}
node = node -> get_next();
index++;
}
return -1;
}
// Print the list
template<typename T>
void LinkedList<T>::print() const
{
auto node = head;
std::cout << "Printing List: \n";
while(node)
{
std::cout << node -> get_val() << "\t";
node = node -> get_next();
}
std::cout << "\n";
}
// Return the size of the list
template<typename T>
std::size_t LinkedList<T>::size() const
{
return size_var;
}
int main()
{
LinkedList<int> ll{};
for(int i = 8; i >= 0; i--)
{
ll.push(i);
}
ll.print();
std::cout << "Size: " << ll.size() << "\n";
std::cout << "Pop: " << ll.pop() << "\n";
std::cout << "Size: " << ll.size() << "\n";
std::cout << "Pop: " << ll.pop() << "\n";
std::cout << "Size: " << ll.size() << "\n";
ll.push(9);
ll.print();
std::cout << "Size: " << ll.size() << "\n";
std::cout << "Search: " << ll.search(10) << "\n";
std::cout << "Search: " << ll.search(5) << "\n";
return 0;
}
The push
inserts a new node or item at the head of the list while pop
removes an item from the tail. The search
returns an index or position of the item if value being searched for is found.
Is the usage of smart pointers correct? Are there any leaks, especially in
pop
?Are there any place in this code where I should be using
std::unique_ptr
? I felt that there is always a possibility of more than one pointer being pointed to any node; hence I avoidedstd::unique_ptr
.What should be the return type of
search
,bool
orint
orstd::size_t
? If it isstd::size_t
, isstd::optional
the best way to indicate search failure?