Linked List destructor in C++: should I delete? - c++

I've start implementing some data structures in C++, starting from Linked Lists.
Coming from a Java background, I'm still wrapping my head around pointers and objects lifespans.
LinkedList:
struct Node
{
int data;
Node *next;
};
class LinkedList
{
private:
Node *head;
Node *tail;
int length;
public:
LinkedList();
~LinkedList();
void addToHead(Node &newHead);
void popHead();
void printList();
};
and then I've implemented it like this:
LinkedList::LinkedList()
{
head = NULL;
tail = NULL;
length = 0;
}
LinkedList::~LinkedList(){}
void LinkedList::addToHead(Node& newHead)
{
newHead.next = head;
head = &newHead;
length++;
}
void LinkedList::popHead()
{
Node *currHead = head;
head = head->next;
length--;
}
void LinkedList::printList()
{
Node *curr = head;
while(curr)
{
curr = curr->next;
}
}
Lastly there's a simple main:
int main()
{
LinkedList list;
Node n1 = {3};
Node n2 = {4};
Node n3 = {5};
list.addToHead(n1);
list.addToHead(n2);
list.addToHead(n3);
list.printList();
list.popHead();
list.printList();
return 0;
}
This a rather naive implementation, and I was wondering if I had to provide a proper destructor which deletes the Node* pointers upon iteration.
Whenever I've tried to add it, the program results in a memory error, and I was thinking that the memory being allocated is being also deallocated at the end of the main, since all the Node*s live there.
Should I fix my destructor? Should I change the whole interface?
Thanks in advance!

Although there are no memory leaks in your code as it stands, I think you should change your interface.
Your linked list isn't doing what you probably think its doing - taking ownership of its contents. A linked list that doesn't own its contents is a strange beast and probably something you did not intend.
One easy way to make it take ownership is to change your design to use std::unique_ptr instead of raw pointers. Your addToHead function would then be change to take std::unique_ptr r-value references pointers (or simply raw pointers that create new std::unique_ptr internally if that's too advanced)
Here is your implementation changed to use std::unique_ptr. Its a bit rough-and-ready, but should get you on your way:
#include <memory>
struct Node
{
Node(int i) : data(i)
{}
int data;
std::unique_ptr<Node> next;
};
class LinkedList
{
private:
std::unique_ptr<Node> head;
Node *tail;
int length;
public:
LinkedList();
~LinkedList();
void addToHead(std::unique_ptr<Node>&& newHead);
void popHead();
void printList();
};
LinkedList::LinkedList()
{
head = NULL;
tail = NULL;
length = 0;
}
LinkedList::~LinkedList(){}
void LinkedList::addToHead(std::unique_ptr<Node>&& newHead)
{
newHead->next = std::move(head);
head = std::move(newHead);
length++;
}
void LinkedList::popHead()
{
head = std::move(head->next);
length--;
}
void LinkedList::printList()
{
auto* curr = head.get();
while(curr)
{
curr = curr->next.get();
}
}
int main()
{
LinkedList list;
list.addToHead(std::make_unique<Node>(3));
list.addToHead(std::make_unique<Node>(4));
list.addToHead(std::make_unique<Node>(5));
list.printList();
list.popHead();
list.printList();
return 0;
}

Related

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

Is it possible to implement a std::list using unique pointers?

List.h:
#pragma once
#include <iostream>
#include <memory>
#include <initializer_list>
class List{
public:
List();
List(const std::initializer_list<int> &list);
~List();
int size() const;
void push_back(int val);
void pop_back();
void pop_front();
friend std::ostream &operator << (std::ostream &os, const List &l);
private:
void printNodes() const;
struct Node {
Node(int data) : data(data) {}
std::unique_ptr<Node> next;
Node *previous;
int data;
};
int len;
std::unique_ptr<Node> head;
Node *tail;
};
List.cpp
#include "List.h"
List::List() : len(0), head(nullptr), tail(nullptr){
}
List::List(const std::initializer_list<int> &list) : len(0), head(nullptr), tail(nullptr) {
for (auto &elem : list)
push_back(elem);
}
List::~List() {
}
void List::push_back(int val){
if (tail == nullptr) {
head = std::make_unique<Node>(val);
tail = head.get();
head->next = nullptr;
head->previous = nullptr;
len++;
}
else {
tail->next = std::make_unique<Node>(val);
(tail->next)->previous = tail;
tail = tail->next.get();
tail->next = nullptr;
len++;
}
}
void List::pop_back(){
if(len == 1){
auto node = head.release();
delete node;
head = nullptr;
tail = nullptr;
}else{
// tail->previous;
}
}
void List::pop_front(){
if(len == 1){
auto node = head.release();
delete node;
head = nullptr;
tail = nullptr;
}else{
}
}
void List::printNodes() const{
Node *temp = head.get();
while (temp != nullptr) {
std::cout << temp->data << "\n";
temp = (temp->next).get();
}
}
int List::size() const{
return len;
}
std::ostream &operator<<(std::ostream & os, const List & l){
l.printNodes();
return os;
}
Source.cpp
#include <iostream>
#include "List.h"
using namespace std;
int main() {
List l{3, 5, 1, 6, 7};
cout << l << endl;
}
Hello Stack Overflow, I'm a Data structures student, and as practice, I'm trying to recreate std::list using smart pointers. Based on what I have read, it appears that unique_ptr should be the default one to use, with shared_ptr and weak_ptr only being used where unique_ptr cannot due to speed differences. Unfortunately, I have hit a wall when trying to implement pop_back() and pop_front(). Do I have to adopt shared pointers to complete the entire std::list reimplementation, or is there a way these functions can be done using unique pointers?
Yes, this is perfectly possible. Let's start with pop_back:
You have a pointer to the node already with tail, but that's not the interesting one, since it's just a raw pointer. The pointer that you need is the unique_ptr to that same node. Where is that pointer stored? Is there an easy way to get to it starting from tail?
Once you have the unique_ptr, unchaining the node from the list is as easy as resetting that pointer. Note that there is no need to call release and delete the node manually.
Now for pop_front: Here you already have the unique_ptr in hand, it's head. But you have to be careful as the whole list rests on this one. Resetting the head will make the entire list disappear. So be sure to detach the rest of the list from the head and reattach it with the list first. If you do this properly, deleting the original head will not even be a worry for you. Try it out!
Be sure to draw a picture of the list to visualize which node points where. It's rather difficult to keep all of this information in your head at once.

Transformation of Linked list from Raw pointers to Smart pointers

How can I implement my linked list code from raw pointers to smart pointers. Any guidelines will be helpful. Here is my code.
Actually I'm more confused about the type of smart pointer. Shared or unique and what will be the head. And how I gonna delete this whole list in destructor?
#include <iostream>
class List{
private:
struct Node{
int datum;
Node* next;
Node(int d, Node* n):datum(d),next(n){std::cout<<"Constructor: "<<datum<<"\n";}
~Node(){std::cout<<"Destructor: "<<datum<<"\n";}
};
Node* head=nullptr;
public:
~List() {
while (head) {
Node *temp = head->next;
delete head;
head = temp;
}
}
void append(int d){
Node* t = getLastNode(head);
(t ? t->next : head) = new Node(d, nullptr);
}
Node *getLastNode(Node *n) const {
for (Node *it = n; it; it = it->next)
if (!it->next)
return it;
return nullptr;
}
};
int main(){
List l1;
l1.append(1);
l1.append(2);
}

how to use "new" to allocate memory of the point reference argument inside a function

Here is my code
#include <stdio.h>
#include <stdlib.h>
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
void insert(ListNode *&head,int value)
{
ListNode *node;
node = head;
if(!node)
{
//node = new ListNode(value);
head = new ListNode(value);
}
else
{
while(node->next != NULL)
node = node->next;
node->next = new ListNode(value);
}
}
void print(ListNode *head)
{
ListNode *node = head;
for(;node!=NULL;){
printf("%d ",node->val);
node = node->next;
}
}
int main(int argc,char *argv[])
{
ListNode *head = NULL;
insert(head,0);
insert(head,1);
insert(head,2);
print(head);
return 0;
}
Inside function insert,if I pass head to the point node,and use node = new ListNode(value);,the insert action fail and head is still NULL.But I use
new allocate memory to head directly,it works.I am confused about the point reference inside function in C++ and hope somebody help me figure it out.
This:
ptr = new whatever;
allocates memory, maybe calls a constructor, and assigns a new value to ptr.
Now consider these two functions:
void foo1(int &n)
{
int k=n;
k=5;
}
void foo2(int &n)
{
n=5;
}
After I call foo1, the value of the variable I passed (by reference) is unchanged. But after I call foo2, it is 5.
Find my inline comments to understand what each step is doing
node = head; //! Here your node pointer pointing to the memory pointed by head
if(!node) //! You are checking if that memory is null or not
{
node = new ListNode(value); //! now you are pointing your node pointer to some newly allocated memory, there is no impact on head pointer.
//! If you want to change the head pointer also so
head = node; //! Now head also pointing to the newly allocated location.
}

Queue Data structure app crash with front() method

I am implementing a queue data structure, but my app crashes. I know I am doing something wrong with Node pointer front or Front() method of queue class
#include <iostream>
using namespace std;
class Node
{
public:
int get() { return object; };
void set(int object) { this->object = object; };
Node * getNext() { return nextNode; };
void setNext(Node * nextNode) { this->nextNode = nextNode; };
private:
int object;
Node * nextNode;
};
class queue{
private:
Node *rear;
Node *front;
public:
int dequeue()
{
int x = front->get();
Node* p = front;
front = front->getNext();
delete p;
return x;
}
void enqueue(int x)
{
Node* newNode = new Node();
newNode->set(x);
newNode->setNext(NULL);
rear->setNext(newNode);
rear = newNode;
}
int Front()
{
return front->get();
}
int isEmpty()
{
return ( front == NULL );
}
};
main()
{
queue q;
q.enqueue(2);
cout<<q.Front();
system("pause");
}
You're using uninitialized pointers on several occasions.
Enqueue refers to rear->setNext(). If the queue is empty, rear is uninitialized, leading to crashes.
Front returns the node by some Node member-function without checking for a non-null pointer. Why not simply return the *front pointer?
None of your classes have a constructor. Your pointers aren't even NULL-pointers, they're just uninitialized. That's asking for troubles.
My advice:
Give both classes a constructor.
When calling ANY Node member-function, check for valid pointers.
Use less Node member-functions; returns raw pointers when you can.