I was practicing my C++ routine and decided to code up this simple linked list data structure. Furthermore, I made an attempt to make the list iterator "safe" in the sense that it throws an exception instead of tampering a memory region it should not..
safelist.h
#ifndef SAFE_LIST_H
#define SAFE_LIST_H
#include <stdexcept>
namespace net {
namespace coderodde {
namespace util {
template<typename T>
class Safe_list {
public:
Safe_list()
:
head{nullptr},
tail{nullptr}
{}
// Disable all the constructors + assignments:
Safe_list(const Safe_list&) = delete;
Safe_list(Safe_list&&) = delete;
Safe_list& operator=(const Safe_list&) = delete;
Safe_list& operator=(Safe_list&&) = delete;
void push_back(const T& datum)
{
Safe_list_node* new_node = new Safe_list_node(datum);
if (tail == nullptr)
{
head = new_node;
}
else
{
tail->next = new_node;
}
tail = new_node;
}
void pop_front()
{
check_list_not_empty();
Safe_list_node* removed = head;
head = head->next;
if (head == nullptr)
{
tail = nullptr;
}
delete removed;
}
const T& front() const
{
check_list_not_empty();
return head->datum;
}
T& front()
{
check_list_not_empty();
return head->datum;
}
private:
struct Safe_list_node {
T datum;
Safe_list_node* next;
Safe_list_node(const T& datum)
:
datum{datum},
next{nullptr}
{}
};
Safe_list_node* head;
Safe_list_node* tail;
void check_list_not_empty()
{
if (head == nullptr)
{
throw std::runtime_error{"Operating on an empty list."};
}
}
public:
class Safe_list_iterator {
Safe_list_node* current_node;
public:
Safe_list_iterator(Safe_list_node* node)
:
current_node{node}
{}
const T& operator*() { return current_node->datum; }
T& operator*() const { return current_node->datum; }
bool operator!=(const Safe_list_iterator& lhs)
{
return this->current_node != lhs.current_node;
}
Safe_list_iterator& operator++()
{
if (current_node == nullptr)
{
throw std::runtime_error{"No elements left for iteration."};
}
current_node = current_node->next;
return *this;
}
};
using iterator = Safe_list_iterator;
iterator begin()
{
return Safe_list_iterator{head};
}
iterator end()
{
return Safe_list_iterator{nullptr};
}
};
} // End of net::coderodde::util
} // End of net::coderodde
} // End of net
#endif // SAFE_LIST_H
main.cpp
#include "safelist.h"
#include <iostream>
#include <stdexcept>
using net::coderodde::util::Safe_list;
using std::cerr;
using std::cout;
using std::endl;
using std::runtime_error;
int main(int argc, const char * argv[]) {
Safe_list<float> list;
for (int i = 0; i < 10; ++i)
{
list.push_back((float) i);
}
cout << "Iterating the queue in for each loop:" << endl;
for (const auto x : list)
{
cout << x << " ";
}
cout << endl;
cout << "Iterating the queue via old style iterators:" << endl;
for (Safe_list<float>::iterator it = list.begin(); it != list.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
cout << "Popping the queue:" << endl;
for (int i = 0; i < 10; ++i)
{
cout << list.front() << " ";
list.pop_front();
}
cout << endl;
cout << "Safety demo:" << endl;
Safe_list<float> list2;
for (int i = 10; i < 20; ++i)
{
list2.push_back((float) i);
}
try
{
for (Safe_list<float>::iterator begin = list.begin(),
end = list2.begin();
begin != end;
++begin)
{
}
}
catch (std::runtime_error& error)
{
cerr << "Exception caught! Message: " << error.what() << endl;
}
}
Please tell me anything that might make my code more idiomatic.