Clear function in a linked list node class C++ - c++

I am having trouble with making sure that I have created a clear function for a linked list Node class. I am using delete this which I know can cause memory issues, but this is the only way that I can think of to assure that all of the objects in the linked list are deleted. The final lines of main() will still print out the value of the head node that should have been deleted. Is this an error in the method or is this due to the pointer still being associated with the object?
Clear method snippet
class Node {
private:
Node *next = NULL;
double value;
public:
void clear();
};
void Node::clear() {
cout << "Clear: " << this << ":" << value << endl;
if(next != NULL){
next -> clear();
}
delete this;
}
Full file
using namespace std;
class Node {
private:
Node *next = NULL;
double value;
public:
Node(double);
Node getNext(){return *next;} //inline
void setNext(Node *newNext); //set *next
double getValue(){return value;} //inline
void setValue(double newValue) {value = newValue;} //inline
void incValue(); //Increment value by the value of next node's value. If next is NULL do nothing.
int sizeOf(); //return size of linked list
double largest(); //return largest value in linked list
double smallest(); //return smallest value in linked list
double getSum(); //Get summation of all
double average(); //return average of all values in the linked list
void print(); //print all values in linked list
void print_reverse(); //print all values in reverse order
void clear(); //remove all nodes from linked list
};
Node::Node(double newValue) {
value = newValue;
}
void Node::setNext(Node *newNext) {
next = newNext;
}
void Node::incValue() {
if(next != NULL) {
double nextVal = next -> getValue();
value += nextVal;
}
}
int Node::sizeOf() {
int count = 0;
if(next != NULL)
count = next -> sizeOf();
count += 1;
return count;
}
double Node::largest() {
double large = value;
if(next != NULL)
large = next -> largest();
if(value > large)
large = value;
return large;
}
double Node::smallest() {
double small = value;
if(next != NULL)
small = next -> smallest();
if(value < small)
small = value;
return small;
}
double Node::average() {
double sum = getSum();
int size = sizeOf();
return sum/size;
}
double Node::getSum() {
double sum = 0;
int count = 0;
if(next != NULL)
sum += next -> getSum();
sum += value;
return sum;
}
void Node::print() {
cout << value << endl;
if(next != NULL)
next -> print();
}
void Node::print_reverse() {
if(next != NULL)
next -> print_reverse();
cout << value << endl;
}
void Node::clear() {
cout << "Clear: " << this << ":" << value << endl;
if(next != NULL){
next -> clear();
}
delete this;
}
int main() {
//set up linked list
Node *head, *temp;
temp = new Node(1);
head = temp;
temp = new Node(2);
temp -> setNext(head);
head = temp;
temp = new Node(3);
temp -> setNext(head);
head = temp;
temp = new Node(4);
temp -> setNext(head);
head = temp;
temp = new Node(5);
temp -> setNext(head);
head = temp;
temp = new Node(6);
temp -> setNext(head);
head = temp;
temp = new Node(7);
temp -> setNext(head);
head = temp;
temp = new Node(8);
temp -> setNext(head);
head = temp;
//print
cout << "Print\n";
head -> print();
//average
cout << "Average\n";
double av = head -> average();
cout << av << endl;
//print reverse
cout << "Print reversed\n";
head -> print_reverse();
//smallest
cout << "Smallest\n";
double small = head -> smallest();
cout << small << endl;
//largest
cout << "Largest\n";
double large = head -> largest();
cout << large << endl;
//size
cout << "Size\n";
int size = head -> sizeOf();
cout << size << endl;
//clear
cout << "Clear\n";
head -> clear();
//clear print
cout << "Clear print\n";
head -> print();
cout << "Clear size\n";
cout << head -> sizeOf() << endl;
//end of program
cout << "End\n";
}

You should rarely (if ever) use delete this;. You also do not clear the next pointer so it becomes a dangling pointer. This memory likely still contains much of the data that was there, so when you traverse the list after "clearing" it, you're seeing the old data -- but note that this is only one of the many things that could happen, because accessing an object that has been destructed is undefined behavior.
Instead, consider doing this:
void Node::clear() {
cout << "Clear: " << this << ":" << value << endl;
if(next != NULL){
next -> clear();
delete next;
next = NULL;
}
}
Even better, do this in Node's destructor, and then you can just delete the target node:
void Node::~Node() {
clear();
}
void Node::clear() {
cout << "Clear: " << this << ":" << value << endl;
if(next != NULL){
delete next;
next = NULL;
}
}
Even better, make next a std::unique_ptr and then you can just reset it on clear(), destruction is automatic, and copying a Node is properly forbidden:
class Node {
private:
std::unique_ptr<Node> next;
double value;
public:
void clear();
};
void Node::clear() {
cout << "Clear: " << this << ":" << value << endl;
next.reset(null);
}
Note that the last node (the head) cannot remove itself. As others have noted, clearing a node can't reasonably do anything except stop pointing at the next node. You need to have a separate class for a list, and clear that.

Everything after your clear() is undefined behavior as your variable head has been deleted in the clear function. You can just consider your print after that garbage.
As for a proper way of freeing everything, you should put your clear function outside of your Node struct:
void clear(Node* head)
{
Node* next = head->next;
delete head;
if (next != nullptr)
clear(next);
}
I think calling delete in the destructor is UB as delete calls the destructor for you. As mentionned in the comments, the delete is not present in the destructor, forget what I said!

Related

Struggling to get this push_back() function to work

I'm working on an assignment for my college class, and I'm struggling to figure out why code for my push_front() function works without fail, but my push_back() always gives:
"Exception thrown: read access violation. st was nullptr."
struct NodeDate
{
int day, month, year; //Structure's data
struct NodeDate* nextNode; //Pointer to point to next node
}*start; //Not sure why we do this, but all examples contain it
class LinkedListDate
{
public:
//Prototype of member functions
NodeDate* create_node(NodeDate);
void push_front();
void pop_front();
void remove_front();
void search();
void display();
LinkedListDate();
void push_back();
void remove_back();
void pop_back();
};
LinkedListDate::LinkedListDate()
{
start = NULL; //Our first created pointer will be set to NULL
}
NodeDate* LinkedListDate::create_node(struct NodeDate newDate) //Function that creates a new node and returns said node
{
struct NodeDate* tempNode, *s;
tempNode = new(struct NodeDate); //Reserving memory for our temp node
if (tempNode == NULL) //If the node is empty
{
std::cout << "Memory not allocated " << std::endl;
return 0;
}
else //Otherwise assign parameter node value to temporary node
{
tempNode->day = newDate.day;
tempNode->month = newDate.month;
tempNode->year = newDate.year;
tempNode->nextNode = NULL; //Remember these are all values contained within the date struct
return tempNode; //Return the node
}
}
void LinkedListDate::push_front() //Function that will insert a node at the start of our linkedlist
{
struct NodeDate* tempNode, *st, newNode;
std::cout << "Enter the day: ";
std::cin >> newNode.day;
std::cout << "Enter the month: ";
std::cin >> newNode.month;
std::cout << "Enter the year: ";
std::cin >> newNode.year;
tempNode = create_node(newNode); //Creating a new node for date with the create_node function
if (start == NULL) //Checks if the starting (Head) node is NULL then first node to insert
{
start = tempNode; //Start node points to our temporary node
start->nextNode = NULL; //Assign null to start->next
}
else //Otherwise nodes are already available in the list
{
st = start; //Assign start to st
start = tempNode; //Start points to temporary node
start->nextNode = st; //Start next point to st
}
std::cout << "Element Inserted at beginning" << std::endl;
}
void LinkedListDate::push_back() //Function to insert a new node at the end of the linkedlist
{
struct NodeDate* tempNode, *st, newNode;
std::cout << "Enter the day: ";
std::cin >> newNode.day;
std::cout << "Enter the month: ";
std::cin >> newNode.month;
std::cout << "Enter the year: ";
std::cin >> newNode.year;
tempNode = create_node(newNode); //Creating a new node for date with the create_node function
st = start;
while (st->nextNode != NULL) //Move til we reach end of the list
{
st = st->nextNode; //Move to the next node
}
tempNode->nextNode = NULL; //Assign null to temporary node next
st->nextNode = tempNode; //st next points to temporary node
std::cout << "Element Inserted at last" << std::endl;
}
Again, push_front() works, but push_back() does not work (The program runs, but after inputting the date for the first node I get the exception.
I've been trying a lot of things, but I can't seem to figure out what exactly I'm doing wrong.
struct NodeDate
{
//...
}*start;
is the same is
struct NodeDate
{
//...
};
NodeDate *start;
push_back will fail when the list is empty (i.e. start is null).
void LinkedListDate::push_back()
{
//...
tempNode = create_node(newNode);
tempNode->nextNode = nullptr;
if(start == nullptr)
{
start = tempNode;
}
else
{
st = start;
while (st->nextNode != nullptr) //Move til we reach end of the list
{
st = st->nextNode; //Move to the next node
}
st->nextNode = tempNode;
}
std::cout << "Element Inserted at last" << std::endl;
}

finding min, max, and average of linked list

I have a program in which I'm supposed to build functions using linked lists to perform a variety of tasks. Currently, I am having an issue finding the min and max value of the linked list. For some reason when both come out to be the highest which digit which is 9, and when I try to find the average of the list, it still comes out as 9.
additionally, I think it's interfering with my pop function which is supposed to delete the last item, but when I try to work it by sections one part wont work until he previous section is running for whatever reason.
here is my header
#include <iostream>
using std::cout;
using std::endl;
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
class LinkedList
{
private:
struct Node
{
int data;
Node *next;
};
int size;
Node *head, *tail;
public:
LinkedList();
~LinkedList();
// misc
void display();
// sorting and searching
// reverse --> sorting in descending
int linearSearch(int key);
void sort();
void reverse();
// various math
int min();
int max();
int mean();
// adding
void append(int num);
void insert(int num, int pos);
// removing
void pop();
void remove(int pos);
};
#endif // LINKEDLIST_H
the header's source file
#include "linkedlist.h"
LinkedList::LinkedList()
{
head = nullptr;
tail = nullptr;
size = 0;
}
LinkedList::~LinkedList()
{
if(head != nullptr)
{
Node *temp;
while(head != nullptr)
{
temp = head->next;
// deletes head
delete head;
// goes to next element
head = temp;
}
}
}
void LinkedList::display()
{
Node *temp = head;
for(int i = 0; i < size; i++)
{
cout << temp->data << "\t";
temp = temp->next;
}
cout << endl;
}
void LinkedList::append(int num)
{
// list is empty
if(head == nullptr)
{
head = new Node;
head->data = num;
head->next = nullptr;
// sets tail to head
tail = head;
}
else
{
// creates new node
Node *temp = new Node;
// sets new node data
temp->data = num;
temp->next = nullptr;
// sets previous tail link to new node
tail->next = temp;
// sets this node to new tail
tail = temp;
}
// increments size
size++;
}
void LinkedList::pop()
{
if(size > 1)
{
Node *temp = head;
// loops to node before tail
while(temp->next->next != nullptr)
{
temp = temp->next;
}
// deletes tail
delete tail;
// sets new tail
tail = temp;
tail->next = nullptr;
}
// if there's only one item
else if(size == 1)
{
Node *temp = tail;
// head and tail are now null
head = nullptr;
tail = nullptr;
// deletes node
delete temp;
}
size--;
}
void LinkedList::insert(int num, int pos)
{
if(pos ==0)
{
Node *temp=new Node;
temp->data=num;
temp->next=head;
head=temp;
}
if(pos>1)
{
Node *pre=new Node;
Node *cur=new Node;
Node *temp=new Node;
cur=head;
for(int i=1;i<pos+1;i++)
{
pre=cur;
cur=cur->next;
}
temp->data=num;
pre->next=temp;
temp->next=cur;
}
size++;
}
int LinkedList::linearSearch(int key)
{
Node *temp = head;
for(int i = 0; i < size; i++)
{
if(temp->data == key)
{
return i;
}
temp = temp->next;
}
return -1;
}
int LinkedList::max()
{
int max = INT_MIN;
for(int i = 0; i < size; i++)
{
while (head != NULL)
{
if (head->data < max)
max = head->data;
head = head->next;
}
}
}
int LinkedList::min()
{
int min = INT_MAX;
for(int i = 0; i < size; i++)
{
while (head != NULL)
{
if (head->data < min)
min = head->data;
head = head->next;
}
}
}
void LinkedList::reverse()
{
Node* temp = head;
// Traverse the List
while (temp) {
Node* min = temp;
Node* r = temp->next;
// Traverse the unsorted sublist
while (r)
{
if (min->data < r->data)
min = r;
r = r->next;
}
// Swap Data
int x = temp->data;
temp->data = min->data;
min->data = x;
temp = temp->next;
}
}
void LinkedList::remove(int pos)
{
Node *temp = head;
if(pos ==0)
{
head = temp->next;
free(temp);
}
if(pos>1)
{
for(int i=0; temp!=NULL && i<pos-1;i++)
{
temp=temp->next;
}
temp->next = temp->next->next;
free(temp->next);
temp->next = temp->next;
}
size--;
}
int LinkedList::mean()
{
int sum = 0;
float avg = 0.0;
Node *temp = head;
while (head != NULL)
{
sum += temp->data;
temp = temp->next;
}
// calculate average
avg = (double)sum / size;
}
void LinkedList::sort()
{
Node* temp = head;
// Traverse the List
while (temp) {
Node* min = temp;
Node* r = temp->next;
// Traverse the unsorted sublist
while (r) {
if (min->data > r->data)
min = r;
r = r->next;
}
// Swap Data
int x = temp->data;
temp->data = min->data;
min->data = x;
temp = temp->next;
}
}
And the main
#include <iostream>
#include "linkedlist.h"
using namespace std;
int main()
{
LinkedList nums;
// adding through append
nums.append(8);
nums.append(6);
nums.append(7);
nums.append(8);
nums.append(0);
nums.append(9);
// displays list
cout << "List after append: " << endl;
nums.display();
cout << endl;
// adding through insert
nums.insert(1, 0);
nums.insert(5, 4);
nums.insert(3, 8);
// displays list
cout << "List after inserting: " << endl;
nums.display();
cout << endl;
// testing searching
cout << "Testing linear search:" << endl;
int pres = nums.linearSearch(7);
if(pres < 0)
{
cout << "7 is not present in the list." << endl;
}
else
{
cout << "7 can be found at location " << pres << endl;
}
pres = nums.linearSearch(5);
if(pres < 0)
{
cout << "5 is not present in the list." << endl;
}
else
{
cout << "5 can be found at location " << pres << endl;
}
cout << endl;
// does math
cout << "Minimum, maximum, and average before removing any items: " << endl;
cout << "Min: " << nums.min() << endl;
cout << "Max: " << nums.max() << endl;
cout << "Mean: " << nums.mean() << endl << endl;
// displays items reversed
cout << "Items reversed: " << endl;
nums.reverse();
nums.display();
cout << endl;
// removing through pop
nums.pop();
nums.pop();
// displays list
cout << "List after popping: " << endl;
nums.display();
cout << endl;
// removing through remove
nums.remove(0);
nums.remove(2);
nums.remove(4);
// displays list
cout << "List after removing: " << endl;
nums.display();
cout << endl;
// displays items sorted
cout << "Items sorted: " << endl;
nums.sort();
nums.display();
cout << endl;
// does math
cout << "Minimum, maximum, and average after removing items: " << endl;
cout << "Min: " << nums.min() << endl;
cout << "Max: " << nums.max() << endl;
cout << "Mean: " << nums.mean() << endl << endl;
// testing searching
cout << "Testing linear search:" << endl;
pres = nums.linearSearch(7);
if(pres < 0)
{
cout << "7 is not present in the list." << endl;
}
else
{
cout << "7 can be found at location " << pres << endl;
}
pres = nums.linearSearch(5);
if(pres < 0)
{
cout << "5 is not present in the list." << endl;
}
else
{
cout << "5 can be found at location " << pres << endl;
}
return 0;
}
the only parts I'm really struggling with is the max, min, and mean along with getting my pop function to actually initiate. I know that the pop function is written correctly but ever since I made the max and min it wont work now.
There are several bugs that I have found in the code, and I have several remarks about it:
You should use spaces, and more consistently. There are places without enough spacing, and places with too many blank lines!
If you have two functions such as insert and append or pop and remove, they should use each other, meaning, append is just insert(0) (notice how I changed it in the code).
You are using double loops where it doesn't make sense (it isn't an error, but it is a bug!).
In the function max, you were doing the wrong comparison, asking if max is bigger than the current value...
You never return a value from min and max, which should at least create a warning in the compilation process!
You were creating empty nodes, and then you just put different values in their pointers, meaning that this new memory was still allocated (since there was no delete), but there was no way to access these anymore (this is a memory leak).
The biggest bug of all - When you loop in the min and max functions, you change the head of the list, which is a major bug (and that is why you got bugs after using this function). The solution is a simple but important lesson in C++ - Const Correctness.
What is const correctness, and why is it important?
When you have a function, that does not change the state of your object, it should be declared const. This is our way to tell the compiler (and other programmers) that it mustn't change the state of our object. For example, min, max and average are classic const functions - they simply make a calculation that does not change the list, and return. If you had written const in the declaration of those, the compilation would have failed, since you actually changed the list (changing the head), although you shouldn't!
Moreover, when receiving objects into a function, whenever possible, you should make the const T& where T is a type. They will enforce that you are using only const functions of this type.
Also, I suggest compiling (at least on g++) with the flags -Wpedantic -Werror'. The first adds some warnings about ISO C++ standards and the second makes all warnings into errors (and thus, yourmin,maxandmean` should not compile, since they don't return a value).
Here is the code:
class LinkedList
{
private:
struct Node
{
int data;
Node *next;
Node(int data_, Node* next_ = nullptr) :
data(data_),
next(next_)
{
}
};
int size;
Node *head, *tail;
public:
LinkedList();
~LinkedList();
void clear();
// various math
int min() const;
int max() const;
int average() const;
// adding
void append(int data);
void insert(int data, int pos);
// removing
void pop();
void remove(int pos);
};
LinkedList::LinkedList()
{
head = nullptr;
tail = nullptr;
size = 0;
}
LinkedList::~LinkedList()
{
clear();
}
void LinkedList::clear()
{
if (head != nullptr)
{
Node *temp;
while(head != nullptr)
{
temp = head->next;
delete head;
head = temp;
}
}
head = nullptr;
tail = nullptr;
size = 0;
}
void LinkedList::display()
{
Node *temp = head;
for(int i = 0; i < size; i++)
{
std::cout << temp->data << "\t";
temp = temp->next;
}
std::cout << std::endl;
}
void LinkedList::insert(int data, int pos)
{
if (pos == 0)
{
Node* prev_head = head;
head = new Node(data, prev_head);
if (size == 0)
{
tail = head;
}
}
else
{
Node *pre=nullptr;
Node *cur = head;
for(int i = 0 ; i < pos + 1; ++i)
{
pre = cur;
cur = cur->next;
}
Node *temp = new Node(data, cur);
pre->next = temp;
}
++size;
}
void LinkedList::append(int data)
{
insert(data, 0);
}
void LinkedList::pop()
{
if (size == 1)
{
Node *temp = tail;
head = nullptr;
tail = nullptr;
delete temp;
}
else
{
Node *temp = head;
while(temp->next != tail)
{
temp = temp->next;
}
Node* node_to_pop = tail;
tail = temp;
tail->next = nullptr;
delete node_to_pop;
}
--size;
}
int LinkedList::max() const
{
int max = INT_MIN;
for (Node* temp = head; temp != nullptr; temp = temp->next)
{
if (temp->data > max)
{
max = temp->data;
}
}
return max;
}
int LinkedList::min() const
{
int min = INT_MAX;
for(Node* temp = head; temp != nullptr; temp = temp->next)
{
if (head->data < min)
{
min = temp->data;
}
}
return min;
}
int LinkedList::average() const
{
int sum = 0;
for(Node* temp = head; temp != nullptr; temp = temp->next)
{
sum += temp->data;
temp = temp->next;
}
return (double)sum / size;
}

Segmentation Fault (core dumped) when trying to run Queue program - C++

I keep getting a Segmentation fault (core dumped) error every time I try to run my code with g++ on Linux. It compiles fine, but then that happens ... All the functions (remove, add and print) seem to have the same problem, I can't seem to figure out what's wrong... Please heeeelppp.
#include <iostream>
#include <string>
using namespace std;
//Create a node struct
struct Node {
int data;
Node *next;
Node *prev;
};
class Queue {
private:
Node *head;
Node *tail;
int size;
public:
Queue();
~Queue();
void add(int d);
int remove();
bool isEmpty();
void printQueue(bool o);
};
//set to NULL
Queue::Queue() {
head = tail = NULL;
size = 0;
}
//destructor
//call remove until empty
Queue::~Queue() {
while (!isEmpty())
remove();
}
//adds a node with the given data at the back of the queue
void Queue::add(int d) {
Node *temp = new Node();
temp->data = d;
temp->next = NULL;
if (isEmpty()) {
//add to head
head = temp;
} else {
//append
tail->next = temp;
tail = temp;
cout << "Added: " << tail->data << endl;
}
size++;
}
//removes the node at the head of the queue and returns its data
int Queue::remove() {
if (isEmpty()) {
cout << "The queue is empty." << endl;
} else {
Node *temp = new Node;
temp = head;
int value = head->data;
//moves pointer to next node
head = head->next;
cout << "Removed: " << head->data << endl;
size--;
delete temp;
return value;
}
}
//determines if the queue is empty
bool Queue::isEmpty() {
return (size == 0);
}
//prints the contents of the queue from front to back, or front
//to back, depending on the value of the parameter
void Queue::printQueue(bool o) {
if (isEmpty()) {
cout << "The queue is empty." << endl;
} else {
Node *p = new Node;
if (o == true) {
cout << "Printing in front to back:" << endl;
//print front to back
while(p != NULL) {
p = head;
cout << p->data << " ";
p = p->next;
}
} else if (o == false) {
cout << "Printing in back to front:" << endl;
//print back to front
while (p != NULL) {
p = tail;
cout << p->data << " ";
p = p->prev;
}
}
}
}
int main() {
Queue q;
q.add(8);
return 0;
}
EDIT: I've made some changes to the code... But I'm still getting the same error. I assume I'm not updating the head and the tail and/or the next and prev nodes correctly... I don't know why it's wrong or what I'm missing, though.
#include <iostream>
#include <string>
using namespace std;
struct Node {
int data;
Node *next;
Node *prev;
};
class Queue {
private:
Node *head;
Node *tail;
int size;
public:
Queue();
~Queue();
void add(int d);
int remove();
bool isEmpty();
void printQueue(bool o);
};
Queue::Queue() {
head = tail = NULL;
size = 0;
}
Queue::~Queue() {
while (!isEmpty())
remove();
}
void Queue::add(int d) {
Node *temp = new Node;
temp->data = d;
temp->next = NULL;
temp->prev = tail;
if (isEmpty()) {
//add to head
head = temp;
} else {
//append
tail->next = temp;
tail = temp;
cout << "Added: " << tail->data << endl;
}
size++;
}
int Queue::remove() {
if (isEmpty()) {
cout << "The queue is empty." << endl;
return 0;
} else {
Node *temp = head;
int value = head->data;
cout << "Removed: " << head->data << endl;
//moves pointer to next node
head = head->next;
head->prev = NULL;
size--;
delete temp;
return value;
}
}
bool Queue::isEmpty() {
return (size == 0);
}
void Queue::printQueue(bool o) {
if (isEmpty()) {
cout << "The queue is empty." << endl;
} else {
Node *p;
if (o == true) {
p = head;
cout << "Printing in front to back:" << endl;
//print front to back
while(p != NULL) {
cout << p->data << " ";
p = p->next;
}
} else if (o == false) {
p = tail;
cout << "Printing in back to front:" << endl;
//print back to front
while (p != NULL) {
cout << p->data << " ";
p = p->prev;
}
}
}
}
int main() {
Queue q;
q.add(9);
q.add(10);
q.add(11);
q.add(12);
q.add(13);
q.add(14);
q.add(15);
q.add(16);
q.remove();
q.remove();
q.printQueue(true);
q.printQueue(false);
return 0;
}
Lots of problems:
You have a double-linked Node but never update its prev member in the add/remove methods.
You are keeping track of both the Queue head/tail but don't properly update them when you add/remove nodes.
Both your forward and reverse loops in printQueue() are wrong and result in an infinite loop for any queue with 2 or more elements. Queue output should be just something like:
Node *p = head;
while (p != NULL)
{
cout << p->data << " ";
p = p->next;
}
Possible null pointer deference in remove() at cout << "Removed: " << head->data << endl; since you've already moved the head pointer by this time. Move the head after the cout.
Memory leak in Queue::remove() at Node *temp = new Node;. Just do Node* temp = head;.
Memory leak in Queue::printQueue() at Node *p = new Node;. You don't need to allocate a node here.
No return value in remove() for an empty queue.
Edit
Don't forget to initialize the tail when adding a node to an empty list:
if (isEmpty()) {
head = temp;
tail = temp;
}
To remove a node from the head of a non-empty list it should be something like:
Node *temp = head;
head = head->next;
if (head) head->prev = NULL;
size--;
delete temp;
if (isEmpty()) tail = NULL;

Linked Lists Program Crashes After Receiving Values from User

My program is meant to run several functions, insertnode takes values from the user and creates a list of them using nodes and sorts them in order from least to greatest, printlist prints the values separated by spaces, mergelist merges the two lists in order, and reverselist reverses the list. The command prompt accepts values but once the stopping condition (0) is entered for the second list it crashes. Visual Studio shows no errors. I figure something is either wrong with the functions or the pointers. Someone spoke to me of a memory leak but Im unsure as to how to fix that.
#include <iostream>
#include <stack>
using namespace std;
class node {
private:
double num;
node *link;
public:
node() { }
node(double m, node *n) { num = m; link = n; }
node* getlink() { return link; }
double getdata() { return num; }
void setdata(double m) { num = m; }
void setlink(node* n) { link = n; }
};
typedef node* nodeptr;
void insertnode(nodeptr& head, double m);
void printlist(nodeptr head);
nodeptr mergelists(nodeptr& head1, nodeptr& head2);
void reverselist(nodeptr& head);
int main()
{
double input;a
nodeptr head1 = NULL; // Pointer to the head of List #1
nodeptr head2 = NULL; // Pointer to the head of List #2
nodeptr temp;
// Part 1 - Create two sorted lists
cout << "-------------------------------------" << endl;
cout << "CREATE LIST #1: " << endl;
cout << "-------------------------------------" << endl;
do {
cout << "Enter value (0 to quit): ";
cin >> input;
insertnode(head1, input);
} while (input != 0);
cout << "-------------------------------------" << endl;
cout << "CREATE LIST #2: " << endl;
cout << "-------------------------------------" << endl;
do {
cout << "Enter value (0 to quit): ";
cin >> input;
insertnode(head2, input);
} while (input != 0);
// Part 1 - Print the lists to make sure that they are correct.
printlist(head1);
printlist(head2);
// Part 2 - Merge the two lists and display the new merged list
temp = mergelists(head1, head2);
printlist(temp);
// Part 3 - Reverse the merged list and then display it
reverselist(temp);
printlist(temp);
return 0;
}
void insertnode(nodeptr& head, double m){
nodeptr p = head;
nodeptr k = p;
if (!p){
nodeptr n = new node(m, NULL);
}
else {
while (m >= p->getdata()){
k = p;
p = p->getlink();
}
nodeptr n = new node;
n->setdata(m);
k->setlink(n);
if (p){
n->setlink(p);
}
}
}
void printlist(nodeptr head){
nodeptr p = head;
while (p){
double m = p->getdata();
cout << m << " ";
p = p->getlink();
}
cout << endl;
}
nodeptr mergelists(nodeptr &head1, nodeptr &head2){
nodeptr result = 0, last = 0;;
if (head1->getdata() <= head2->getdata()){
result = head1;
head1 = head1->getlink();
}
else {
result = head2;
head2 = head2->getlink();
}
last = result;
while (head1 && head2){
if (head1->getdata() <= head2->getdata()){
last->setlink(head1);
last = head1;
head1 = head1->getlink();
}
else{
last->setlink(head2);
last = head2;
head2 = head2->getlink();
}
}
if (head1)
last->setlink(head1);
else if (head2)
last->setlink(head2);
last = 0;
head1 = 0;
head2 = 0;
return result;
}
void reverselist(nodeptr& head){
stack<double> holder;
nodeptr p = head;
while (p){
holder.push(p->getdata());
p = p->getlink();
}
p = head;
while (p){
p->setdata(holder.top());
holder.pop();
p = p->getlink();
}
}
There are a few issues with this method:
void insertnode(nodeptr& head, double m){
nodeptr p = head;
nodeptr k = p;
if (!p)
{
head = new node(m, NULL); // Update head
}
else
{
while (p && m >= p->getdata()) // Check for p!=NULL
{
k = p;
p = p->getlink();
}
nodeptr n = new node;
n->setdata(m);
k->setlink(n);
if (p)
{
n->setlink(p);
}
}
}
The simplified version:
void insertnode(nodeptr& head, double m)
{
nodeptr p = head;
nodeptr k = nullptr;
while (p && m >= p->getdata())
{
k = p;
p = p->getlink();
}
if (!k)
{
head = new node(m, p);
}
else
{
k->setlink(new node(m, p));
}
}
There are two issue is that you are not updating the head node when you insert into the linked list. The previous answer by #uncletall outlined this.
The second issue is very simple -- you failed to initialize the link to NULL when you default construct a node. Make the following change in your node class:
class node {
private:
double num;
node *link;
public:
node() : link(0) { } // here is the change here
//...
//... the rest of your code
};
When you default construct a node the link is now initialized. Without this change, your link node was a garbage value, thus you were not traversing the links properly when you were inserting more items in the list.
The change is similar to (but not exactly) the same as this:
class node {
private:
double num;
node *link;
public:
node() { link = 0; } // here is the change here
//...
//... the rest of your code
};
The difference is that link is assigned in the body of the constructor here, while the first example initializes the link to 0 before the constructor has started. Both wind up doing the same thing.

C++ “Access violation reading location” Error

I just implemented a Destructor and I am getting an “Access violation reading location”. I beleive the problem is in my while loop but just can't figure it out.
Below is my code. If needing to reference any other part of my List Class please let me know.
Thanks!
List::List():first(NULL), last(NULL), nodeListTotal(0)
{
}
List::~List()
{
Node* currentNode = first;
while( currentNode != 0 )
{
Node* temp = currentNode->getNext();
delete currentNode;
currentNode = temp;
}
first = 0;
}
Here is my entire List class. I have made the changes recommended, removed the first = 0; and change 0 to nullptr
#include "Node.h"
#include <string>
using namespace std;
class List
{
private:
int nodeListTotal;
Node* first;
Node* last;
public:
//Constructor
List();
//Destructor
~List();
//Copy-Constructor
//List(const List& theList);
//////Overloading Assignment Operator
//List& operator=(const List& L);
void push_back(Node*);
void push_front(Node*);
Node* pop_back();
Node* pop_front();
Node* getFirst() const;
Node* getLast() const;
int getListLength() const;
};
List::List():first(NULL), last(NULL), nodeListTotal(0)
{
}
// Destructor
List::~List()
{
Node* currentNode = first;
while( currentNode != nullptr )
{
Node* temp = currentNode->getNext();
delete currentNode;
currentNode = temp;
}
}
// Copy-Constructor
//List::List(const List& theList)
//{
// Node * tempPtr = new Node;
// tempPtr = theList.first;
// List(tempPtr);
//
// while (tempPtr != NULL)
// {
// Node * copyNode = new Node;
// copyNode = tempPtr;
// tempPtr = tempPtr->getNext();
// nodeListTotal++;
// }
//}
// Overloading Assignemnt Operator
//List& List::operator=(const List& L)
//{
// List* overList;
// Node* temp = L.first;
//
// while( temp != NULL ) {
// overList->getLast();
// temp = temp -> getNext();
//
// return *this;
//}
void List::push_back(Node* newNode)
{
Node* temp = last;
if (temp)
temp->setNext(newNode);
else
first = newNode;
last = newNode;
nodeListTotal++;
}
void List::push_front(Node* newNode)
{
Node* temp = getFirst();
newNode->setNext(temp);
first = newNode;
nodeListTotal++;
if (!temp)
last = first;
}
Node* List::pop_back()
{
Node* old = last;
if (first == last)
{
first = 0;
last = 0;
}
else
{
Node* temp = first;
for (int i = 0; i < (nodeListTotal - 1); i++)
{
temp = temp->getNext();
}
temp->setNext(NULL);
last = temp;
}
nodeListTotal--;
return old;
}
Node* List::pop_front()
{
Node* temp = getFirst();
first = temp->getNext();
if (!first)
last = 0;
nodeListTotal--;
return temp;
}
Node* List::getFirst() const
{
return first;
}
Node* List::getLast() const
{
return last;
}
int List::getListLength() const
{
return nodeListTotal;
}
Node.h
#include <string>
using namespace std;
class Node
{
private:
string dataItem;
string dataUnit;
int unitTotal;
Node* next;
public:
//Constructor
Node();
Node(int, string, string);
string getDescription( )const;
void setDescription(string);
string getQuantityName()const;
void setQuantityName(string);
int getQuantityNumber()const;
void setQuantityNumber(int);
Node* getNext( )const;
void setNext(Node*);
};
Node::Node(void):dataItem("None"), dataUnit("None"), unitTotal(0), next(NULL)
{
}
Node::Node(int q, string i, string u):dataItem(i), dataUnit(u), unitTotal(q), next(NULL)
{
}
string Node::getDescription( ) const
{
return dataItem;
}
void Node::setDescription(string iSetter)
{
dataItem = iSetter;
}
string Node::getQuantityName() const
{
return dataUnit;
}
void Node::setQuantityName(string uSetter)
{
dataUnit = uSetter;
}
int Node::getQuantityNumber() const
{
return unitTotal;
}
void Node::setQuantityNumber(int tSetter)
{
unitTotal = tSetter;
}
Node* Node::getNext() const
{
return next;
}
void Node::setNext(Node* nSetter)
{
next = nSetter;
}
Driver.cpp
int main( )
{
//===============================================
// PART ONE
//===============================================
cout << "\nPart I: push_front and pop_front\n";
cout << "\n----------------------------------\n";
List groceries;
// test push_back function
groceries.push_front(new Node(1, "gallon", "milk") );
groceries.push_front(new Node(2, "loaves", "bread") );
groceries.push_front(new Node(1, "dozen", "eggs" ) );
groceries.push_front(new Node(1, "package", "bacon") );
cout << "\nThe original nodes in the List:\n";
printList(groceries);
cout << "\n----------------------------------\n";
// test push_front function
cout << "\nAdding to the front of the List:\n";
cout << "\n----------------------------------\n";
groceries.push_front(new Node(2, "lbs", "hamburger") );
groceries.push_front(new Node(1, "dozen", "hamburger buns") );
printList(groceries);
cout << "\n----------------------------------\n";
// test pop-front
cout << "\nRemoving the first node from the list.\n";
cout << "\n----------------------------------\n";
Node* item = groceries.pop_front( );
cout << "\nPopped " << item->getDescription( ) << " from the list.\n\n";
printList(groceries);
if (item != NULL)
delete item;
// ===============================================
// PART TWO: Uncomment this block to test part two
// ===============================================
cout << "\n----------------------------------\n";
cout << "\nPart Two: Push_back and pop_back";
// test push_back
groceries.push_back(new Node(2, "cans", "orange juice") );
groceries.push_back(new Node(1, "lb", "swiss cheese") );
cout << "\nAdding two nodes at the end\n";
cout << "\n----------------------------------\n";
printList(groceries);
// test pop-back
cout << "\n----------------------------------\n";
cout << "\nRemove last node from the list\n";
cout << "\n----------------------------------\n";
item = groceries.pop_back( );
cout << "\nPopped " << item->getDescription( ) << " from the list.\n\n";
printList(groceries);
if (item != NULL)
delete item;
// ============================================
// end of part two
// ============================================
// ================================================
// PART THREE: uncomment this block to test part three
// ================================================
/*
// create a second list to test assignment
cout << "\n\n--------------extra credit------------------\n";
cout << "\n\n overloaded assignment operator\n";
cout << "The hardware list ...\n";
cout << "\n-------------------------------------------\n";
List hardware;
hardware.push_back(new Node(2, "lbs", "nails") );
hardware.push_back( new Node(3, "gals", "white paint") );
hardware.push_back(new Node(1, "piece", "plywood") );
printList(hardware);
hardware = groceries;
cout << "\n-------------------------------------------\n";
cout << "\nafter assignment";
cout << "\n-------------------------------------------\n";
printList(hardware);
cout << "\n-------------------------------------------\n";
cout << "\nTest the copy constructor\n";
cout << "\n-------------------------------------------\n";
printFirstNode(hardware);
// ==============================================
// end of part 3
// ==============================================
*/
cout << "\n-------------------------------------------\n";
cout << "\nEnd of Test";
cout << "\n-------------------------------------------\n";
system("PAUSE");
return 0;
}
Looks like pop back does not remove last node from the list, but returns it. Then the node is deleted and in list's destructor you try to delete it second time.
The push_back() method could be your culprit, coupled with the failure to initialize Node:next to zero. If only one node is added to the list using push_back then the value of that node's next member would be unknown and in the destructor an attempt to delete a second node referring to a random memory location would cause the access error.
Either ensure that the nodes values are initialized, or ensure that node.next is set explicitly in push_back() if it's the first node added to the list.
Something to note on pop_back() and pop_front().
Calling pop_back() on an empty list would still decrement nodeListTotal.
Calling pop_front() on an empty list would actually cause an access violation trying to access address 0.
The below deletion of node is causing the problem while cleaning up in the destructor.
if (item != NULL)
delete item;
When you do the pop_back you're deleting that(last) node in main(), but what happens to the node which is before that? it was pointing to the one which you deleted and is not set to NULL.
So, in the destructor when you start deleting the nodes you're checking for the NULL value of the nodes. All goes fine but now for the last one next wasn't set to NULL, instead it is still pointing to the one which you just deleted. Hence, tries to free the node which is already freed.
Then your code crashes.
Set the previous node's next to NULL whenever you're freeing the successive node.