Destructing a linked list - c++

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;
};

Related

Why assignment operator working even when it was forbidden?

I'm struggling with OOP again. I tried to implement signgly linked list, here's the code:
template <typename T>
class node
{
T value;
node* next;
public:
node(const T& n)
{
this->value = n;
this->next = nullptr;
}
~node()
{
//is it ok to leave destructor empty in my case?
}
};
template <typename T>
class list
{
node<T>* head;
node<T>* tail;
public:
list()
{
this->head = nullptr;
this->tail = nullptr;
}
list(const list& arr)
{
*this = list(); //<================== the line I mentioned
node* current_this = this->head;
node* current_arr = this->arr;
while (current_arr != nullptr)
{
current_this = new node(current_arr);
current_arr = current_arr->next;
current_this = current_this->next;
}
}
list(list&& arr)
{
this->head = arr.head;
this->tail = arr.tail;
arr = list(); //<================== the line I mentioned
}
~list()
{
node* current = this->head;
while (current != nullptr)
{
node* next = current->next;
delete current;
current = next;
}
}
};
I wrote some lines without thinking, i.e. arr = list() and *this = list(). I didn't implemented operator= so there should be some troubles. I thought compiler has decided to generate copy/move assigment for me, so I decided to add these lines, just for curiosity sake:
list& operator=(const list& arr) = delete;
list& operator=(list&& arr) = delete;
But it still compiled and I don't understand why. What's the deal here? Shoudn't assignment be forbidden and therefore arr = list() and *this = list() be illegal?
Templates aren't evaluated unless/until you instantiate them. If you don't use the copy or move constructor, then their bugs don't produce errors.

How to implement try_pop() method in queue in c++?

Below is queue example in "C++ concurrency in action 2nd".
This example is single threaded queue, which will be modified to explain thread safe.
template<typename T>
class queue {
private:
struct node {
T data;
std::unique_ptr<node> next;
node(T data_) : data(std::move(data_)) {}
};
std::unique_ptr<node> head;
node* tail;
public:
queue() : tail(nullptr) {};
queue(const queue& other) = delete;
queue& operator=(const queue& other) = delete;
std::shared_ptr<T> try_pop() {
if (!head) return std::shared_ptr<T>();
std::shared_ptr<T> const res(std::make_shared<T>(std::move(head->data)));
std::unique_ptr<node> const old_head = std::move(head);
head = std::move(old_head->next);
if(!head) tail = nullptr;
return res;
}
void push(T new_value) {
std::unique_ptr<node> p(new node(std::move(new_value)));
node* const new_tail = p.get();
if(tail) {
tail->next = std::move(p);
} else {
head = std::move(p);
}
tail = new_tail;
}
};
I can understand this queue works fine, but the way try_pop() is implemented seems to be weird.
Is it better way to do like below ?
std::shared_ptr<T> try_pop() {
if (!head) return std::shared_ptr<T>();
std::shared_ptr<T> const res(std::make_shared<T>(std::move(head->data)));
// std::unique_ptr<node> const old_head = std::move(head);
// head = std::move(old_head->next);
head = std::move(head->next);
if(!head) tail = nullptr;
return res;
}
Why head should be copied to old_head on this?
Is there any reason I missed?

Fixing memory leaks in a doubly linked list implementation

I read some of the other posts on this topic because there were quite a few, but they didn't really help my situation.
I am getting memory leaks in my implementation of a doubly linked list. I have to make my own so using list is not an option.
here are the two push functions I am using...
template <class T>
void dllist<T>::push_front(T val) {
node* new_node = new node;
new_node->value = val;
new_node->forward = head;
new_node->backward = nullptr;
if (head != nullptr)
head->backward = new_node;
head = new_node;
}
and...
template <class T>
void dllist<T>::push_back(T val) {
node* new_node = new node;
new_node->value = val;
new_node->forward = nullptr;
if (!head)
head = new_node;
else {
node* traveller = head;
while (traveller->forward != nullptr)
traveller = traveller->forward;
traveller->forward = new_node;
new_node->backward = traveller;
}
}
finally, here is my destructor
template <class T>
dllist<T>::~dllist() {
node* current = head;
while (current != nullptr) {
node* forward = current->forward;
delete current;
current = forward;
}
}
In main, I declare an object of type dllist called mylist and I make a few calls to push_front with some integer values and then push_back.
I am using the CRT library to check for leaks and there is a leak at each call to push_back or push_front.
I am confused because I thought I made my destructor correctly. Is there something else Im not seeing?
If anyone could point me in the right direction I'd appreciate it!
Thanks.
MRE
template<class T>
class dllist {
struct node {
T value;
node* forward;
node* backward;
};
node* head;
public:
dllist(); // default constructor
~dllist(); // default destructor
void push_front(T); // push element to the front of the list
void push_back(T); // push element to the back of the list
};
int main() {
{
dllist<int> mylist;
mylist.push_front(10);
mylist.push_front(12);
mylist.push_front(14);
mylist.push_front(16);
mylist.push_front(18);
mylist.push_front(19);
mylist.push_back(11);
mylist.push_back(21);
mylist.push_back(31);
mylist.push_back(41);
mylist.push_back(31);
mylist.push_back(41);
mylist.push_back(222);
}
_CrtDumpMemoryLeaks();
return 0;
}
template <class T>
dllist<T>::dllist() {
head = nullptr;
}

How to implement Front() method to return the first element of a templated doubly linked list C++?

Whenever I implement the front() method (to return the first element of the doubly linked list) in the main I get a segmentation fault even though the back() method (returning the info of tail) that was implemented in a similar manner works. Can someone help?
template <class T>
class Node {
public:
T info;
Node<T>* next;
Node<T>* prev;
Node(const T info){
this->info = info;
next = NULL;
prev = NULL;
}
Node* getNode(T info){
Node* newnode = (Node *)malloc(sizeof(Node));
}
};
template <class T>
class DLlist {
private:
Node<T>* head;
Node<T>* tail;
int size;
public:
DLlist();
T front();
T back();
};
template<class T>
DLlist<T>::DLlist(){
head = NULL;
tail = NULL;
size = 0;
}
template <class T>
void DLlist<T>::addback(const T newdata){
if (isEmpty()){
Node<T> *newnode = new Node<T>(newdata);
head = tail = newnode;
size++;
return;
}
Node<T> *newnode;
newnode = new Node<T>(newdata);
newnode->prev = tail;
newnode->next = NULL;
tail->prev = newnode;
tail = newnode;
size++;
}
template <class T>
T DLlist<T>::front(){
return (head->info);
}
In your addback() function:
Node<T> *newnode; // Two lines where
newnode = new Node<T>(newdata); // one suffices
newnode->prev = tail;
newnode->next = NULL; // Unnecessary, your constructor did this
tail->prev = newnode; // THIS IS YOUR PROBLEM
tail = newnode;
size++;
Your tail should be setting its next pointer to the new node, not its previous. Drawing this stuff out on a piece of paper can go a long way in better understanding how it should work.
I am always willing to chalk up poor formatting on this site to copy/paste, but there are other things you can do to simplify your code, make it a bit more modern, etc.
So here's your code again, cleaned up a tad (This code went through clang-format using the Webkit style):
#include <iostream>
template <class T>
struct Node {
T info;
Node<T>* next = nullptr;
Node<T>* prev = nullptr;
Node(const T& info)
: info(info)
{
}
// Don't know why you need this, so just deleting it because it's a bad
// function
};
template <class T>
class DLlist {
private:
Node<T>* head = nullptr;
Node<T>* tail = nullptr;
int size = 0;
public:
DLlist() = default;
bool isEmpty() { return head == nullptr && tail == nullptr; }
void push_back(const T& newdata);
const T front() const;
const T back() const;
};
template <class T>
void DLlist<T>::push_back(const T& newdata)
{
if (isEmpty()) {
head = new Node<T>(newdata);
tail = head;
size++;
return;
}
Node<T>* newnode = new Node<T>(newdata);
newnode->prev = tail;
tail->next = newnode; // THIS WAS PROBABLY YOUR ISSUE
tail = newnode;
size++;
}
template <class T>
const T DLlist<T>::front() const
{
return head->info;
}
template <class T>
const T DLlist<T>::back() const
{
return tail->info;
}
int main()
{
DLlist<int> list;
list.push_back(42);
list.push_back(54);
std::cout << list.front() << '\n'; // 42 prints just fine, Debian w/ LLVM 9
}

Destructor for a List class in 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")
}