I am working with doubly linked list. Every function operates well but at the end of main(), it stalls few seconds and return an unexpected random value.
At first I thought it was caused by the report() function, thus I put one more add() function at the end but nothing got fixed. I doubt it is a memory deallocating problem, but I don't see where some object got pre-deallocated.
(Compiled using Code::Blocks 17.12).
Here is my .cpp file (all in one):
#include <iostream>
using namespace std;
typedef struct element {
element(){
data = 0;
next = 0;
prev = 0;
}
~element(){
delete next;
delete prev;
cout << "element destructed" << endl;
}
int data;
element* next;
element* prev;
} elem;
typedef struct doublylinkedlist{
doublylinkedlist(){
head = 0; tail = 0;
}
~doublylinkedlist(){
while(head!=0) {
head = head->next;
delete head->prev;
}
delete tail;
cout << "list destructed" << endl;
}
elem* head;
elem* tail;
} doublyll;
doublyll ls;
void add(){
elem* temp = new elem;
cout << "Enter an integer: ";
cin >> temp->data;
if(ls.head == 0) {//empty
ls.head = new elem;
ls.head = temp;
} else{
if(ls.tail == 0){ //1-item list
ls.tail = new elem;
ls.tail = temp;
ls.head->next = ls.tail;
ls.tail->prev = ls.head;
}
else{
temp->prev = ls.tail;
ls.tail->next = temp;
ls.tail = temp;
}
}
}
void report(){
if(ls.head == 0) cout << "List is empty!" << endl;
else{
elem *temp = ls.head;
do{
cout << temp->data << endl;
temp = temp->next;
} while (temp != 0);
}
}
int main(){
report();
add();
add();
add();
report();
add();
return 0;
}
Could someone point out where the error comes from and how to fix it? I want the main() not to stall and return 0 as usual, not to the opposite.
This is the program when executed, this is my build message
First point: The elements will be deallocated by the class doublylinkedlist, so deallocating elements in the class element will cause double-deallocation.
Therefore, you should remove two delete statements from the destructior of the lass element.
~element(){
/* remove them */
//delete next;
//delete prev;
cout << "element destructed" << endl;
}
Second point: In the destructor of doublylinkedlist, head->prev is read after head = head->next; without checking if head is NULL.
head can be NULL by the assignment, so it should be checked.
~doublylinkedlist(){
while(head!=0) {
head = head->next;
if (head!=0) /* add this */
delete head->prev;
}
delete tail;
cout << "list destructed" << endl;
}
The last element will be deallocated by delete tail;, so this code looks tricky but should be OK.
Extra point: These code segments
ls.head = new elem;
ls.head = temp;
and
ls.tail = new elem;
ls.tail = temp;
are causing memory leaks by allocating elements and throwing them right away.
You should remove the extra allocations.
/* remove this */
//ls.head = new elem;
ls.head = temp;
and
/* remove this */
//ls.tail = new elem;
ls.tail = temp;
Unless you are using std::shared_ptr or similar constructs each object needs to have one other object which is it's owner and is responsible for deallocating it. Your code needs to have clear semantics for transferring ownership (e.g. a function createNode() would expect its caller to destroy the node).
In your code nodes are both deleted by the list and by each element. This means everything gets deleted twice (or more). In your particular case this is the sequence of events on destruction of doublylinkedlist:
doublylinkedlist deletes its first element.
The destructor of the first element deletes its previous element, this is null so has no effect
The destructor of the first element deletes its next element (the second element).
The destructor of the second element deletes its previous element (the first element)
The destructor of the first element deletes its previous element, this is null so has no effect
The destructor of the first element deletes its next element (the second element).
This infinite loop eventually causes a stack overflow. Note that this isn't guaranteed to be the exact sequence of events as deleting an object twice is undefined behaviour so potentially anything could happen.
The simple fix is to remove the element destructor and have the list be responsible for the lifetime of all elements.
You should also modify your doublylinkedlist destructor as it will attempt to dereference a null pointer on the last element, you also don't need to delete tail as it should have already been deleted. E.g:
~doublylinkedlist(){
while(head!=0) {
auto temp = head;
head = head->next;
delete temp;
}
}
You shoudl also make sure you obey the rule of three/five). One way of doing this is to make use of smart pointers, for example using unique_ptrs your code could look like this:
#include <iostream>
#include <memory>
using namespace std;
typedef struct element {
element() {
data = 0;
next = nullptr;
prev = nullptr;
}
~element() {
cout << "element destructed" << endl;
}
int data;
std::unique_ptr< element > next;
element* prev;
} elem;
typedef struct doublylinkedlist {
doublylinkedlist() {
head = 0; tail = 0;
}
~doublylinkedlist() {
std::cout << "list destructed\n";
}
std::unique_ptr< elem > head;
elem* tail;
} doublyll;
doublyll ls;
void add() {
std::unique_ptr<elem> temp(new elem());
cout << "Enter an integer: ";
cin >> temp->data;
if (ls.head == nullptr) {//empty
ls.head = std::move(temp);
}
else {
if (ls.tail == nullptr) { //1-item list
ls.head->next = std::move(temp);
ls.tail = ls.head->next.get();
ls.tail->prev = ls.head.get();
}
else {
temp->prev = ls.tail;
ls.tail->next = std::move(temp);
ls.tail = ls.tail->next.get();
}
}
}
void report() {
if (ls.head == 0) cout << "List is empty!" << endl;
else {
elem *temp = ls.head.get();
do {
cout << temp->data << endl;
temp = temp->next.get();
} while (temp != 0);
}
}
int main() {
report();
add();
add();
add();
report();
add();
return 0;
}
The ownership of elements is now explicit, the list owns head and head owns its next node which owns its next node etc. Destroying the list automatically destroys the first node which automatically destroys the second node etc. In this code you can actually omit the destructors completely. This should also help to prevent memory leaks, for example if you decide to add some error checking to add the unused temp element gets automatically deleted:
void add() {
std::unique_ptr<elem> temp(new elem());
cout << "Enter an integer: ";
cin >> temp->data;
if (!cin || temp->data > 100) {
cout << "invalid input value\n";
return; // temp is automatically deleted here
}
...
}
Related
I'm trying to make single linked list's fucntions.
but it reports an error. like this..
i am trying to a lot of thing about that.
like using rvlaue reference, double pointer
but nothings work..
what is the problem?
and can i return p pointer in getNode(int k) fucntion?
#include<iostream>
using namespace std;
template<typename T>
class SingleLList {
private:
template<typename T>
struct Node {
T data;
Node<T>* next;
};
Node<T>* head;
int size; // for List size
public:
SingleLList() :head(nullptr) {};
~SingleLList() {
Node<T>* delNode;
while (head->next != nullptr) {
delNode = head;
head = head->next;
delete delNode;
}
};
// add Node at index th
void addNode(int index, T data) {
if (index < 0)return;
Node<T>* newNode = new Node<T>;
newNode->data = data;
newNode->next = nullptr;
if (index == 0) { // add at 0
// empty
if (head == nullptr) head = newNode;
// not empty
else {
newNode->next = head->next;
head = newNode;
}
size++;
}
else {
Node<T>* prev = head;
for (int i = 1; i < index && prev != nullptr; i++) {
prev = prev->next;
}
newNode->next = prev->next;
prev->next = newNode;
size++;
}
}
// traversa
void showList()const {
Node<T>* p = head;
cout << "Single Linked List : [ ";
while (p != nullptr) {
cout << p->data << " ";
p = p->next;
}
cout << " ]" << "total elements are : "
<< size << endl;
}
// return k th Node by reference.
Node<T>*& getNode(int k)const {
if (head == nullptr || k > size) {
Node<T>* temp = nullptr;
return temp;
}
// Node<T>* p; < -- is it okay?
Node<T>* p = new Node<T>;
p= head;
for (int i = 1; i < k && p->next != nullptr; i++) {
p = p->next;
}
cout << " address of p : " << &p << endl;
cout << "value of p : " << p << endl;
return p;
}
// delete n Node in list
void deleteNode(Node<T>*& n) {
cout << "address of n : " << &n << endl;
cout << n->data << endl;
if (n->next == nullptr) { // if last node
delete n->next;
n = nullptr; //
size--;
}
else {
Node<T>* del_node = n->next;
n->data = n->next->data;
n->next = n->next->next;
delete del_node;
size--;
}
}
};
int main() {
SingleLList<int> sll;
sll.addNode(0, 4);
sll.addNode(1, 5);
sll.addNode(2, 6);
sll.addNode(3, 8);
sll.addNode(4, 9);
sll.showList();
sll.deleteNode(sll.getNode(5));
sll.showList();
return 0;
}
and in main i make Linked List like this.
Node<T>*& getNode(int k)const {
if (head == nullptr || k > size) {
Node<T>* temp = nullptr;
return temp;
This same basic bug occurs several times in the shown code. All instances of this bug will need to be fixed.
temp is a local variable. Once this function returns, it goes out of scope and gets destroyed.
However: this function is declared as returning a reference to a pointer, and by returning temp this ends up returning a reference to an object that's already destroyed, when the function returns. All subsequent use of this reference automatically becomes undefined behavior, and the likely reason for your crash. For example:
sll.deleteNode(sll.getNode(5));
For example, getNode() returns a reference here. To add insult to injury this reference isn't even used immediately, but it gets passed to deleteNode(). By that time temp, or a reference to whatever was originally returned from getNode, is a distant memory and was already destroyed a long, long time ago, and attempting to reference it will not end well.
There are likely other issues, but this is fundamental, and fixing it will require fundamental changes to the shown logic, as such the first order of business will be to redesign the shown code, and it will likely involve other major changes to the rest of the code, as well.
Hi I have the following code, and keep getting memory leaks, can someone help me fix this please, I've been at this for hours but cant seem to find why there is a memory leak, I am new with nodes, I think the problem is with the destructor, but can't seem to pin point exactly what, please help!
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next;
};
class LinkedList {
public:
LinkedList() { // constructor
head = NULL;
}
~LinkedList(); // destructor
void insert(int val);
void display();
private:
Node* head;
};
LinkedList::~LinkedList() { delete head; }
// function to add node to a list
void LinkedList::insert(int val) {
Node* newnode = new Node();
newnode->data = val;
newnode->next = NULL;
if (head == NULL) {
head = newnode;
} else {
Node* temp = head; // head is not NULL
while (temp->next != NULL) {
temp = temp->next; // go to end of list
}
temp->next = newnode; // linking to newnode
}
}
void LinkedList::display() {
if (head == NULL) {
cout << "List is empty!" << endl;
} else {
Node* temp = head;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
}
int main() {
LinkedList* list = new LinkedList();
list->insert(999);
list->insert(200);
list->insert(300);
list->insert(700);
list->insert(500);
cout << "Linked List data" << endl;
list->display();
delete list;
return 0;
}
An alternative to Abel's answer with the Node-destroying Nodes:
LinkedList::~LinkedList()
{
while (head)
{
Node * temp = head;
head = head->next;
delete temp;
}
}
The LinkedList loops removes, and deletes the first Node until there are no Nodes left.
Why do I prefer this approach? Two reasons:
Ownership. Who is responsible for managing the nodes? With the loop, managing the Nodes is entirely in the hands of LinkedList. If Nodes can destroy one another, management is split between LinkedList and Node, and both owners need to remain in agreement about the state of the managed resource. Maintaining this agreement is tricky and tricky means more code you can get wrong. For example, if LinkedList isn't careful when removing a single Node from the list, that Node will recursively destroy the rest of the list. Ooops.
The second reason is recursion. If the list gets too long, the program will exhaust its automatic storage (Usually causing a Stack Overflow) and become unstable. You've limited the size of the list you can handle unnecessarily and the only way you'll know you've exceeded the limit is when the program fails.
The access violation the Asker has been experiencing I have been unable to reproduce. I may have accidentally fixed it.
I don't think you want destructing a node to delete the entire list. You could but I think each node should be independent of the others - the linked list class is where list level things should happen.
Also, you don't want the destructor to contain the code to clear the list because you may want to clear the list at some arbitrary point - so the linked list should have a clear function that is called from the linked list destructor and can be called from other places too.
So the destructor would call this function to clear the list:
void LinkedList::clear() {
Node* next;
Node* temp = head;
while (temp != NULL) {
next = temp->next;
delete temp;
temp = next;
}
head = NULL;
}
The whole code would be:
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next;
Node() : data(0), next(NULL) {
cout << "Constructed default node\n";
}
Node(int data) : data(data), next(NULL) {
cout << "Constructed node: " << data << "\n";
}
~Node() {
cout << "Destructed node: " << data << "\n";
}
};
class LinkedList{
public:
LinkedList() { // constructor
head = NULL;
}
~LinkedList() {
clear();
}
void insert(int val);
void display();
void clear();
private:
Node* head;
};
// function to add node to a list
void LinkedList::insert(int val) {
Node* newnode = new Node(val);
if (head == NULL) {
head = newnode;
}
else {
Node* temp = head; // head is not NULL
while (temp->next != NULL) {
temp = temp->next; // go to end of list
}
temp->next = newnode; // linking to newnode
}
}
// function to delete the entire list
void LinkedList::clear() {
Node* next;
Node* temp = head;
while (temp != NULL) {
next = temp->next;
delete temp;
temp = next;
}
head = NULL;
}
// function to display the entire list
void LinkedList::display() {
if (head == NULL) {
cout << "List is empty!" << endl;
}
else {
Node* temp = head;
while (temp != NULL) {
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
}
}
int main() {
LinkedList list;
cout << "Creating List\n";
list.insert(999);
list.insert(200);
list.insert(300);
list.insert(700);
list.insert(500);
cout << "Linked List data:\n";
list.display();
cout << "Clearing list\n";
list.clear();
cout << "Creating List\n";
list.insert(400);
list.insert(600);
cout << "Linked List data:\n";
list.display();
cout << "NOT clearing list (should happen automatically\n";
return 0;
}
You can try it here: https://onlinegdb.com/HJlOT1ngqP
The output:
Creating List
Constructed node: 999
Constructed node: 200
Constructed node: 300
Constructed node: 700
Constructed node: 500
Linked List data:
999 200 300 700 500
Clearing list
Destructed node: 999
Destructed node: 200
Destructed node: 300
Destructed node: 700
Destructed node: 500
Creating List
Constructed node: 400
Constructed node: 600
Linked List data:
400 600
NOT clearing list (should happen automatically
Destructed node: 400
Destructed node: 600
For my homework, I need to make methods that add (append) and remove (serve) nodes from a Deque. However, when trying to serve the last node I struggle with memory leaks since I don't know how to retrieve the node that's not being used anymore to the system.
Here's the code for the method.
void Deque::serveAtRear(int &x)
{
if(empty())
{
cout << "Fila Vazia" << endl;
exit(0);
}
else
{
DequePointer q; //Pointer before p
q = head; //q starts at the deque's head
p = head->nextNode; //Pointer for the next node
if(q->nextNode==NULL) //If there's only one node
{
x = q->entry; //x will receive the value of the removed node to print it afterward
head = tail = NULL;
delete p; //I don't know if I'm using the "delete" right
}
else
{
while(p->nextNode != NULL)
{
p = p->nextNode;
q = q->nextNode;
if(p->nextNode==NULL)
{
q->nextNode=NULL;
}
}
x = p->entry;
delete p->nextNode;
}
delete q->nextNode;
}
}
To add nodes I have this method:
void Deque::appendAtFront(int x)
{
if(full())
{
cout << "FULL" << endl;
exit(0);
}
else
{
p = new DequeNode;
p->entry=x;
if(p == NULL)
{
cout << "Can't insert" << endl;
exit(0);
}
else if(empty())
{
head = tail = p;
p->nextNode=NULL;
}
else
{
p->nextNode=head;
head = p;
}
}
}
Also I can't use the "deque" lybrary
Generic answer to how avoid leaks is: avoid manual memory allocation.
For example you can use smart pointers, e.g. std::unique_ptr and instead of calling new DequeNode call std::make_unique<DequeNode>(). Compiler will then point places where your code needs to be adapted, as you will limit yourself so that each of your DequeNode(s) will be owned by just one unique_ptr at the same time. Example pop function (where head and DequeNode.next are uniuque_ptrs) based on your serveAtRear:
if (!head)
{
return;
}
DequeNode* q; //Pointer before p
DequeNode* p; //will be tail eventually
q = head.get(); //q starts at the deque's head
p = q->next.get(); //Pointer for the next node
if(!p) //If there's only one node
{
x = q->entry; //x will receive the value of the removed node to print it afterward
head.reset(); // hear we release DequeNode pointed by head
}
else
{
while(p->next)
{
q = p;
p = q->next.get();
}
x = p->entry;
q->next.reset(); // here we release tail (i.e. p)
}
Of course implementation of push needs to be adopted too :).
I am using pointers for the first time, my code runs correctly but I need to print a destructor from another .cpp file and don't know how to do so.
After a node is dropped using these two functions:
bool LList::remove(node* r) {
if (r == NULL || search(r->key) == NULL) {
return false;
}
if (r == head) {
head = head->next;
}
else {
node* prev = getb4(r);
prev->next = r->next;
}
r->next = NULL;
return true;
}
bool LList::drop(int k) {
node* currentNode = search(k);
if (currentNode == NULL || !remove(currentNode))
return false;
node* tmp = currentNode;
while (tmp != NULL) {
currentNode = currentNode->dup;
remove(tmp);
tmp = currentNode;
}
return true;
}
... it correctly prints "(key) removed", using this function from the main.cpp.
void testDrop(LList& L) {
int key;
cout << "Enter key: ";
cin >> key;
if (L.drop(key))
cout << key << " removed\n";
else
cout << key << " not found in list\n";
}
However, I also need it to print the destructor from my node.cpp without altering my main.cpp. Here is the destructor:
node::~node() {
if (next) delete next;
if (dup) delete dup;
cout << "NODE DESTRUCT: key=" << key << " data=" << data << endl;
}
Any advice would be appreciated, thank you.
I'm assuming that by printing you mean executing the destructor. In this case whenever you call delete on an object the compiler sort of checks to make sure that
a destructor is present within the object and then executes it. So in this case you would call delete n; where n is your node. Also when you call the remove node method you can also call delete on that node, so long as you're sure that your linked list and node destructor are taking care of the pointers appropriately as to not ruin the order of your list, or cause any other more serious problems such as memory leaks or dangling pointers.
I want to delete a certain element from a linked list (it's a list of numbers).
When i find the right number, i check if the previous element is NULL. If it is, it's the head of the list, and i just move that pointer, and if it isn't i re-link elements, so that the previous element points to the next element of the element to be deleted.
Now, this works fine, unless i uncoment the following command:
delete old;
Now, old is a pointer that points to the element that needs to be deleted.
I want to delete the element, not just re-link the list.
// zag.h - header file
#ifndef _zag_h_
#define _zag_h_
#include <iostream>
using namespace std;
struct Elem {
int n;
Elem* next;
Elem(int bbr,Elem* nex = NULL){n = bbr; next = nex;}
~Elem(){delete next;}
};
class Lista {
Elem* head;
public:
Lista(){
head=NULL;
}
~Lista(){
}
void put(int broj){
Elem* temp = new Elem(broj);
Elem* n0 = NULL;
Elem* n1 = head;
while(n1!=NULL && temp->n >= n1->n){
n0 = n1;
n1 = n1->next;
}
if(n0 == NULL){
temp->next=head;
head = temp;
}
else {
n0->next = temp;
temp->next = n1;
//if(n1==NULL)tail=temp;
}
//cout << head->n << endl;
}
void remove(int num){
Elem* n1 = head;
Elem* n0 = NULL;
while(n1!=NULL && n1->n!= num){
n0 = n1;
n1 = n1->next;
}
if(n0 == NULL){
Elem* old = n1;
head = head->next;
n1 = n1->next;
delete old;
}
else {
Elem* old = n1;
n1 = n1->next;
n0->next = n1;
cout << old->n;
delete(old);
}
}//remove
void write(){
Elem* temp = head;
while(temp){
cout << temp->n << " ";
temp = temp->next;
}
cout<<endl;
} //ispisi
};
#endif
// main.cpp file
#include "zaglavlje.h"
#include <iostream>
using namespace std;
void main(){
cout << "Welcome " << endl;
Lista* l = new Lista();
l->put(4);
l->put(2);
l->put(8);
l->put(7);
l->put(6);
l->put(9);
l->put(11);
l->put(15);
l->put(17);
l->put(2);
l->put(1);
l->write();
l->remove(11);
//l->remove(2);
//l->remove(2);
//l->remove(11);
//l->remove(15);
cout << "ispisujemo elemente liste nakon brisanja" << endl;
l->ispisi();
}
So, i insert some elements in the list, write the list elements (which all seems to work fine), and then i call a function to remove one element. After that, when i try to write the list (to check if the element is really removed) i get the following error:
An unhandled win32 exception occurred in test.exe
and the debugger points the line
cout << temp->n << " ";
in the write function.
Without calling the delete old command it all works fine.
When your list is empty, you are attempting to access the "first" element anyway:
if (n0 == NULL && n1 == NULL)
{
// empty list, do nothing
}
else if (n0 == NULL && n1 != NULL)
{
Elem* old = n1;
head = head->next;
n1 = n1->next;
delete old;
}
else
{
Elem* old = n1;
n1 = n1->next;
n0->next = n1;
delete old;
}
When you remove the delete's in your current code, you are keeping the memory allocated, so you won't get an access violation when you access it incorrectly. When you add them back in, you are accessing memory that is no longer allocated when the list is empty.
This will also be a problem when you first run the program is head is not initialized.