doubly_linked_list::~doubly_linked_list()
{
list_item* current = head;
while (current)
{
list_item* next = current->Get_Next();
delete current;
current = next;
}
}
I am writing a dynamic graph in C++ with the usage of a doubly linked list to save the Graph nodes, however my code keeps on failing as it calls the destructor more times than it should be calling it, for example, during my destructor for the graph
Dynamic_Graph::~Dynamic_Graph()
{
edges.~doubly_linked_list();
nodes.~doubly_linked_list();
}
the destructor for the doubly linked list is called more than 2 times, despite it being called explicitly twice.
then i have the function for insertion of an edge:
Graph_Edge* Dynamic_Graph::Insert_Edge(Graph_Node* from, Graph_Node* to)
{
Graph_Edge* new_edge = new Graph_Edge(from, to);
from->Get_out_Nodes().List_Insert_After(to, from->Get_out_Nodes().Get_Tail());
to->Get_in_Nodes().List_Insert_After(from, to->Get_in_Nodes().Get_Tail());
edges.List_Insert(new_edge);
return new_edge;
}
after adding the to node to the adjacency list of the from node the code unexplainably calls the destructor for a doubly linked list and deletes this insertion, however i am not sure why.
here are the headers for the graph and the linked list:
class doubly_linked_list
{
private:
list_item* head;
list_item* tail;
public:
doubly_linked_list() { this->head = NULL; this->tail = NULL; }
doubly_linked_list(list_item* head){ this->head = head; this->tail = NULL; }
~doubly_linked_list();
list_item* Get_Head() { return head; }
list_item* Get_Tail() { return tail; }
void Set_Head(list_item* h) { head = h; }
void List_Insert(list_item* x);
void List_Insert_After(list_item* x, list_item* y);
void List_Delete(list_item* x);
};
class Dynamic_Graph
{
protected:
doubly_linked_list edges;
doubly_linked_list nodes;
public:
Dynamic_Graph() { edges = doubly_linked_list(); nodes = doubly_linked_list(); }
~Dynamic_Graph();
Graph_Node* Insert_Node(unsigned node_key);
void Delete_Node(Graph_Node* node);
Graph_Edge* Insert_Edge(Graph_Node* from, Graph_Node* to);
void Delete_Edge(Graph_Edge* edge);
Rooted_Tree* SCC() const;
Rooted_Tree* BFS(Graph_Node* source) const;
};
any help would be welcome
EDIT:
i removed the calls to the destructors, however i still am getting a destructor call in the insert after function and i am not sure why.
EDIT 2:
adding more relevant lines of code:
Graph_Edge* Dynamic_Graph::Insert_Edge(Graph_Node* from, Graph_Node* to)
{
Graph_Edge* new_edge = new Graph_Edge(from, to);
from->Get_out_Nodes().List_Insert_After(to, from->Get_out_Nodes().Get_Tail());
to->Get_in_Nodes().List_Insert_After(from, to->Get_in_Nodes().Get_Tail());
edges.List_Insert(new_edge);
return new_edge;
}
this is the function that triggers the error where it access a deleted pointer despite it not supposed to be deleted, it triggers after finishing inserting the "to" node to the "from" node adjacency list.
void doubly_linked_list::List_Insert_After(list_item* x, list_item* y)
{
if (!y)
{
head = x;
tail = x;
return;
}
if (y == tail)
{
tail = x;
}
if (y->Get_Next())
{
y->Get_Next()->Set_Prev(x);
}
x->Set_Next(y->Get_Next());
x->Set_Prev(y);
y->Set_Next(x);
}
this function is the insert after function in the doubly linked list.
EDIT 3:
i tried to recrating the issue via the smallest amount of possible function, this is what i got:
#pragma once
#include < cstddef >
class List_Item
{
protected:
List_Item* next;
List_Item* prev;
public:
List_Item(): next(NULL), prev(NULL) {}
List_Item* Get_Next() { return next; }
List_Item* Get_Prev() { return prev; }
void Set_Next(List_Item* next) { this->next = next; }
void Set_Prev(List_Item* prev) { this->prev = prev; }
List_Item(const List_Item& item) : next(item.next), prev(item.prev){}
List_Item& operator=(const List_Item& item) { this->next = item.next; this->prev = item.prev; return *this; }
};
class Doubly_Linked_List
{
protected:
List_Item* head;
List_Item* tail;
public:
Doubly_Linked_List() : head(NULL), tail(NULL) {}
~Doubly_Linked_List();
List_Item* Get_Head() { return head; }
List_Item* Get_Tail() { return tail; }
void Set_Head(List_Item* h) { head = h; }
void Set_Tail(List_Item* t) { tail = t; }
void insert(List_Item* item);
void insert_after(List_Item* item, List_Item* after);
Doubly_Linked_List(const Doubly_Linked_List& list) : head(list.head), tail(list.tail) {}
Doubly_Linked_List& operator=(const Doubly_Linked_List& list) { this->head = list.head; this->tail = list.tail; return *this; }
};
Doubly_Linked_List::~Doubly_Linked_List()
{
List_Item* item = head;
while (item)
{
List_Item* next = item->Get_Next();
delete item;
item = next;
}
}
void Doubly_Linked_List::insert(List_Item* item)
{
item->Set_Next(head);
if (head)
head->Set_Prev(item);
if (!head)
tail = item;
head = item;
item->Set_Prev(NULL);
}
void Doubly_Linked_List::insert_after(List_Item* item, List_Item* after)
{
if (!after)
{
head = item;
tail = item;
return;
}
if (after->Get_Next())
{
after->Get_Next()->Set_Prev(item);
}
else
{
tail = item;
}
item->Set_Next(after);
item->Set_Prev(after);
after->Set_Next(item);
}
#pragma once
#include "List_Item.h"
#include"Doubly_Linked_List.h"
class Graph_Node :
public List_Item
{
protected:
const unsigned key;
Doubly_Linked_List in_nodes;
Doubly_Linked_List out_nodes;
public:
Graph_Node(unsigned key): key(key) {}
Doubly_Linked_List Get_In_Nodes() { return in_nodes; }
Doubly_Linked_List Get_Out_Nodes() { return out_nodes; }
};
class Graph_Edge :
public List_Item
{
protected:
Graph_Node* from;
Graph_Node* to;
public:
Graph_Edge(Graph_Node* f, Graph_Node* t): from(f), to(t) {}
Graph_Node* Get_From() { return from; }
Graph_Node* Get_To() { return to; }
};
int main()
{
unsigned node_key = 1;
Graph_Node* from = new Graph_Node(node_key++);
Graph_Node* to = new Graph_Node(node_key++);
from->Get_Out_Nodes().insert_after(to, from->Get_Out_Nodes().Get_Tail());
to->Get_In_Nodes().insert_after(from, to->Get_In_Nodes().Get_Tail());
}
You are not supposed to call destructors manually. They are called automatically for you.
You only need to use delete (which also calls the destructor) and only on objects that you have actually created with a call to new. Every other object will be destroyed automatically when the end of its scope is reached and its destructor will then be called automatically. This is also true for members of classes.
The only exception here is if you used a placement-new to create the object or if you intend to reuse its storage. But you probably don't intend to do something like this.
Remove the destructor of Dynamic_Graph completely. It is not needed.
Your class doubly_linked_list violates the rule of 0/3/5. The rule says that if you define a custom destructor, then you should also define a custom copy(/move) constructor and copy(/move) assignment operator.
If you violate this rule and you happen to make an implicit or explicit copy of an object of that class, then you are likely going to have undefined behavior because the destructors will be called twice and try to delete memory twice.
After question edit with a full example:
In the code you are showing now the destructor for Doubly_Linked_List is called four times (see https://godbolt.org/z/KmStwy). These calls are because Get_In_Nodes and Get_Out_Nodes return Doubly_Linked_List lists by-value as copies of the class members of Graph_Node. Since you call these functions four times in main, there are four Doubly_Linked_List temporary objects that need to be destructed (which the compiler does automatically).
You did not properly implement Doubly_Linked_List(const Doubly_Linked_List& list) (and the copy assignment operator) to actually copy the whole list, so your program still has undefined behavior. You need to implement the copy operations in such a way that the whole list is being deep copied, otherwise the destructor call of both the originial and the copy will delete the same nodes.
Alternatively you can define the copy operations as deleted:
Doubly_Linked_List(const Doubly_Linked_List& list) : head(list.head), tail(list.tail) = delete
Doubly_Linked_List& operator=(const Doubly_Linked_List& list) = delete;
in which case the compiler will not allow copying and give a compile-time error if you try to copy. But your current implementation is just as broken as it was before. You basically just copied what the implicit copy operations do. Read the link about the rule of 0/3/5 again and carefully since it is extremely important to avoid undefined behavior.
The fact that the destructor is called multiple times is not by itself a problem. That is normal if copies are involved. The problem is only that your class copy operations are broken. If you don't want to create copies in the Get_In_Nodes and Get_Out_Nodes though, then return by-reference instead.
In general you should not use new/delete in the way that you are doing in modern C++. Instead you can use std::make_unique and std::unique_ptr for owning pointers and if you do so, then none of your shown classes will need custom destructors (at least not custom destructors that have effective behavior different from the implicit one) and you can always use the rule of 0, meaning that you don't need to implement any of the copy operations either.
And if this is anything but a learning exercise, you shouldn't write your own list in the first place. std::list works fine and is almost surely superior to what you are coming up with.
Related
I'm trying to implement a linked list in C++. The list contains a pointer to a node type allocated on the heap
The code is as follow:
#include <memory>
template<typename T>
class node {
public:
node(T v) : value(v) {}
~node() = default;
T value;
node *next;
};
template<typename T, class Allocator = std::allocator<node<T>>>
class linked_list {
private:
node<T>* head;
Allocator alloc;
public:
linked_list() : head(nullptr) {}
~linked_list() {
for (auto start = head; start != nullptr; start = start->next) {
start->~node();
alloc.deallocate(start, 1);
}
}
void push_back(T value) {
node<T> *new_node = alloc.allocate(1);
new (new_node) node<T>(value);
if (head == nullptr) {
head = new_node;
return;
}
head->next = new_node;
head = new_node;
}
};
In main.cpp:
#include "linked_list.hpp"
int main() {
linked_list<int> a;
a.push_back(4);
a.push_back(5);
return 0;
}
When I ran it I got double free detected in cache T2.
What did I do wrong with the destructor ?
This is a common newbie error. You modified your loop control variable.
for (auto start = head; start != nullptr; start = start->next)
{
start->~node();
alloc.deallocate(start, 1);
}
You modified start (deleting the memory) in the for loop's body and then tried to dereference the pointer you just deleted in its continuation expression. BOOM! You are fortunate that the runtime library was smart enough to catch this and give you the "double free" error rather than, say, launch a nuclear missile.
This is one place a while loop is better than a for loop.
while (head)
{
auto to_del = head;
head = head->next;
to_del ->~node();
alloc.deallocate(to_del, 1);
}
I've left out a lot of commentary about your antiquated techniques because they don't relate to the problem you're having, but if you really want to substitute in a different kind of allocator you should look into using allocator_traits for allocation, construction, destruction, and deallocation of your elements.
There are other problems, such as push_back being completely wrong as to where it inserts the new node. Replacing head->next = new_node; with new_node->next = head; will at least keep your program from orphaning all of the new nodes.
I am trying to create an appendToTail function which will add a node to the end of a singly linked list.
I am having trouble in adding a node if the head is NULL(the linked list is empty)
class Node {
private:
Node* next;
int data;
public:
Node(int d, Node* n = NULL)
: data(d)
, next(n)
{
}
void appendToTail(int);
//other trivial functions(getters and setters etc. ) defined and
//declared
};
void Node::appendToTail(int d)
{
Node* end = new Node(d);
Node* n = this;
if (n == NULL)
n = end;
else {
while (n->next != NULL)
n = n->next;
n->next = end;
n->next->next = NULL;
}
end = NULL;
delete end;
}
int main()
{
Node* n = NULL;
n->appendToTail(5);
std::cout << n->getData(); //getData() is a function which
//retrieves the Data member variable
}
I am expecting to get 5 but I am getting an error which appears to be caused because my node remains null.
Now with modern C++ idioms we use smart pointers instead of raw pointers, it gives you the benefit of RAII (Resource acquisition is initialization) mechanism. In addition if you want an elegant solution to your problem you should introduce a List class with which you can express more clearly the concept of an empty list. It would give something like this:
#include <memory>
#include <iostream>
class List
{
public:
class Node
{
private:
std::shared_ptr<Node> next;
int data;
public:
Node(int d):next(nullptr),data(d){}
inline int getData() const {return data;}
inline std::shared_ptr<Node> getNext() const {return next;}
friend List;
};
List():head(nullptr),tail(nullptr){}
void appendToTail(int );
inline std::shared_ptr<Node> getHead() const {return head;}
inline std::shared_ptr<Node> getTail() const {return tail;}
private:
std::shared_ptr<Node> head;
std::shared_ptr<Node> tail;
};
void List::appendToTail(int d)
{
auto newTail = std::make_shared<Node>(d);
if (head == nullptr)
{
head = tail = newTail;
}
else
{
tail->next = newTail;
tail = newTail;
}
}
int main()
{
List l;
l.appendToTail(5);
std::cout<<l.getHead()->getData();
}
But you should definitely prefer std::list<T> or std::vector<T>.
Unfortunately there several errors with your approach. Semantic errors and a logical error with your interpretation of a linked list. Let's start with your initial misunderstanding. You cannot add a new tail to an empty list. Because it is emtpy. Meaning, not yet existing. Only if some object is existing/instantiated you can add a tail. You cannot add something to not existing stuff. So your idea to start with a Node* n = nullptr cannot work logically.
Additionally you are dereferencing a nullptr (major bug). That is also the main problem of your code. Nothing works. You need an instantiated object, before you can call it's member functions.
So before you can populate the list, you need to create/instantiate it initially. So you need to explicitly create the first node in your main function with
Node* n = new Node (5)
Then the list is existing and from now on you can add new members with calling appendToTail.
There are more semantic errors in your code which have luckily no side effects.
You must not delete the 'end' variable in your function. You want to keep the newly allocated memory for the new tail. But you introduced an additional sematic error by setting 'end' to nullptr and then call delete. Deleting a nullptr is a noOp and will do nothing. So, although you have a semantic error, this will not cause any trouble.
There is more:
For a pointer to Null you should always use nullptr.
And, your
if (n == NULL)
is always false. Before that, you assigned this to n. This is never NULL. You can delete the if else. Keep the statements from the else, except the
n->next->next = NULL;
That's not necessary. The constructor did that already for you. As explained, the next 2 statements should also be elimanted.
Additionally you may want to read a little more on the concept of linked lists.
I hope I could help a little
I'm reading lines from a file and storing them into linked list.
void add(llist *list, somevalue) {
Node *newnode = (Node *) malloc(sizeof(Node));
newnode->value = somevalue;
newnode->next = list->head;
list->head = newnode;
}
and I call this function from an initialize function which opens the file and reads lines from the file.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
And another problem that I realized is the values get concatenated every time I try to print the value. That is to say, the output would be:
First Loop: Tony
Second Loop: Peter
TonyPeter
Third Loop: Mir
PeterMir
TonyPeterMir
How do I fix it so the add function permanently adds the node to the linked list?
Why would the values be jumbled up like that?
----EDITED----
The list is a global variable, and here are some more snippets from the init function. This is the while loop with the problem:
//open file
//initialize & declare pointers
while (1) {
for (i = 0; i < max; i++) {
value[i] = '\0';
}
if (!(fgets(value,max,f))) {
//segfaults if I try to print out the list inside this block.
break;
}
add(list, value);
//the values are well separated in this statement
printf("id is %s\n", list->head->value);
//This print list works, but works weird as shown above.
print_list(list);
}
fclose(f);
//This print list doesn't work, the list is NULL
print_list(list);
And this is the print list function:
void print_users(llist *list) {
ListNode *e;
if (list->head == NULL) {
printf("NO USERS\r\n");
return;
}
e = list->head;
while (e != NULL) {
puts(e->id);
e = e->next;
}
}
So I don't have a good grasp at all on what you're exactly trying to do here, so we can only do but so much. You may consider posting a MCVE. However, I may be able to give you some pointers on building a linked list. I directly copied your add function into a linked list class that I just hurriedly built, and it worked fine, so there may be something else in your llist class that is causing the issue, or it could be something else in your code. The class and a brief description of the class are listed below.
basic node class
Note: I used templates, but you could just as easily remove the template statements and replace T with any type.
template<typename T>
class node {
private:
T data;
node* next;
public:
// The C++11 rule of 5
// Default Constructor
node(T value = T(), node* nxt = nullptr) : data(value), next(nxt) { }
// Copy Constructor
node(const node<T>& src) : data(src.data), next(src.next) { }
// Move Constructor
node(node<T>&& src) : data(src.data), next(src.next) {
src.data = T();
src.next = nullptr;
}
// Copy Assignment Operator
node<T>& operator=(const node<T>& src) {
this->data = src.data;
this->next = src.next;
return(*this);
}
// Move Assignment Operator
node<T>& operator=(node<T>&& src) {
this->data = src.data;
this->next = src.next;
src.data = T();
src.next = nullptr;
}
// Destructor
~node() {};
// Some functions to help with encapsulation
void set_next(node* nxt) {
this->next = nxt;
}
void set_data(T value) {
this->data = value;
}
node* get_next() {
return(this->next);
}
T& get_data() {
return(data);
}
};
linked list class body
Since you're using dynamic memory, you need to make sure you adhere to the rule of 3 or 5 depending on whether or not you're using C++11.
template<typename T>
class llist {
private:
node<T>* head;
public:
llist();
llist(const llist& src);
llist(llist&& src);
llist(~llist);
llist& operator=(const llist& src);
llist& operator=(llist&& src);
void push();
void insert();
};
default constructor
Nothing fancy here.
template<typename T>
llist<T>::llist() : head(nullptr) { }
copy constructor
Since you're using dynamic memory this is crucial
template<typename T>
llist<T>::llist(const llist& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
}
move constructor
template<typename T>
llist<T>::llist(llist&& src) {
// delete current nodes
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
// steal the sources list
this->head = src.head;
src.head = nullptr;
}
destructor
template<typename T>
llist<T>::~llist() {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
}
copy assignment operator
template<typename T>
llist& llist<T>::operator=(const llist<T>& src) {
node<T>* tmp = src.head;
while(tmp) {
this->push(tmp->get_data());
}
return(*this);
}
move assignment operator
template<typename T>
llist& llist<T>::operator=(llist<T>&& src) {
node<T>* tmp = this->head;
while(tmp) {
tmp = head->get_next();
delete head;
head = tmp;
}
this->head = src.head;
src.head = nullptr;
return(*this);
}
push member
this is essentially opposite of your add member.
template<typename T>
void llist<T>push(T data) {
node<T>* new_node = new node<T>(data);
if(this->head) {
node<T>* tmp = this->head;
while(tmp->get_next()) {
tmp = tmp->get_next();
}
tmp->set_next(new_node);
} else {
this->head = new_node;
}
}
insert member
This is essentially your add member.
template<typename T>
void llist<T>insert(T data) {
node<T>* new_node = new node<T>(data, this->head);
this->head = new_node;
}
I don't know if this will help, and you probably already have and know most of this, but I hope it helps.
In this code, it would appear that you attempted to 'malloc' space for a "llist" user defined object.
void init() {
llist *list = (llist *) malloc(sizeof(llist));
//
//bunch of file i/o codes
//
while (read file until it returns NULL) {
add(list, line);
//if I try to print the values of the list here it works.
}
//Outside the loop, the head is back to NULL
}
First, you tagged this as C++. In C++, you simply must use new and delete. The C++ compiler does not associate "malloc" with the ctor / dtor of your user created object called "llist". And I assure you that you really do want to create these two methods, even when each are simple. Really.
On the other hand, the C++ compiler does provide New and Delete, and will automatically invoke the ctor and dtor when appropriate for both dynamic variables (in heap), and automatic variables (on stack). The compiler will not support this with malloc.
Second, your function init() does not return or otherwise deliver the value of the automatic variable you named "list" to any other scope. (Typically, a list lifetime exceeds the life of any function that uses or creates it.)
So your object "list" only exists within the scope of the function init(), within the lifetime of init(). Not very useful.
So the handle to the list of 'malloc'ed things is lost, no longer accessible to anything else. After init(), where did you plan for the listHead to reside?
Even if you used new (and delete) the code still does not deliver the listHead anywhere.
To further your program, you need perhaps 1 of 2 things:
1) a return (from the function) of the "list" handle (I've been calling it "listHead" as you intended, right?)
llist* init() {
llist *listHead = ...
return(listHead);
}
OR
2) a parameter reference which your init function changes. This places the list head outside of init().
void init( llist** listHead) {
llist *list = ...
*listHead = list;
}
You might look into, and take hints from std::list, which has 40+ methods, though you might only need 10. For the methods you plan to implement, you should strive to conform to and use similar names and parameters.
Perhaps you meant to use a class data attribute with the label list (it is quite difficult to imagine this from what you provided). In this case, you should distinguish data attributes names to help you remember what it is, and that it has a different scope. For instance, I would use m_listHead. The prefix m_ (or often, simply the one char prefix '_') simply indicates to the reader that this symbol is a data attribute of a class. This idea is a common c++ idiom, and not enforced by the compiler, but is often part of a coding-standard.
Good luck.
I've created class for building a linked list. The class declaration is as follows:
class LinkedList
{
private:
int data;
LinkedList *next;
static int count;
public:
LinkedList(void);
~LinkedList(void);
int insert(int arg);
int remove(int arg);
bool find(int arg);
};
How can I make sure all nodes of this linked list are deleted? The destructor is made responsible for deleting just one node. I used to make linked list like this previously but never thought about clearing the memory.
The naive implementation
~LinkedList() {delete next;}
will do the right thing - delete will call the destructor on the next element, which will delete the one following it, and so on, to delete the whole list.
However, this means that the destructors are called recursively, so that deleting a very long list could cause a stack overflow. Iteration might be better:
~LinkedList() {
while (LinkedList * head = next) {
next = head->next;
head->next = nullptr;
delete head;
}
}
As noted in the comments, it might be more appropriate to have separate List and Node classes, with List responsible for memory management, and Node a simple aggregate containing the data and the link. Then there's less scope for error in the destructor, as it doesn't need to nullify any pointers to prevent recursion:
struct Node {
int data;
Node * next;
};
struct List {
Node * head;
~List() {
while (Node * victim = head) {
head = victim->next;
delete victim;
}
}
};
I've just implemented the Linked List. It works perfectly fine but even tough I've seen notation I am unable to create working destructor on Node, that's why it's unimplemented here in code.
I need to implement working destructor on node
Destructor of List but this one is simple I will just use the destructor from Node class(but I need this one).
Make the List friendly to Node so I will not have to use getNext(), but I think I can
handle it myself(not sure how, but I'll find out).
Please look at the code it is perfectly fine, just will work if you copy it.
#include <cstdio>
#include <cmath>
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
class Node {
public:
Node(Node* next, int wrt) {
this->next = next;
this->wrt = wrt;
}
Node(const Node& obiekt) {
this->wrt = obiekt.wrt;
this->next = obiekt.next;
}
~Node() {}
void show() {
cout << this->wrt << endl;
}
int getWrt(){
return this->wrt;
}
Node* getNext(){
return this->next;
}
void setNext(Node* node){
this->next = node;
}
private:
Node* next;
int wrt;
};
class List{
public:
List(int wrt){
this->root = new Node(NULL, wrt);
}
List(const List& obiekt){
memcpy(&this->root,&obiekt.root,sizeof(int));
Node* el = obiekt.root->getNext();
Node* curr = this->root;
Node* next;
while(el != NULL){
memcpy(&next,&el,sizeof(int));
curr->setNext(next);
curr = next;
next = curr->getNext();
el = el->getNext();
/* curr->show();
next->show();
el->show(); */
}
}
void add(int wrt){
Node* node = new Node(NULL, wrt);
Node* el = this->root;
while(el->getNext() != NULL){
//el->show();
el = el->getNext();
}
el->setNext(node);
}
void remove(int index){
Node* el = this->root;
if(index == 0){
//deleting old one
this->root = this->root->getNext();
}
else{
int i = 0;
while(el != NULL && i < index - 1){
// el->show();
el = el->getNext();
i++;
}
if(el!=NULL){
Node* toRem = el->getNext();
Node* newNext = toRem->getNext();
el->setNext(newNext);
//deleteing old one
}
}
}
void show(){
Node* el = this->root;
while(el != NULL){
el->show();
el = el->getNext();
}
}
~List(){}
private:
Node* root;
};
int main(){
List* l = new List(1); //first list
l->add(2);
l->add(3);
l->show();
cout << endl;
List* lala = new List(*l); //lala is second list created by copy cosntructor
lala->show();
cout << endl;
lala->add(4);
lala->remove(0);
lala->show();
return 0;
}
I suggest you to start with implementing destructor of List. Since you dynamically allocated memory by using new, you should free it by using delete. (If you used new[], it would be delete[]):
~List()
{
Node* currentNode = this->root; // initialize current node to root
while (currentNode)
{
Node* nextNode = currentNode->getNext(); // get next node
delete currentNode; // delete current
currentNode = nextNode; // set current to "old" next
}
}
Once you have proper destructor, you should try whether your copy constructor is correct:
List* lala = new List(*l);
delete l; // delete list that was used to create copy, shouldn't affect copy
you will find out that your copy constructor is wrong and also causes your application to crash. Why? Because purpose of copy constructor is to create a new object as a copy of an existing object. Your copy constructor just copies pointers assuming sizeof(Node*) equal to sizeof(int). It should look like this:
List(const List& list)
{
// if empty list is being copied:
if (!list.root)
{
this->root = NULL;
return;
}
// create new root:
this->root = new Node(NULL, list.root->getWrt());
Node* list_currentNode = list.root;
Node* this_currentNode = this->root;
while (list_currentNode->getNext())
{
// create new successor:
Node* newNode = new Node(NULL, list_currentNode->getNext()->getWrt());
this_currentNode->setNext(newNode);
this_currentNode = this_currentNode->getNext();
list_currentNode = list_currentNode->getNext();
}
}
Also your function remove is wrong since it "removes" reference to some Node but never frees memory where this Node resides. delete should be called in order to free this memory.
"I need to implement working destructor on node" - No, you don't. Node itself doesn't allocate any memory, thus it shouldn't free any memory. Node shouldn't be responsible for destruction of Node* next nor cleaning memory where it's stored. Don't implement destructor nor copy constructor of Node. You also want to read this: What is The Rule of Three?
"Make the List friendly to Node so I will not have to use getNext()" - You want to say within Node class, that class List is its friend:
class Node
{
friend class List; // <-- that's it
Note that from these 5 headers that you include your code requires only one: <iostream>.
Also note that writing using namespace std; at the beginning of the file is considered bad practice since it may cause names of some of your types become ambiguous. Use it wisely within small scopes or use std:: prefix instead.
The linked list destructor will be called either when delete is used with a previously allocated pointer to a linked list or when a linked list variable goes out of scope (e.g., a local variable is destroyed when returning from a function).
The destructor for the linked list should be responsible to free the memory you previously reserved for the nodes (i.e., using add operation). So, basically, you need to traverse the list of nodes and apply the delete operation on each one of them. There is a little trick: when you are about to delete a node you must be careful not to lose the pointer to the next element (when a node is deleted you cannot be sure that next member will still be valid).
If you want to create a destructor for your Node, it should be quite simple actually.
Here it is:
class Node {
private:
int wrt;
Node* next;
public:
Node(Node* next, int wrt) {
this->next = next;
this->wrt = wrt;
}
// Your desired destructor using recursion
~Node() {
if ( next != NULL )
delete next;
}
};
It's that simple :)
Basically, right before the Node is deleted, if next is not empty, we delete next, which will again call the destructor of next, and if next->next is not empty, again the destructor gets called over and over.
Then in the end all Nodes get deleted.
The recursion takes care of the whole thing :)