Destructor for a List class in C++ - c++

I'm new to C++ thus the question. I've a toy implementation of a Singly Linked List in C++.
template<typename T>
class List {
template<typename U>
struct Node {
U data_;
Node<U>* next_;
Node() : data_(0), next_(nullptr) {}
Node(U data) : data_(data), next_(nullptr) {}
};
private:
Node<T>* head_;
std::size_t size_;
public:
List() : head_{nullptr}, size_{0} {}
void insert(const T& item) {
Node<T>* p(new Node<T>(item));
if (size_ == 0) {
head_ = p;
} else {
p->next_ = head_;
head_ = p;
}
size_++;
}
std::size_t getSize() {
return size_;
}
~List(){
while(head_){
Node<T> p = head_;
delete(p);
head_ = head_->next_;
}
};
This code seems to work. The problem though is that the objects allocated by new are never cleaned up, despite the ~List() destructor. Can someone help me understand, how I can write a destructor for this class that cleans up all the allocated nodes ?
Important remark: I am aware that this can be done using smart pointers, but I want to understand the old school way of managing heap.

while(head_){
Node<T> p = head_; <-- change to pointer
delete(p); <-- you can't delete this right now
head_ = head_->next_;
}
p should be a pointer. You cannot delete p right away. You have to find the next node, and delete p later. Also use delete p; instead of delete (p); as follows:
~List() {
while(head_) {
Node<T> *p = head_;
head_ = head_->next_;
delete p;
}
}
As noted in comments, Node does not need to be a template. You can simplify your class. insert can also be simplified because head_ is initialized to nullptr, you can safely assign p->next_ = head_;
template<typename T> class List {
struct Node {
T data_;
Node* next_;
Node() : data_(0), next_(nullptr) {}
Node(T data) : data_(data), next_(nullptr) {}
};
Node* head_;
std::size_t size_;
public:
List() : head_{ nullptr }, size_{ 0 } {}
void insert(const T& item) {
Node* p = new Node(item);
p->next_ = head_;
head_ = p;
size_++;
}
std::size_t getSize() {
return size_;
}
~List() {
while(head_) {
Node *marked = head_;
head_ = head_->next_;
delete marked;
}
}
};

The general idea is that you have to figure out who is the owner of the object to decide who should delete it.
In relation to nodes, the List is the owner. So you should carefully develop all the methods in a way that once the List looses the ownership of the object, it makes sure that object is deleted or the ownership is taken over.
Obvious places when you want to free the memory is, first one you delete the list. Second, when you remove an element, for example pop it.
Lets look in both cases.
First delete the list. For that you need to write a destructor, which iterates over the list and deletes elements one by one. For that I refer to the answer of #barmak-shemiani.
For the case when you pop an element you can do following:
T pop() {
Node<T> *tmp = head_;
if (head_ != nullptr) {
head_ = head_->next_;
T data = tmp->data_;
delete tmp;
return data;
}
throw std::runtime_error("Can't pop an empty list")
}

Related

Copy Constructor Error "Terminate called after throwing an instance of 'int'" [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 10 months ago.
Improve this question
The task was to create a copy constructor for a Forward_list class, and this is the code I have tried. But it keeps showing an error:
terminate called after throwing an instance of 'int'
I have been editing and changing, but I can't seem to get past that error.
class Forward_list
{
public:
class Node
{
public:
// A node will hold data of type T
T data{};
// next will point to the next node in the list
// we initialise next to nullptr
Node* next = nullptr;
// Because we have already intialised the variables
// the default constructor doesn't need to do anything
Node(){}
// if the constructor is called with just one argument.
Node(T input_data, Node* next_node= nullptr)
{
data = input_data;
next = next_node;
}
// Destructor
~Node(){}
};
private:
// private member variables for Forward_list
unsigned size_ = 0;
Node* head_ = nullptr;
public:
Forward_list(const Forward_list<T>& other);
template <typename T>
Forward_list<T>::Forward_list(const Forward_list& other) {
head_ = nullptr;
Node *prev_node = nullptr;
for(Node *other = head_; other != nullptr; other = other->next) {
Node *new_node = new Node;
new_node->data = other->data;
new_node->next = nullptr;
if (!head_)
head_ = new_node;
else
prev_node->next = new_node;
prev_node = new_node;
}
}
Nothing in this code throws an int. So it has to be in code you have not shown. Perhaps in the constructor of T?
Not that it matters, because your copy constructor does not even run its loop at all, since you are looping through the wrong list. You are starting your loop with this->head_ when you need to start it with other.head_ instead.
Also, the Node converting constructor should take its input_data parameter by const reference, and use a member initialization list, to avoid having to default-construct and then assign the data member in separate operations.
Also, the Forward_list copy constructor can be simplified.
Also, make sure you are following the Rule of 3/5/0, if you are not already doing so (you did not show that code).
Try something more like this:
class Forward_list
{
public:
class Node
{
public:
T data;
Node* next;
Node(const T &input_data, Node* next_node = nullptr);
};
Forward_list() = default;
Forward_list(const Forward_list<T>& other);
Forward_list(Forward_list<T>&& other);
~Forward_list();
Forward_list& operator=(Forward_list<T> other);
...
private:
// private member variables for Forward_list
unsigned size_ = 0;
Node* head_ = nullptr;
};
template <typename T>
Forward_list<T>::Node(const T &input_data, Node* next_node)
: data(input_data), next(next_node)
{
}
template <typename T>
Forward_list<T>::Forward_list(const Forward_list& other) {
Node **new_node = &head_;
for(Node *other_node = other.head_; other_node != nullptr; other_node = other_node->next) {
*new_node = new Node(other->data);
++size_;
new_node = &((*new_node)->next);
}
}
template <typename T>
Forward_list<T>::Forward_list(Forward_list&& other) :
head_(std::exchange(other.head_, nullptr)),
size_(std::exchange(other.size_, 0))
{
}
template <typename T>
Forward_list<T>::~Forward_list() {
Node *node = head_;
while (node) {
Node *next = node->next;
delete node;
node = next;
}
}
template <typename T>
Forward_list<T>& Forward_list<T>::operator=(Forward_list other)
{
std::swap(head_, other.head_);
std::swap(size_, other.size_);
return *this;
}

How to implement the two member functions (push_front and the destructor)?

I am trying to implement two member functions, i.e., push_front and the destructor. I have written the code of the push_front function. But I seem, I am doing wrong anywhere. How can I fix this issue? How can I insert a node in front of Linked-List properly?
template <typename T>
class Node {
public:
Node():next_p_{nullptr} {}
Node(T v, Node* n):data_{v}, next_p_{n} {}
~Node() {};
void setData( T &v ) { data_ = v; }
const T &getData() const { return data_; }
void setNext( Node *n ) { next_p_ = n; }
Node *getNext() const { return next_p_; }
private:
T data_;
Node *next_p_;
};
template <typename T>
class LinkedList {
private:
Node<T> *head_ptr_;
public:
LinkedList():head_ptr_{nullptr} {}
~LinkedList() {
}
// Insert at the front of the linked list (UPDATED)
void push_front( T v ){
Node *new_node = new Node;
new_node->setNext(*head_ptr_) = v;
new_node->next_p_ = this->head_ptr_;
this->head_ptr_ = new_node;
}
};
The first Node constructor takes the value and next pointer, use that to create the new node in one step.
You shouldn't dereference head_ptr_ when using it as the next pointer. It's already a Node*, which is the correct type for head_ptr_.
void push_front( T v ){
Node *new_node = new Node(v, head_ptr_);
this->head_ptr_ = new_node;
}
I would also recommend not dealing with manual memory management and instead use smart pointers to simplify your life a bit.
Using std::unique_ptr you wont have to deal with reclaiming the memory you allocated from the free store.
One caveat to this approach is you lose your ability to copy the List but usually that's what you want anyway.
template<typename T>
class List {
struct Node {
Node( T value ) noexcept
: data{ std::move_if_noexcept( value ) }
{ }
T data;
std::unique_ptr<Node> next{ nullptr };
};
public:
auto push_front( T value ) -> void {
auto node{ std::make_unique<Node>( std::move_if_noexcept( value ) ) };
std::swap( node->next, head_ );
std::swap( head_, node );
}
~List()
{
while (head_) {
std::unique_ptr<Node> tmp(std::move(head_));
head_ = std::move(tmp->next);
}
}
private:
std::unique_ptr<Node> head_{ nullptr };
};

C++ linked list Segmentation fault and valgrind errors

i need to code a linked list for university in c++, mostly to practice coding iterators.
I tested it with some basic cases and it works but after i pass it in valgrind and the test server for the program i get a list of different errors. Maybe somebody can help me not to despair.
(At the end i will append the error list)
template <typename T = float>
class ForwardList
{
struct Node
{
/// Constructs a Node from a data value and a link to the next element.
Node(const T &data, Node *next) : data{data}, next{next} {}
/// A Node owns all nodes after it, so it deletes them on destruction
~Node() { delete next; }
//Performs a deep copy of the Node and all Nodes after it. Bad practice but we got it like that
Node *clone() const
{
if (next == nullptr)
{
return new Node{data, nullptr};
}
else
{
return new Node{data, next->clone()};
}
}
T data;
Node *next;
};
public:
ForwardList() : head(nullptr) {}
/// Copy constructor performs a deep copy of the other list's Nodes
ForwardList(const ForwardList &other)
{
head = other.head->clone();
}
/// Destructor makes sure that all Nodes are correctly destroyed
~ForwardList()
{
while (head->next != nullptr)
{
Node *tmp = head;
head = head->next;
delete tmp;
}
delete head;
}
/// Copy assignment operator uses the copy-and-swap idiom to make a safe
/// assignment
ForwardList &operator=(ForwardList other)
{
swap(*this, other);
return *this;
}
/// Add an element to the front of the list.
void push_front(const T &value)
{
std::cout << "Num: " << numberOfNodes << std::endl;
Node *item = new Node(value, nullptr);
if (head==nullptr)
{
head = item;
}else
{
item->next=head;
head = item;
}
numberOfNodes++;
}
/// Remove the first element of the list. Calling this function on an empty
/// list is undefined behavior. When implementing this function, be careful
/// to delete the one and only the one element that is removed.
void pop_front()
{
Node *item;
item = head->next;
delete head;
head = item;
numberOfNodes--;
}
/// Get a reference to the first element of the list
/// (const and non-const version)
T &front()
{
return head->data;
}
const T &front() const
{
return head->data;
}
/// Return true is the list is empty
bool empty() const
{
return numberOfNodes == 0 ? true : false;
}
std::size_t size() const
{
return numberOfNodes;
}
friend void swap(ForwardList &l, ForwardList &r)
{
Node *tmp = l.head;
l.head = r.head;
r.head = tmp;
}
private:
Node *head;
size_t numberOfNodes = 0;
};
And now the fun part (i will put it on pastebin because its pretty long):
https://pastebin.com/4JAKkJtP
Your issue is that ~Node tries to delete its next, and you also try to walk the list in ~ForwardList. By deleting ~Node(), you let ForwardList handle cleanup and everything works.
The clue here is that valgrind reported use after free, meaning something was deleting a pointer twice. That was a clue to look at everything that deletes a Node* (or really, delete in general).

SinglyLinkedList implementation with rule of 3

I'm new to C++ and have been trying to implement a Singly Linked List, that provides an implementation the destructor, copy constructor and assignment operator. I'm running into compilation issues when trying to implement the copy constructor and the assignment operator.
Here's node.hpp
#ifndef LINKED_LIST_NODE_HPP
#define LINKED_LIST_NODE_HPP
template <typename T>
class Node{
public:
T data;
Node* next;
Node();
Node(T);
Node(const Node&);
~Node();
};
template <typename T>
Node<T>::Node(){}
template <typename T>
Node<T>:: Node(const T data): data(data), next(nullptr){}
template <typename T>
Node<T>::Node(const Node<T>& source) : data(source.data),
next(new Node)
{
(*next) = *(source.next) ;
}
template <typename T>
Node<T>::~Node(){}
#endif //LINKED_LIST_NODE_HPP
This is singly_linked_list.hpp
#ifndef LINKED_LIST_SINGLYLINKEDLIST_HPP
#define LINKED_LIST_SINGLYLINKEDLIST_HPP
#include <iostream>
#include "node.hpp"
template <typename T>
class SinglyLinkedList {
private:
Node<T>* head;
std::size_t count;
public:
SinglyLinkedList();
SinglyLinkedList(const SinglyLinkedList& source);
SinglyLinkedList& operator=(const SinglyLinkedList& source);
~SinglyLinkedList();
void insert(T);
void remove(T);
bool isEmpty();
int length();
void print();
};
template <typename T>
SinglyLinkedList<T>::SinglyLinkedList() : head(nullptr), count(0){}
template <typename T>
template <typename T>
SinglyLinkedList<T>::SinglyLinkedList(const SinglyLinkedList& source){
Node<T>* curr = source.head;
while(curr != nullptr){
Node<T>* p = new Node<T>;
p->data = curr->data;
curr = curr->next;
}
}
//template <typename T>
//SinglyLinkedList<T>::SinglyLinkedList& operator=(const SinglyLinkedList<T>& source){
// //not sure how to implment this.
//}
template <typename T>
SinglyLinkedList<T>::~SinglyLinkedList() {
if(!isEmpty()){
Node<T>* temp = head;
Node<T>* prev = nullptr;
while(temp->next != nullptr){
prev = temp;
temp = temp->next;
delete prev;
}
delete temp;
}
}
template <typename T>
bool SinglyLinkedList<T>::isEmpty() {
return head == nullptr;
}
template <typename T>
void SinglyLinkedList<T>::insert(T item) {
Node<T>* p = new Node<T>(item);
p->next = head;
head = p;
count += 1;
}
template <typename T>
void SinglyLinkedList<T>::remove(T item) {
bool present = false;
if (head->data == item){
Node<T>* temp = head;
head = head->next;
delete(temp);
count -= 1;
return;
}
Node<T>* temp = head;
while (temp->next != nullptr){
if (temp->next->data == item){
Node<T>* removable = temp->next;
temp->next = temp->next->next;
delete(removable);
present = true;
count -= 1;
break;
} else{
temp = temp->next;
}
}
if(!present){
throw std::invalid_argument("item not present in list");
}
}
template <typename T>
int SinglyLinkedList<T>::length() {
return count;
}
template <typename T>
void SinglyLinkedList<T>::print() {
if(isEmpty()){
throw std::invalid_argument("Can't print an empty list!");
}
Node<T>* temp = head;
while(temp != nullptr){
if(temp->next != nullptr){
std::cout<<temp->data;
std::cout<<"->";
}else{
std::cout<<temp->data;
}
temp = temp->next;
}
std::cout<<std::endl;
}
#endif //LINKED_LIST_SINGLYLINKEDLIST_HPP
I've commented out the copy constructor code to make this compile. What is the correct way of doing this? I'm just learning C++.
One issue that introduces complexity is that it is not well defined what the copy constructor of a node should do? Should the next field of the copy point to the next of the original, or it should create a copy of the next and point to that? The former is inadequate and error-prone, the latter would recursively create a copy of the whole list, one node at a time. This will work for lists of small size but will cause stack overflow for lists with many elements due to the depth of the recursive calls.
So to keep things simple, I wouldn't bother with copy constructor of a node.
template <typename T>
class Node {
public:
T data;
Node* next = nullptr;
Node() = default;
Node(const Node&) = delete; // since copying is not well defined, make it impossible to copy a node.
};
Copying a list is a well defined operation, so implementing the copy constructor makes sense. A mistake with your current implementation is that you allocate a new node, only to leak it later (nothing keeps track of the newly allocated node p). What you need looks more like this:
template <typename T>
SinglyLinkedList<T>::SinglyLinkedList(const SinglyLinkedList<T>& source)
: head(nullptr)
, count(0)
{
// deal with the trivial case of empty list
if (source.head == nullptr)
return;
// deal with the case where count >= 1
head = new Node<T>;
head->data = source.head->data;
head->next = nullptr;
count = 1;
Node<T>* lastCopied = source.head; // last node to be copied
Node<T>* lastAdded = head; // last node to be added to the current list
while (lastCopied->next != nullptr)
{
// create new node
Node<T>* p = new Node<T>;
p->data = lastCopied->next->data;
p->next = nullptr;
// link the newly created node to the last of the current list
lastAdded->next = p;
lastAdded = p;
// advance lastCopied
lastCopied = lastCopied->next;
count++;
}
}
Now regarding the assignment operator, luckily you can use the 'copy and swap' idiom that greatly simplifies things.
template <typename T>
SinglyLinkedList<T>& SinglyLinkedList<T>::operator =(SinglyLinkedList<T> source) // note that you pass by value.
{
std::swap(head, source.head);
std::swap(count, source.count);
return *this;
}
My answer would become too long if I tried to explain the copy and swap technique. It is a clever trick to write exception safe code and avoid duplication (implements assignment by using the copy ctor) at the same time. It is worth reading about it here.
Btw, the declaration of your class should look like this
template <typename T>
class SinglyLinkedList
{
private:
Node<T>* head = nullptr;
std::size_t count = 0;
public:
SinglyLinkedList(const SinglyLinkedList& source);
SinglyLinkedList& operator=(SinglyLinkedList source);
// other members here...
};
PS. My code assumes you are using c++11 or a later standard.
I don't like the direction this is headed. I'm going to explain how to do this approach right because it is an excellent lesson on recursion, but because it's recursion it can run the program out of Automatic storage (march off the end of the stack, most likely) with a sufficiently large list. Not cool.
The logic:
Copying a node copies the next node if there is one. This looks something like
template <typename T>
Node<T>::Node(const Node<T>& source) : data(source.data)
{
if (source.next) // if there is a next, clone it
{
next = new Node<T>(*source.next);
}
else
{
next = nullptr;
}
}
This reduces the linked list copy constructor to
template <typename T>
SinglyLinkedList<T>::SinglyLinkedList(const SinglyLinkedList& source){
head = new Node<T>(*source.head); //clone the head. Cloning the head will clone everything after
count = source.count;
}
A helper function may, uh... help here to make the Node copy constructor a bit more idiomatic
template <typename T>
Node<T> * initnext(const Node<T> & source)
{
if (source.next)
{
return new Node<T>(*source.next);
}
else
{
return nullptr;
}
}
template <typename T>
Node<T>::Node(const Node<T>& source) : data(source.data),
next(initnext(source))
{
}
but I don't think you gain much.
So... I don't like the above. What would I do instead? Something a lot like opetroch's solution above, but different enough that I'll write this up.
The node stays brutally stupid. As far as I'm concerned all a Node should ever know is how to store the payload and find other Nodes. This means the linked list should do all of the heavy lifting.
Concept 1: head is nothing but a next pointer. Once you abstract away its differences, unimportant here, you can use it exactly the same way you would next.
Concept 2: If you only know where next points, you have to do a bunch of extra book-keeping to track the previous node to update it's next. But if you take advantage of the previous's next pointing to the current node, you can throw out even more code. By tracking the previous node's next you have all of the information you need.
Concept 3: If you keep a pointer to the previous node's next, you can update that previous node's next any time you want by dereferencing it.
template <typename T>
SinglyLinkedList<T>::SinglyLinkedList(const SinglyLinkedList& obj)
{
Node<T>* tocopy = obj.head;
Node<T>** nextpp = &head; // head is a next. We are now pointing to a pointer to next
while (tocopy) // keep looping until there is no next node to copy
{
*nextpp = new Node<T>(tocopy->data); // copy source and update destination's next
nextpp = &(*nextpp)->next; // advance to point at the next of the node we just added
tocopy= tocopy->next; // get next node to copy
}
count = obj.count;
}
Because this iterates rather than recurses it doesn't eat up Automatic storage (probably the stack) and can keep going until the cows come home.
This logic can also be applied to remove
template <typename T>
void SinglyLinkedList<T>::remove(T item) {
Node<T>** temp = &head; //head is nothing but a next pointer.
// by pointing to where the next is, we don't
// need to track a previous or have special handling
// for the head node
while (*temp){ // because we now have a pointer to a pointer, we need an
// extra dereference
if ((*temp)->data == item){
Node<T>* removable = *temp;
*temp = (*temp)->next;
delete(removable);
count -= 1;
return; // no need for any special magic. Just get out.
} else{
temp = &(*temp)->next; // update the pointer to the next
}
}
// if we got here the node was not found.
throw std::invalid_argument("item not present in list");
}
And following through on head is just a next, we can also gut the destructor:
template <typename T>
SinglyLinkedList<T>::~SinglyLinkedList() {
while(head){ // if head null, list empty
Node<T>* temp = head; // cache so we can delete
head = head->next; // move head
delete temp; //delete removed node
}
}

Destructing a linked list

I was trying to implement a linked list for solving an algorithm problem.
It basically worked, however, it turned out that I was using too much memory.
I would appreciate if someone point out defects of following destructor design.
template<typename T>
struct Node {
Node(): item(0),next(0) {}
Node(T x): item(x),next(0) {}
T item;
Node* next;
};
template <typename T>
struct List {
List() : head(0),tail(0) {}
Node<T>* head;
Node<T>* tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if(head == NULL) {
head = tail = newNode;
} else {
tail->next = newNode;
tail = tail->next;
}
}
void clearRecur(Node<T>* h) {
if(h) {
clearRecur(h->next);
delete h;
}
}
void clear() {
if(head) {
clearRecur(head);
}
}
};
A list can be cleared recursively or iteratively.
Alternatively to your (IMHO correct) version, I use a slight different approach – make the Node itself "responsible" to delete its tail. This leads to recursive clearing as well (but with less code).
Recursive clearing:
template<typename T>
struct Node {
Node(): item(), next(nullptr) {}
Node(T x): item(x), next(nullptr) {}
~Node() { delete next; } // <== recursive clearing
T item;
Node* next;
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
Node(const Node&) = delete;
// Deleting assignment operator as well.
Node& operator=(const Node&) = delete;
};
template <typename T>
struct List {
List() : head(nullptr), tail(nullptr) {}
~List() { clear(); }
Node<T>* head, tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if (head == nullptr) head = tail = newNode;
else {
tail->next = newNode;
tail = tail->next;
}
}
void clear() {
delete head;
head = tail = nullptr;
}
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
List(const List&) = delete;
// Delete assignment operator as well.
List& operator=(const List&) = delete;
};
This is the way, I did it in our current project. At the first glance, it seemed enjoying simple and worked fine. I changed my mind when our beta-testers came into play. In real world projects, the lists were such long that the recursive clearing ran out of stack memory. (Yepp – a stack overflow.) I should've known better!
Thus, I made the clearing iteratively – whereby the "responsibility" is moved back from Node to List. (The API user will not note this as it happens "under the hood".)
Iterative clearing:
template<typename T>
struct Node {
Node(): item(), next(nullptr) {}
Node(T x): item(x), next(nullptr) {}
T item;
Node* next;
};
template <typename T>
struct List {
List() : head(nullptr), tail(nullptr) {}
~List() { clear(); }
Node<T>* head, tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if (head == nullptr) head = tail = newNode;
else {
tail->next = newNode;
tail = tail->next;
}
}
void clear() {
while (head) {
Node<T> *p = head; head = head->next;
delete p;
}
tail = nullptr;
}
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
List(const List&) = delete;
// Delete assignment operator as well.
List& operator=(const List&) = delete;
};