C++ Print Destructor - c++

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.

Related

Unexpected return value from main() function

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

Link list add,delete and print imlementation

The code bellow inserts nodes correctly but
I have a problem that when try to print the list the program unfortunately stop working.
The error message is : your project has stopped working.
This is my code:
#include <iostream>
#include <string>
using namespace std;
typedef struct st {
string data;
int ISBN;
string Title;
string Author;
int publishedyear;
bool borrow;
st* next;
} NODE;
NODE* add(NODE* head, int isbn)
{
NODE *p1, *p2;
NODE* n;
n = new NODE;
n->ISBN = isbn;
if (head == NULL) {
head = n;
return head;
}
if (n->ISBN < head->ISBN) {
n->next = head;
head = n;
return head;
}
p1 = p2 = head;
while (p2 != NULL) {
if (n->ISBN < p2->ISBN) {
n->next = p2;
p1->next = n;
return head;
}
else {
p1 = p2;
p2 = p2->next;
}
}
n->next = p2;
p1->next = n;
return head;
}
void print(NODE* head)
{
NODE* p;
p = head;
if (head == NULL) {
cout << "empty list" << endl;
}
while (p != NULL) {
cout << "Book ISBN Is : " << p->ISBN << endl;
p = p->next;
}
}
void main()
{
// cout << "hi";
NODE* head;
head = NULL;
string op;
int isbn;
cout << "Enter the opertion in the following format : op , ISBN" << endl;
while (1) {
cin >> op;
if (op == "add") {
cin >> isbn;
if (op == "add") {
head = add(head, isbn);
cout << "book with thie ISBN code " << isbn << " is added successfuly."
<< endl;
}
}
else if (op == "print") {
print(head);
}
else {
cout << "Enter vaild operation! ." << endl;
}
}
}
any suggestions ?
The answer was pointed out, but... I feel awfully unsatisfied by the state of your code, so allow me to give you a few tips.
Note: unless the point is to build a list, do reuse the existing standard containers (vector, in particular) and algorithms (sort) rather than building your own.
Let's start with the basics, this is the year 2016 you should have access to C++11 by now.
C++11 allows to initialize data-members straight at the point of declaration, and I recommend that you do it for all built-in types (integrals, booleans, floating points and pointers) since by default they otherwise contain garbage which is puzzling.
struct Node {
std::string data;
int ISBN = 0;
std::string title;
std::string author;
int publishedyear = 0;
bool borrow = false;
Node* next = nullptr;
};
Note that this alone solves your bug. And it also avoids forgetting it the next time.
Secondly, the add method should NOT be responsible for creating a node. This is mixing concerns, and it also leaves most of the node with default values and no way to access it without looking for it by its ISBN.
There's also a point that the add method does not account for: what if the ISBN is already in the list?
// Adds the new node to the list, maintaining the ordering by ISBN.
//
// Returns the new head of the list, unless an existing node in the list already
// has this ISBN in which case returns `nullptr`.
Node* add(Node* head, Node* node) {
assert(node != nullptr && "Null argument provided");
if (head == nullptr) {
return node;
}
if (node->ISBN < head->ISBN) {
node->next = head;
return node;
}
if (node->ISBN == head->ISBN) {
return nullptr;
}
// Find "current" such that "current->ISBN" < "node->ISBN" and
// "node->ISBN" <= "current->next->ISBN"
Node* current = head;
while (current->next != nullptr && node->ISBN > current->next->ISBN) {
current = current->next;
}
if (node->ISBN == current->next->ISBN) {
return nullptr;
}
node->next = current->next;
current->next = node;
return head;
}
Note: assert requires #include <cassert>.
Your print method is already pretty good, congratz!
Just two nitpicks:
if you know that nothing further will be executed, return immediately, don't wait
don't use endl, it both appends an end of line AND flushes the buffer immediately, which leads to performance issues more often than not
// Prints the list, in order.
void print(Node* head) {
if (head == nullptr) {
std::cout << "empty list\n";
return;
}
for (Node* p = head; p != nullptr; p = p->next) {
std::cout << "Book ISBN: " << p->ISBN << "\n";
}
}
And finally, the modified main.
Note that I expanded the help text a bit, and provided a (clean) quit operation.
The main change, however, is dealing without input error. Dealing with output error is left as an exercise to the reader (hint: make them throw).
It would also be a good exercise to properly deal with the allocated memory.
int main() {
std::cout << "Enter one of the following operations when prompted:\n"
" - add <isbn>\n"
" - print\n"
" - quit\n";
Node* head = nullptr;
while (1) {
std::cout << "> ";
std::string op;
if (!(std::cin >> op)) {
std::cerr << "An error occurred reading the operation, sorry\n";
break;
}
if (op == "quit") {
std::cout << "See you later!\n";
break;
}
if (op == "print") {
print(head);
continue;
}
if (op == "add") {
int isbn = 0;
if (!(std::cin >> isbn)) {
std::cout << "Please provide a correct ISBN!\n";
continue;
}
Node* node = new Node();
node->ISBN = isbn;
Node* h = add(head, node);
if (h == nullptr) {
std::cout << "This ISBN was already provided!\n";
delete node;
continue;
}
head = h;
continue;
}
std::cout << "Please enter a valid operation!\n";
}
// Deal with allocated memory ;)
}
st::next is never set to NULL. This makes testing p!=NULL in print somewhat problematic.
Solution: NULL next when the node is the tail node.

C++ Singly Linked List Insertion Sort

So hey, I'm having a problem with this project I have. I'm supposed to read integers from a file and insert them into a list. There's a findSpot function that needs to be implemented that traverses the linked list and if the next node's value is larger than what is being checked, it returns the current "spot". And then we output the linked list to a separate file.
Here's the code.
#include <iostream>
#include <fstream>
using namespace std;
class listNode {
public:
int value;
listNode* next;
friend class linkedList;
listNode()
: value(0)
, next(NULL)
{
}
public:
~listNode(){
};
};
class linkedList {
listNode* listHead;
public:
linkedList()
: listHead(NULL)
{
}
bool isEmpty()
{
return (listHead == 0);
}
void listInsert(int data, listNode* spot)
{
listNode* newNode;
newNode->value = data;
newNode->next = NULL;
if (isEmpty()) {
listHead = newNode;
}
else {
newNode->next = spot->next;
spot->next = newNode;
cout << newNode;
}
}
/*void listDelete ()
{
}*/
listNode* findSpot(int data)
{
listNode* spot;
spot = listHead;
while (spot->next != 0 && spot->next->value < data) {
spot = spot->next;
}
return spot;
}
void printList(listNode* spot)
{
listNode* newNode = spot;
while (newNode != NULL) {
cout << "Inserting " << newNode->value << ": "
<< "listHead-->(" << newNode->value << "," << newNode->next->value << ")-->(";
newNode = newNode->next;
}
cout << endl;
}
/*~linkedList()
{
listNode* temp = spot->next;
spot->next = spot->next->next;
delete temp;
}*/
};
int main(int argc, char* argv[])
{
int data;
listNode* spot;
ifstream infile;
infile.open(argv[1]);
ofstream outfile(argv[2]);
cout << "Reading Data from the file" << endl;
while (infile >> data) {
cout << data << endl;
}
infile.close();
linkedList myList;
infile.open(argv[1]);
while (infile >> data) {
myList.findSpot(data);
myList.listInsert(data, spot);
myList.printList(spot);
}
cout << "Printing your linked list to the output file.";
/*while (outfile.is_open())
{
myList.printList();
}*/
infile.close();
outfile.close();
return 0;
}
I don't know if the problem lies mainly in the insertList function or if it's the findSpot function. The findSpot function seems correct to me but I may just be missing something.
As I run the code, the actual reading of the file the first time is fine. Actually inserting anything into the linked list causes the program to hang.
Ok, lets try this again. I'll actually include some code, but please try to use this as a learning point, and not something to just copy paste. I know you said you were copying your teachers algorithm, but what they gave you is probably just that, an algorithm. It is your job to actually implement that in working code, checking for error conditions, etc. Anyway, here we go:
For the function findSpot:
listNode* linkedList::findSpot(int data) {
listNode* spot = listHead; // Initialize spot to start of list
if ( isEmpty() ) // if list is empty, return NULL
return NULL;
// now we know listHead isn't null, so look through the list and
// find the entry that has a value greater than the one provided
// return the list item BEFORE the one with the greater value
while (spot->next != 0 && spot->next->value < data) {
spot = spot->next;
}
// return the one we found; This could be the same as listHead
// (start of list), something in the middle, or the last item on the
// list. If we return from here, it will not be NULL
return spot;
}
Now we can do the insert function:
void linkedList::listInsert(int data, listNode* spot) {
// We need a new item to put on the list, so create it
listNode* newNode = new listNode();
newNode->value = data;
newNode->next = NULL;
// If the list is empty, update the head to point at our new object
if ( isEmpty() ) {
listHead = newNode;
// otherwise point spot to new item, and new item to the one spot
// pointed to
} else {
newNode->next = spot->next;
spot->next = newNode;
}
}
Looking at your print function, that is going to have it's own issues. It looks like you want to print the whole list, but it seems that you are starting to print from "spot". It's all very confused. It also has an issue using newNode->next->value, without checking if newNode->next is NULL. Here's a short example of what I think you are trying to do... note that I don't even need to pass in spot, just the data point added:
void linkedList::printList(int data) {
// if some huckleberry called this before calling insert,
// list will be empty... always a good idea to check
if ( isEmpty())
return;
// first line of output... just print out the data point
// added and start of output text
cout << "Inserted " << data << ": " << "listHead-->(";
// start at start of list
listNode* newNode = listHead;
// loop through until we find the end
while (newNode != NULL) {
cout << newNode->value; // print the value
newNode = newNode->next; // move to the next item on the list
// We moved to the next node; It might be NULL and the loop will end
// if not, we want to print an open bracket since we know another one
// is going to be printed
if ( newNode != NULL )
cout << ")-->(";
}
// last item was just printed, so close off the last bracket
cout << ")" << endl;
}
Hope that is somewhat helpful
Since this looks like a homework assignment, I'm going to give you one fix:
change
myList.findSpot(data);
to
spot = myList.findSpot(data);
If you look closely, spot is used, but never assigned anything.
Well, there are several problems with your program (besides formatting). In the function findSpot(), you have:
listNode* spot;
spot = listHead;
while (spot->next != 0 && spot->next->value < data) {
spot = spot->next;
}
return spot;
The problem here is that the first time you call this, listHead is NULL, so the
while (spot->next
is going to fail, since spot is NULL.
I also notice that nowhere in your code do you call new(). In listInsert, you need to use new() to initialize your newNode variable.
Lastly, find spot has 2 conditions where it can return NULL. If the list is empty, it should return NULL, and you would want to insert at the start of the list. If the new value you are adding is greater than all the others, you will also return NULL and you would have to add to the end of the list.
Since this is a homework assignment, I don't want to write the code for you, but hopefully that helps.

Why can't I check to see if a char stored in a pointer and a char are equivalent?

I have a program that returns an error after I've compiled it in g++, then run it in a Unix environment. The error is that my program says 'Segmentation error' before it can do anything. This error is occurring when I try to set a new nodes data pointer equal to something. I know this because when I test this code in Visual Studio when I try to check if(curr->data == ch); with curr being a pointer, data being the char element that the list is made up of, and ch being the char passed into the bool LinkedList::find(char ch) function, Visual Studio breaks (stops) at that line of code. For context, here's part of my header file (with if(curr->data == ch); towards the end):
#include <ostream>
class LinkedList
{
public:
LinkedList();
~LinkedList();
bool find(char ch);
private:
struct node
{
node();
char data;
node * next;
};
node * head;
node * curr;
node * prev;
};
LinkedList::LinkedList() : head(nullptr), curr(nullptr), prev(nullptr);
LinkedList::node::node() : data('\0'), next(nullptr);
LinkedList::~LinkedList()
{
if (!head) // head is null and so list is empty
{
return; //nothing to delete
}
for(curr = head; head; /* head isn't NULL*/ delete curr /*delete first element*/)
{
curr = head; // set curr to head of list
head = curr->next; // move head over to next element (or make it null)
}
}
bool LinkedList::find(char ch)
{
if(head)
{
for(curr = head; curr && curr->data != ch; curr = curr->next);
if(curr->data == ch)
{
//std::cout << "'" << ch << "' is in the list." << std::endl;
return true;
}
else
{
//std::cout << "'" << ch << "' isn't in the list." << std::endl;
return false;
}
//std::cout << "The list is empty" << std::endl;
return false;
}
else
{
//std::cout << "The list is empty" << std::endl;
return false;
}
}
I wish I could give you guys more context, but I have no idea how to fix this. I thought a char and a pointer to a char were the same type; after all, Visual Studio doesn't break when the for loop checks: curr->data != ch;.
for(curr = head; curr && curr->data != ch; curr = curr->next);
has two exit conditions. One is you find a node with ch. The other is curr == NULL. The next line after the loop exits
if(curr->data == ch)
tests the data member of a curr that might be NULL. Tears in rain... Time to die....
In order to test data, the program first has to find and read it. Finding it is easy, it is at most a few bytes at best after memory address 0, but reading it is problematic. Your program does not own address 0 and cannot read it, triggering an access violation.
By convention, the first chunk of memory is reserved and invalid to make trapping invalid accesses like this one easy. Rather than returning a garbage answer and appearing to function, the program typically crashes. But it might not if convention is not followed.

findNode in binary search tree

Does this look right? I mean I am trying to implement the delete function.
Node* BST::findNode(int tofind) {
Node* node = new Node;
node = root;
while (node != NULL) {
if (node->val == tofind) {
return node;
} else if (tofind < node->val) {
node = node->left;
} else {
node = node->right;
}
}
}
Here is the delete, it's not even close to done but,
void BST::Delete(int todelete) {
// bool found = false;
Node* toDelete = new Node();
toDelete=findNode(todelete);
if(toDelete->val!=NULL) {
cout << toDelete->val << endl;
}
}
This causes a segmentation fault just running that, any ideas?
The main problem with findNode() is that you never return the node that you've found. That's why you're getting the segfault.
Also, in deleteNode() you should check whether findNode() has returned NULL. And of course you also need to code up the rest of the deletion logic.
Finally, the two new Node allocations are unnecessary and are leaking memory.
oh wait it's because in delete I should have done:
if(toDelete!=NULL) {
cout << toDelete->val << endl;
}
before it was
if(toDelete->val!=NULL)