This is my code so far, it obviously does not work. I want this iterator to work on
both range and increment based for loops. How can I do it?
template<typename T>
class MyList {
public:
class Node {
public:
Node(const T& data): data(data) {
this->next = NULL;
}
std::unique_ptr<Node> next;
T data;
};
MyList() {
this->_size = 0;
}
int size() const;
void push_front(const T& data);
T pop_front();
T front() const;
void remove(T data);
typedef T* iterator;
typedef const Node* const_iterator;
iterator begin() {
return (&_head.get()->data);
}
iterator end() {
return (NULL);
}
private:
int _size;
std::unique_ptr<MyList<T>::Node> _head;
};
T* is not suitable as a linked-list iterator, as it has no way to get to the next node in the list when incremented. Also because &_head.get()->data is not valid when the list is empty.
And Node* will not work for either iterator or const_iterator, either, since it can't have a valid operator++ to iterator the list, or anoperator* to access the data. See the requirements for a ForwardIterator.
You are better off defining a separate type to act as a (const_)iterator and let it hold a Node* internally for iterating and dereferencing, eg:
template<typename T>
class MyList {
public:
class Node {
public:
Node(const T& data): data(data) {}
std::unique_ptr<Node> next;
T data;
};
template<typename D>
class my_iterator
{
public:
my_iterator(Node* node) : _current(node) {}
bool operator==(const my_iterator &rhs) const { return _current == rhs._current; }
D& operator*() { return _current->data; }
D* operator->() { return &(_current->data); }
my_iterator& operator++() { _current = _current->next.get(); return *this; }
my_iterator operator++(int) { my_iterator tmp(_current); ++(*this); return tmp; }
private:
Node* _current;
};
using iterator = my_iterator<T>;
using const_iterator = my_iterator<const T>;
...
iterator begin() {
return iterator(_head.get());
}
const_iterator cbegin() const {
return const_iterator(_head.get());
}
iterator end() {
return iterator(nullptr);
}
const_iterator cend() const {
return const_iterator(nullptr);
}
...
};
Related
template <typename T>
class LinkedList {
struct node;
class Iterator;
public:
LinkedList() {}
LinkedList(std::initializer_list<T> init_list) {
this->operator=(init_list);
}
template <typename InputIterator>
LinkedList(InputIterator first, InputIterator last) {
for (; first != last; ++first)
this->push_back(*first);
}
LinkedList(const LinkedList& another) {
this->operator=(another);
}
~LinkedList() {
while (this->head) {
node* old_head = this->head;
this->head = old_head->next;
delete old_head;
}
}
Iterator begin() {
return Iterator(this->head);
}
Iterator end() {
return Iterator(this->tail->next);
}
I tried to add an empty node at the tail->next, however I can't get the result that I want. And without the empty node I just get a segmentation fault when I run the code.
class Iterator {
friend class LinkedList;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = int;
using pointer = T*;
using reference = T&;
Iterator(node* ptr) : ptr(ptr) {}
Iterator(const Iterator& other) {
this->operator=(other);
}
Iterator& operator=(const Iterator& that) {
this->ptr = that.ptr;
return *this;
}
Iterator& operator++() {
this->ptr = ptr->next;
return *this;
}
Iterator operator++(int) {
Iterator tmp(*this);
this->operator++();
return tmp;
}
Iterator& operator--() {
this->ptr = ptr->prev;
return *this;
}
Iterator operator--(int) {
Iterator tmp(*this);
this->operator--();
return tmp;
}
bool operator!=(Iterator that) const { return !(this->operator==(that)); }
bool operator==(Iterator that) const { return this->ptr == that.ptr; }
T& operator*() const { return ptr->data; }
Iterator* operator->() { return this; }
private:
node* ptr = nullptr;
};
Here is the main function I used to test and when I print the stl_list end() it prints the size of the list. I'm confused what should be returned for this function. I thought it was supposed to be an empty nullptr that points to the location after the tail.
int main() {
std::list<int> stl_list{1, 2, 3, 4, 5};
cs19::LinkedList<int> our_list{1, 2, 3, 4, 5};
std::cout << *stl_list.begin() << '\n';
std::cout << *our_list.begin() << '\n';
std::cout << *our_list.end() << '\n';
std::cout << *stl_list.end() << '\n';
}
Confused on how to implement the end function for doubly linked list
You could create a special node type that is one past the end node which only has a prev pointer.
template <typename T>
class LinkedList {
struct empty_node { // used for end iterator
empty_node* prev = nullptr;
};
struct node : empty_node {
empty_node* next;
T data;
};
Your LinkedList would then have an instance of empty_node whos only purpose is to let prev point back to the last real node. You'd then instantiate the end() iterator with a pointer to this empty_node.
You'd then use empty_node* everywhere until stepping the iterator forward or dereferencing an iterator.
Example with comments to explain:
template <typename T>
class LinkedList {
// node definitions as above
public:
class Iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = int;
using pointer = T*;
using reference = T&;
Iterator(empty_node* ptr) : ptr(ptr) {}
Iterator(const Iterator& other) : ptr(other.ptr) {}
Iterator& operator=(const Iterator& that) {
ptr = that.ptr;
return *this;
}
Iterator& operator++() {
// If the user steps forward, the iterator can't be at the end
// or the program will have undefined behavior as per the usual
// contract, so a cast is fine:
ptr = static_cast<node*>(ptr)->next;
return *this;
}
Iterator operator++(int) {
Iterator tmp(*this);
++*this;
return tmp;
}
Iterator& operator--() {
ptr = ptr->prev;
return *this;
}
Iterator operator--(int) {
Iterator tmp(*this);
--*this;
return tmp;
}
bool operator==(Iterator that) const { return this->ptr == that.ptr; }
bool operator!=(Iterator that) const {
return !(*this == that);
}
// Dereferencing is not allowed if the iterator is at the end so
// cast is fine:
reference operator*() const { return static_cast<node*>(ptr)->data; }
pointer operator->() { return &static_cast<node*>(ptr)->data; }
private:
empty_node* ptr = nullptr;
};
LinkedList() = default;
template <typename InputIterator>
LinkedList(InputIterator first, InputIterator last) {
for (; first != last; ++first) this->push_back(*first);
}
// Delegate to ctor taking iterators:
LinkedList(std::initializer_list<T> init_list)
: LinkedList(init_list.begin(), init_list.end()) {}
// Copy ctor - delegate to ctor taking iterators too:
LinkedList(const LinkedList& another)
: LinkedList(another.begin(), another.end()) {}
~LinkedList() {
// As long as it's not pointing at end, cast is fine:
for(empty_node* next; head != &beyond_end; head = next) {
next = static_cast<node*>(head)->next;
delete static_cast<node*>(head);
}
}
void push_back(const T& value) {
// Create a new node where `prev` points at the current last real node
// and `next` points at our empty end node:
node* nn = new node{{beyond_end.prev}, &beyond_end, value};
// link it:
if (head != &beyond_end) { // not the first node added
// the previous node must be a real node, so cast is fine:
static_cast<node*>(beyond_end.prev)->next = nn;
} else { // the first node added
head = nn;
}
beyond_end.prev = nn; // link beyond_end to the last real node
}
Iterator begin() { return Iterator(head); }
Iterator end() { return Iterator(&beyond_end); } // use `beyond_end` for end()
private:
empty_node* head = &beyond_end; // start pointing at the empty node
empty_node beyond_end; // note, not a pointer
};
So, instead of a node* tail; you'll have an instance of an empty_node in your LinkedList. It will have the same size as a node* so it doesn't waste space.
Demo
You could also store both the head and tail pointer in an empty_node to remove all casts except when dereferencing/deleteing.
template <typename T>
class LinkedList {
struct empty_node { // used for end iterator
empty_node* prev = nullptr;
empty_node* next = nullptr;
};
struct node : empty_node {
T data;
}
It requires minor changes to the example:
Demo
I've created a Linked List in C++ and want to implement an iterator for it so that I can do range loops: for (const int& i : list) where Linked_List<int> list;.
My idea is to create the Iterator as part of the Linked_List class like this:
This is what I got so far:
template <typename T>
class Linked_List
{
public:
struct Iterator;
struct Node;
public:
Linked_List();
~Linked_List() noexcept(false);
Linked_List(const Linked_List&) = delete;
Linked_List(Linked_List&&) = delete;
Linked_List& operator=(const Linked_List&) = delete;
Linked_List& operator=(Linked_List&&) = delete;
void push_back(T);
void push_front(T);
void pop_back();
void pop_front();
bool empty() const;
T back() const;
T front() const;
//void swap(T, T);
//void insert(Iterator, T);
//void erase(Iterator);
//Iterator begin() const;
//Iterator end() const;
private:
Node* head;
Node* tail;
};
template<typename T>
struct Linked_List<T>::Node
{
Node() : prev(nullptr), next(nullptr) {}
Node(T t) : value(t), prev(nullptr), next(nullptr) {}
Node* prev;
Node* next;
T value;
};
Is this a good approach?
Should I do error checking when incrementing the list to check if current->next == tail? If so, how do I do that? Because my Iterator doesn't have a list object with a tail.
Edit:
I'm not sure how to implement the struct Iterator;, I get stuck when figuring out how to connect it with the list so that I can check if the current node returned from the iterator equals the tail in the list, in the Linked_List Iterator end() const method.
Let's say I've implemented all the necessary operators for an iterator like this:
struct Iterator
{
T& operator*() const { return current->value; }
bool operator!=(const Iterator& rhs) { return (*_current != rhs._current); }
Iterator& operator++()
{
current = current->next;
return *this;
}
};
How would I go about implementing Iterator Linked_List<T>::begin() const; and end() now?
I imagine an imaginary user making an iterator object like this:
Linked_List<int>::Iterator it;
An idea is to have a public constructor with no parameters and a private constructor that takes a node as a parameter which _current will be set to, and have the Linked_List class as a friend.
A few notes.
There are two options where to declare Node and Iterator. Inside the list class as List<T>::Node or outside as Node<T>. It is, in part, a matter of taste. From engineering perspective though, the symbol names are longer for nested classes, so your debuginfo is bigger. Also, when nested classes are also templates it is harder to specialize them if/when necessary (because that requires fully specializing the enclosing template first), but this is not the case here.
It leads to more elegant code when one list node is used as list head and tail. Empty list is a node whose next and prev point to itself. push_front appends to list.next which points to the first node or itself. push_back appends a node to list.prev which points to the last node or itself. When inserting/removing nodes there is no need to have special handling of the first and last nodes. E.g. :
struct Node {
Node *next_, *prev_;
Node()
: next_(this), prev_(this)
{}
~Node() {
unlink();
}
void push_back(Node* n) {
n->next_ = this;
n->prev_ = prev_;
prev_->next_ = n;
prev_ = n;
}
void unlink() {
Node *next = next_, *prev = prev_;
next->prev_ = prev;
prev->next_ = next;
next_ = this;
prev_ = this;
}
};
In the above, Node only needs two operations to be able to maintain a list. More than that, Node is itself a minimalist list that can be used for intrusive lists (with auto-unlink in the destructor). Note how using this makes checks for nullptr unnecessary - Node is always a valid list.
Error checking should be in debug mode only (use assert, for example). Otherwise, those checks penalise correct applications with unnecessary run-time checks.
Here is a minimal working example based on the ideas for you:
template<class T>
class List;
class Iterator;
class Node {
friend class Iterator;
template<class T> friend class List;
protected:
Node *next_, *prev_;
void push_back(Node* n) {
n->next_ = this;
n->prev_ = prev_;
prev_->next_ = n;
prev_ = n;
}
void unlink() {
Node *next = next_, *prev = prev_;
next->prev_ = prev;
prev->next_ = next;
next_ = this;
prev_ = this;
}
public:
Node()
: next_(this), prev_(this)
{}
~Node() { unlink(); }
};
class Iterator {
protected:
Node* node_;
Iterator(Node* node)
: node_(node)
{}
public:
Iterator& operator++() {
node_ = node_->next_;
return *this;
}
bool operator==(Iterator b) const { return node_ == b.node_; }
bool operator!=(Iterator b) const { return node_ != b.node_; }
// Implement the rest of iterator interface.
};
template<class T>
class List {
class NodeT : public Node {
friend class List<T>;
T value_;
NodeT(T t) : value_(t) {}
};
template<class U>
class IteratorT : public Iterator {
friend class List<T>;
NodeT* node() const { return static_cast<NodeT*>(node_); }
public:
U& operator*() const { return node()->value_; }
U* operator->() const { return &node()->value_; }
operator IteratorT<U const>() const { return node_; } // iterator to const_iterator conversion
IteratorT(Node* node) : Iterator{node} {}
};
Node list_;
public:
using iterator = IteratorT<T>;
using const_iterator = IteratorT<T const>;
~List() { clear(); }
bool empty() const { return list_.next_ == &list_; }
iterator begin() { return list_.next_; }
iterator end() { return &list_; }
void push_back(T t) { list_.push_back(new NodeT(t)); }
void erase(const_iterator i) { delete i.node(); }
void clear() {
while(!empty())
erase(begin());
}
// Implement the rest of the functionality.
};
int main() {
List<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
for(auto elem : l)
std::cout << elem << ' ';
std::cout << '\n';
}
I need to implement following function:
int size() const;
function returns number of pieces of data stored in the list
Time complexity - O(1).
Basically I have a class called DList contained in DList.h which has the following structure:
class DList
{
struct Node
{
T data_;
Node *next_;
Node *prev_;
Node(const T &data = T{}, Node *next = nullptr, Node *prev = nullptr)
{
data_ = data;
next_ = next;
prev_ = prev;
}
};
Node *front_;
Node *back_;
public:
class const_iterator
{
friend class DList;
protected:
Node *curr_;
//assign curr_ with n;
const_iterator(Node *n)
{
curr_ = n;
}
public:
const_iterator()
{
curr_ = nullptr;
}
const_iterator operator++()
{
//++x
curr_ = curr_->next_;
return *this;
};
const_iterator operator++(int)
{
//x++
const_iterator old = *this;
curr_ = curr_->next_;
return old;
};
const_iterator operator--()
{
curr_ = curr_->prev_;
return *this;
}
const_iterator operator--(int)
{
//x--
const_iterator old = *this;
curr_ = curr_->prev_;
return old;
}
bool operator==(const_iterator rhs)
{
return (curr_ == rhs.curr_);
}
bool operator!=(const_iterator rhs)
{
return (curr_ != rhs.curr_);
}
const T &operator*() const
{
return curr_->data_;
}
};
class iterator : public const_iterator
{
friend class DList;
iterator(Node *n) : const_iterator(n) {}
public:
iterator() {}
iterator operator++()
{
//++x
this->curr_ = this->curr_->next_;
return *this;
}
iterator operator++(int)
{
//x++
iterator old = *this;
this->curr_ = this->curr_->next_;
return old;
}
iterator operator--()
{
//--x
this->curr_ = this->curr_->prev_;
return *this;
}
iterator operator--(int)
{
//x--
iterator old = *this;
this->curr_ = this->curr_->prev_;
return old;
}
T &operator*()
{
return this->curr_->data_;
}
};
DList();
~DList();
DList(const DList &rhs);
DList &operator=(const DList &rhs);
DList(DList &&rhs);
DList &operator=(DList &&rhs);
iterator begin()
{
return iterator(front_);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin() const
{
return const_iterator(front_);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
void push_front(const T &data);
void push_back(const T &data);
void pop_front();
void pop_back();
iterator insert(const T &data, iterator it);
iterator search(const T &data);
const_iterator search(const T &data) const;
iterator erase(iterator it);
iterator erase(iterator first, iterator last);
bool empty() const;
int size() const; <--------- THIS IS THE FUNCTION I NEED TO IMPLEMENT
};
The function size() is being called in the tester program main.cpp as following:
DList<Record> recList;
DList<int> intList;
.
.
.
.
if (!checkList(recList, mirror, 15) || recList.empty() || recList.size() != 15)
{
passtest = false;
cout << "Error 9a: Bug in = operator, no deepcopy?" << endl;
}
For O(1) complexity, you need to keep a running count that is updated whenever you add or remove a node. The default constructor must initialize it to zero, and other constructors and assignments must do the right thing (DTRT).
class DList
{
struct Node;
Node *front_;
Node *back_;
size_t count_;
public:
Dlist()
: front_(nullptr)
, back_(nullptr)
, count_(0)
{}
size_t size() const { return count_; }
// etc..
}
Okay, So I've been working on some book examples and stuff and found this exercise to implement the STL List lookalike. I've made it somehow and it kinda works, but I've got some major flaws in the implementation. The biggest one is that I have totally no idea how to make my List.end() iterator to work as it's supposed to do.
I guess I'll show the code first and try to tell some of my ideas next.
#ifndef TESTS_LST_H
#define TESTS_LST_H
#include <memory>
#include <cstddef>
template<class T> class Node;
template<class T> class ListIter;
template<class T>
class List {
public:
typedef ListIter<T> iterator;
typedef const ListIter<T> const_iterator;
typedef std::size_t size_type;
List(): first(0), last(0), sz(0) {}
List(const List<T>& lst);
~List() { clear(); }
iterator begin() { return iterator(first); }
iterator end() { return iterator(last); }
iterator insert() {}
iterator erase() {}
const_iterator begin() const { return iterator(first); }
const_iterator end() const { return iterator(last); }
void push_back(const T& val);
void push_front(const T& val);
void clear();
void pop_front();
void pop_back();
size_type size() { return sz; }
bool empty() { return sz == 0; }
List& operator=(const List& l);
private:
Node<T>* first;
Node<T>* last;
size_type sz;
std::allocator<Node<T>>* alloc;
};
template<class T>
class Node {
public:
Node(): next(0), prev(0), value(0) {}
Node(const T& val): next(0), prev(0), value(val) {}
private:
Node<T>* next;
Node<T>* prev;
T value;
friend class List<T>;
friend class ListIter<T>;
};
template<class T>
class ListIter {
public:
typedef ListIter<T> iterator;
ListIter(Node<T>* iter): current_node(iter) {}
ListIter(): current_node(0) {}
ListIter(ListIter<T>* iter): current_node(iter->current_node) {}
inline T& operator*() { return current_node->value; }
iterator& operator=(const iterator& rhs) { *this->current_node = rhs.current_node; }
bool operator==(const iterator& rhs) { return current_node->value == rhs.current_node->value; }
bool operator!=(const iterator& rhs) { return current_node->value != rhs.current_node->value; }
iterator& operator++();
iterator operator++(int);
iterator& operator--();
iterator operator--(int);
private:
Node<T>* current_node;
friend class List<T>;
};
template<class T>
void List<T>::push_back(const T& val)
{
Node<T>* temp = alloc->allocate(1);
alloc->construct(temp, val);
if (first == 0) {
first = last = temp;
} else {
temp->prev = last;
last->next = temp;
last = temp;
}
sz++;
}
template<class T>
void List<T>::push_front(const T &val)
{
Node<T>* temp = alloc->allocate(1);
alloc->construct(temp, val);
if (first == 0) {
first = last = temp;
} else {
temp->prev = 0;
temp->next = first;
first->prev = temp;
first = temp;
}
sz++;
}
template<class T>
void List<T>::clear()
{
Node<T>* current = first;
while (current != 0) {
Node<T>* next = current->next;
//delete current
alloc->deallocate(current, 1);
alloc->destroy(current);
current = next;
}
first = last = 0;
sz = 0;
}
template<class T>
List<T>::List(const List &lst)
{
first = last = 0;
sz = 0;
for (auto it = lst.begin(); it != lst.end(); it++) {
push_back(it.current_node->value);
}
push_back(lst.last->value);
}
template<class T>
List<T>& List<T>::operator=(const List &lst)
{
first = last = 0;
sz = 0;
for (auto it = lst.begin(); it != lst.end(); ++it) {
push_back(it.current_node->value);
}
push_back(lst.last->value);
return *this;
}
template<class T>
void List<T>::pop_front()
{
first = first->next;
alloc->deallocate(first->prev, 1);
alloc->destroy(first->prev);
first->prev = 0;
sz--;
}
template<class T>
void List<T>::pop_back()
{
last = last->prev;
alloc->deallocate(last->next, 1);
alloc->destroy(last->next);
last->next = 0;
sz--;
}
template<class T>
ListIter<T>& ListIter<T>::operator++()
{
current_node = current_node->next;
return *this;
}
template<class T>
ListIter<T>& ListIter<T>::operator--()
{
current_node = current_node->prev;
return *this;
}
template<class T>
ListIter<T> ListIter<T>::operator++(int)
{
iterator tmp(*this);
++*this;
return tmp;
}
template<class T>
ListIter<T> ListIter<T>::operator--(int)
{
iterator tmp(*this);
--*this;
return tmp;
}
#endif //TESTS_LST_H
As you can see .end() function returns a regular last element of the list and not the one past the end as it should. Should I try to rework this part to possibly keep the *last as the one past the end iterator and use the operator+ to iterate through the list to omit the need in the pointer to the end of the actual list?
Something like this (not sure about the corectness of the code below):
iterator& operator+(std::size_type n)
{
for (auto i = 0; i < n; ++i) {
++*this;
}
return *this;
}
But I'm not sure that's how the stuff works in the actual implementation, loops could be very demanding after all.
I know that this stuff is already out there and works and all that. That's just for the educational purposes, so I hope to hear some ideas. Thanks in advance.
Iterator were known in the past as "smart pointer", since they works like that. (Indeed, pointers are iterators, but not the opposed). So, think an iterator like a pointer.
"One past the end" is clear what means when you are working with vectors: a vector contains its elements in contiguous space. Indeed, it is possible to implement vector iterator with just pointers. But that is not the case for a linked list, where generally its element are not in contiguous memory.
Because you implemented the List class as a doubled linked list, I suggest you to change the first and last pointers by a head:
template<class T>
class List {
// ...
private:
Node<T> head;
size_type sz;
};
So, the begin() iterator become head.next and end() iterator become &head. This works as far the last element in the list points to the head.
BTW: You don't need to create Node<T> as a class with friends classes. It is just an implementation detail. Change it to a struct and put it in a implementation namespace.
I created a singly linked list for a project and now need to create a custom Iterator class. I have a nested class within my Linked List that defines my iterator. I have written most of the class, but am confused as to how to implement some functions. My issues are as follows:
-Please look at my end() function. I set it to the default constructor for the Iterator class, so the
currentNode variable in iterator got defaulted to NULL I guess. Is this correctly implemented?
-How should I overload the -> operator in the Iterator class?
class SSLL_Iter : public std::iterator<std::forward_iterator_tag, T>
{
public:
typedef T value_type;
typedef std::ptrdiff_t difference_type;
typedef T& reference;
typedef T* pointer;
typedef std::forward_iterator_tag iterator_category;
typedef SSLL_Iter self_type;
typedef SSLL_Iter& self_reference;
private:
Node* currentNode;
public:
explicit SSLL_Iter( Node* start = NULL) : currentNode( start ) {} //****************complete
SSLL_Iter( const SSLL_Iter& src ) : currentNode( src.currentNode ) {} //****************complete
reference operator*() const { //****************complete
T& temp = (currentNode->data);
return temp;
}
pointer operator->() const {} //*******??????????????????
self_reference operator=( const SSLL_Iter& src ) { //****************complete
this->here = src.here;
return *this;
}
self_reference operator++() { // preincrement //****************complete
currentNode = currentNode->next;
return *this;
}
self_type operator++(int) { // postincrement //****************complete
self_type temp = (*this);
++(*this);
return temp;
}
bool operator==(const SSLL_Iter& rhs) const { //****************complete
return (this->currentNode == rhs.currentNode);
}
bool operator!=(const SSLL_Iter& rhs) const { //****************complete
return (this->currentNode != rhs.currentNode);
}
}; // end SSLL_Iter
typedef std::size_t size_t;
typedef T value_type;
typedef SSLL_Iter iterator;
//typedef Const_SSL_Iter const_iterator;
SSLL() {}
SSLL(const SSLL& src ) {
for(int i = 0; i < src.size(); ++i) { // populate this SSLL with copies of the other SSLL's contents
this->push_back(src.item_at(i));
}
}
~SSLL() {
if(!is_empty()) {
clear();
}
}
iterator begin() { return SSLL_Iter( head ); }
iterator end() { return SSLL_Iter(); }
return &*(*this); is a decent operator->.
Your SSLL class probably does not have an efficient at, so don't use it in the copy constructor. Instead, create a SSLL template<class Iterator> void append(Iterator s, Iterator f), and implement SSLL(const SSLL& src) in terms of it.
If you have C++11 support, consider replacing the next node of Node with a std::unique_ptr<Node>. Do the same with the pointer-to-first Node in the SSLL root. Now memory management is handled pretty much automatically. You may need a .get() in your iterator after that change. This also eliminates the body of your destructor, makes your move-constructor =default correct (unless you are counting nodes), etc.