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.
Related
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!
I am trying to delete the node of specific data. For this, I am using deleteNode function but not able to delete. Please see the code:
#include<iostream>
using namespace std;
class node
{
public:
int data;
node* next;
///constructor for initializing the data-
node(int d)
{
data=d;
next=NULL;
}
};
void addAtEnd(node* &head,int data)
{
if(head==NULL)
{
head=new node(data);
return ;
}
node* temp=head;
while(temp->next!=NULL)
{
temp=temp->next;
}
node* n =new node(data);
n->data=data;
temp->next=n;
n->next=NULL;
return;
}
AddAtTail(node* head,int d)
{
node* ptr=head;
while(ptr->next!=NULL)
{
ptr=ptr->next;
}
node *n=new node(d);
ptr->next=n;
n->next=NULL;
}
AddAtPosition(node* head,int p,int d)
{
///2 3 4 6 7 8 -1
///4th Position-
node*ptr=head;
int jump=1;
while(jump<=p-1)
{
jump++;
ptr=ptr->next;
}
node*n=new node(d);
n->next=ptr->next;
ptr->next=n;
}
/// Delete First node
void deleteFirst(node *&head)
{
head=head->next;
}
///Delete last node;
void deleteLast(node* head)
{
node* ptr=head;
while(ptr->next->next!=NULL)
{
ptr=ptr->next;
}
ptr->next=NULL;
}
**///Delete Specific Node :-**
Here the function starts for deleting the node. I am trying to delete the node that has data 3 I am taking data as input from main function.
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{
ptr=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
void takeInput(node*& head)
{
int d;
cin>>d;
while(d!=-1)
{
addAtEnd(head,d);
cin>>d;
}
}
void print(node* head)
{
while(head!=NULL)
{
cout<<head->data<<"=>";
head=head->next;
}
}
AddAtFront(node* &head,int d)
{
///create new node;
node*n=new node(d);
n->next=head;
head=n;
}
int main()
{
node* head(NULL);
takeInput(head);
print(head);
cout<<endl<<endl<<endl<<"---------- Here The Insertion Process starts at different Positions -----------"<<endl;
cout<<endl<<"Adding at Tail"<<endl;
AddAtTail(head,9);
print(head);
cout<<endl<<"Adding at Position p"<<endl;
int p,d;
cout<<"Enter Position and data :"<<endl;
cin>>p>>d;
AddAtPosition(head,p,d);
print(head);
cout<<endl<<"Adding at Front"<<endl;
cout<<"Enter data to add at front : "<<endl;
cin>>d;
AddAtFront(head,d);
print(head);
cout<<endl<<endl<<endl;
cout<<endl<<"-------------------- NOW LETS PERFORM DELETION ------------------"<<endl;
cout<<endl<<"Deleting first node :"<<endl;
deleteFirst(head);
print(head);
cout<<endl;
cout<<endl<<"Deleting Last node :"<<endl;
deleteLast(head);
print(head);
cout<<endl;
cout<<"deleting specific node"<<endl;
cout<<"Enter data to delete"<<endl;
cin>>d;
deleteData(head,d);
print(head);
cout<<endl;
return 0;
}
Please see DeleteNode function in which I am trying to delete the node.
Why node is not deleting? Here is the function:
**///Delete Specific Node i.e- data :-**
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{
ptr=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
But Node is not deleting.
Your delete... functions are not actually deleting anything. You are just manipulating pointers but are leaking the actual node objects. And you are not taking into account the possibility that the node being deleted is the head node, which requires updating the head to point at the next node.
Also, your functions will crash on an empty list, and deleteLast will crash on a list with fewer than 2 nodes.
And deleteData is not enumerating nodes correctly.
Try something more like this instead:
#include <iostream>
using namespace std;
class node {
public:
int data;
node* next;
///constructor for initializing the data-
node(int d) {
data=d;
next=NULL;
}
};
node* findLast(node *head, node **before) {
if (before) *before = NULL;
node *last = head;
if (last) {
while (last->next) {
if (before) *before = last;
last = last->next;
}
}
return last;
}
node* addAtFront(node* &head, int data) {
node* n = new node(data);
n->next = head;
head = n;
return n;
}
node* addAtEnd(node* &head, int data) {
node *last = findLast(head, NULL);
node* n = new node(data);
if (last) {
last->next = n;
} else {
head = n;
}
return n;
}
node* addAtPosition(node* &head, int p, int d) {
if ((!head) || (p <= 0)) {
return addAtFront(head, d);
}
node *ptr = head;
node *temp;
do {
temp = ptr;
ptr = ptr->next;
}
while ((ptr) && (--p > 0));
node *n = new node(d);
n->next = temp->next;
temp->next = n;
return n;
}
/// Delete First node
void deleteFirst(node* &head) {
if (head) {
node *ptr = head;
head = head->next;
delete ptr;
}
}
///Delete last node
void deleteLast(node* &head) {
node *beforeLast;
node *last = findLast(head, &beforeLast);
if (last) {
if (beforeLast) {
beforeLast->next = NULL;
}
if (head == last) {
head = NULL;
}
delete last;
}
}
///Delete Specific Node
void deleteData(node* &head, int d) {
node *before = NULL;
node *ptr = head;
while (ptr) {
if (ptr->data == d) {
if (before) {
before->next = ptr->next;
}
if (head == ptr) {
head = head->next;
}
delete ptr;
return;
}
before = ptr;
ptr = ptr->next;
}
}
void takeInput(node* &head) {
int d;
if (!((cin >> d) && (d != -1))) return;
node *last = findLast(head, NULL);
node *n = new node(d);
if (last) {
last->next = n;
} else {
head = n;
}
last = n;
while ((cin >> d) && (d != -1)) {
n = new node(d);
last->next = n;
last = n;
}
}
void print(node* head) {
while (head) {
cout << head->data << "=>";
head = head->next;
}
}
int main() {
node* head = NULL;
takeInput(head);
print(head);
cout << endl;
cout << endl << endl;
cout << "---------- Here The Insertion Process starts at different Positions -----------" << endl << endl;
cout << "Adding at End" << endl;
addToEnd(head, 9);
print(head);
cout << endl;
cout << "Adding at Position p" << endl;
int p, d;
cout << "Enter Position and data :" << endl;
if (cin >> p >> d) {
addAtPosition(head, p, d);
print(head);
cout << endl;
}
cout << "Adding at Front" << endl;
cout << "Enter data to add at front : " << endl;
if (cin >> d) {
addAtFront(head, d);
print(head);
cout << endl;
}
cout << endl << endl << endl;
cout << "-------------------- NOW LETS PERFORM DELETION ------------------" << endl << endl;
cout << "Deleting first node :" << endl;
deleteFirst(head);
print(head);
cout << endl;
cout << endl << "Deleting Last node :" << endl;
deleteLast(head);
print(head);
cout << endl;
cout << "deleting specific node" << endl;
cout << "Enter data to delete" << endl;
if (cin >> d) {
deleteData(head, d);
print(head);
cout << endl;
}
cout << "deleting remaining nodes" << endl;
node *ptr = head;
while (ptr) {
node *temp = ptr;
ptr = ptr->next;
delete temp;
}
return 0;
}
That being said, you really should be using std::list (double linked) or std::forward_list (single linked) instead. Let the STL do the hard work for you.
In your delete node function, you are changing the value of the local variable, ptr. But you are not changing the "next" pointer of the node pointing to the one you want to delete. So it stays in the list. The value of your local pointer becomes irrelevant as soon as you leave the function.
This line is the problem: ptr=ptr->next->next;
You need to change its "next" value. Something like (I haven't compiled and tested this; it is a suggestion to point you in the right direction.)
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{ /* pointer->next points to the node you want to delete.
point it instead to the one beyond */
ptr->next=ptr->next->next;
return;
}
ptr=ptr->next;
}
}
You will then have a memory leak, as you are not freeing the memory to the node you are deleting.
You'd need something like:
void deleteData(node* head,int d)
{
node*ptr=head;
while(ptr->next!=NULL)
{
if(ptr->next->data==d)
{ /* pointer->next points to the node you want to delete.
point it instead to the one beyond,
freeing the memory of the deleted node */
node * pNodeToDelete = ptr->next;
ptr->next=ptr->next->next;
free(pNodeToDelete)
return;
}
ptr=ptr->next;
}
}
I'm going to be upfront and apologize for asking for your help. I really don't deserve it in any way. I also apologize for what I know is my sloppy code. I don't want to be that guy, but I'm really stumped as to what I should do.
Here's the story so far:
list.cpp
Node::Node()
{
next = nullptr;
book = nullptr;
}
Node::Node(Book* newBook)
{
next = nullptr;
book = newBook;
}
Node::~Node()
{
if (next != nullptr)
{
delete book;
}
}
Node* Node::getNextNode() const
{
return next;
}
void Node::setNext(Node* newNext)
{
if (newNext == nullptr)
{
next = nullptr;
}
else
{
next = newNext;
}
}
Book* Node::getBook() const
{
return book;
}
List::List()
{
first = nullptr;
last = nullptr;
numNodes = 0;
}
List::~List()
{
Node* tempNode = first;
first = first->getNextNode();
while (first != nullptr)
{
delete tempNode;
numNodes--;
tempNode = first;
first = first->getNextNode();
}
last = nullptr;
}
void List::push_front(Node* newNode)
{
Node* tempNode = first;
newNode->setNext(tempNode);
first = newNode;
if (last == nullptr)
{
last = first;
}
numNodes++;
}
void List::push_back(Node* newNode)
{
Node* tempNode = last;
tempNode->setNext(newNode);
last = newNode;
if (first == nullptr)
{
first = last;
}
numNodes++;
}
Node* List::pop_front()
{
if (first != nullptr)
{
Node* tempNode = first;
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
first = first->getNextNode();
numNodes--;
return tempNode;
}
return nullptr;
}
Node* List::pop_back()
{
if (last != nullptr)
{
Node* tempNode = first;
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
while (tempNode->getNextNode()->getNextNode() != nullptr)
{
tempNode = tempNode->getNextNode();
}
last = tempNode;
last->next = nullptr;
numNodes--;
return tempNode;
}
return nullptr;
}
Node* List::getFirst() const
{
return first;
}
Node* List::getLast() const
{
return last;
}
driver.cpp
int main()
{
// set up cout for displaying prices
cout.setf(ios::fixed);
cout.setf(ios::showpoint);
cout.precision(2);
// create a List object
List partsList;
cout << "\nPart I: multiple node test: push_front and pop_front\n";
cout << "\n----------------------------------\n";
// build a List using push_front
partsList.push_front(new Node(new Book("Fun With C++", "I. M. Codemann", 95.00)));
partsList.push_front(new Node(new Book("Lousy Linked Lists", "B. A. Hacker", 74.90)));
partsList.push_front(new Node(new Book("Programming Nuts", "R. U. Krazy", 85.25)));
partsList.push_front(new Node(new Book("Silly Syntax", "Irma Coder", 30.15)));
cout << "\nThe original nodes in the List:\n";
printList(partsList);
cout << "\n----------------------------------\n";
// test push_front function
cout << "\nAdding to the front of the List:\n";
cout << "\n----------------------------------\n";
partsList.push_front(new Node(new Book("Python's a Snake", "C. Rules", 65.45)));
partsList.push_front(new Node(new Book("Programming Fables", "J. Aesop", 73.15)));
printList(partsList);
cout << "\n----------------------------------\n";
// test pop-front
cout << "\nRemoving the first node from the list.\n";
cout << "\n----------------------------------\n";
Node* item = partsList.pop_front();
printList(partsList);
if (item != NULL)
delete item;
cout << "\n----------------------------------\n";
cout << "\nPart Two: Push_back and pop_back";
// test push_back
partsList.push_back(new Node(new Book("Coding Shortcuts", "B. Lazy", 110.25)));
partsList.push_back(new Node(new Book("Famous Programmers I Know", "M. T. Set", 126.00)));
cout << "\nAdding two nodes at the end\n";
cout << "\n----------------------------------\n";
printList(partsList);
// test pop-back
cout << "\n----------------------------------\n";
cout << "\nRemove last node from the list\n";
cout << "\n----------------------------------\n";
item = partsList.pop_back();
printList(partsList);
if (item != NULL)
delete item;
// delete all of the Nodes in the list
cout << "\nEmpty the list and delete all nodes\n";
while (partsList.getFirst() != nullptr)
{
Node * t = partsList.pop_front();
delete t;
}
printList(partsList);
// Test Push_front and pop_back - do they handle special case
cout << "\nTesting special case handling for push_front and pop_back\n";
partsList.push_front(new Node(new Book("Test Book 1", "nobody", 1.25)));
Node* t = partsList.pop_back();
cout << "\nThe Node just removed contains " << t->getBook()->getTitle() << endl;
delete t;
// Test push_back and pop_front - do they handle special cases
cout << "\nTesting special case handling for push_back and pop_front\n";
partsList.push_back(new Node(new Book("Test Book 2", "nobody", 1.25)));
t = partsList.pop_front();
cout << "\nThe Node just removed contains " << t->getBook()->getTitle() << endl;
delete t;
// Is the list now empty
cout << "\nThe list should now be empty...\n";
printList(partsList);
cout << "\n-------------------------------------------\n";
cout << "\nEnd of Test";
cout << "\n-------------------------------------------\n";
system("PAUSE");
return 0;
}
void printList(const List& theList)
{
if (theList.getFirst() == nullptr) // if the list is empty
cout << "\nempty list\n";
else
{
Node* t = theList.getFirst();
while (t != NULL)
{
Book* bp = t->getBook();
cout << bp->getTitle() << ' ';
cout << bp->getAuthor() << ' ';
cout << "$" << bp->getPrice() << endl;
t = t->getNextNode();
}
}
}
void printFirstNode(List theList)
{
Node* t = theList.getFirst();
Book* bp = t->getBook();
cout << bp->getTitle() << ' ';
cout << bp->getAuthor() << ' ';
cout << "$" << bp->getPrice() << endl;
t = t->getNextNode();
}
Alright, so what I have so far for my linked list seems to run relatively smoothly so far. The program only throws an exception (Unhandled exception at 0x00E5654B in Proj_02.exe: 0xC0000005: Access violation reading location 0xDDDDDDF1.) at the homestretch, the last printList function call. I'm not really sure why this exception is being thrown, the list should be completely empty. And so, I must hang my head in shame. What should I do to fix this issue?
Thank you. From the bottom of my heart.
Thomasz' answer is right so far, just some additional hints:
Wherever you set one of first or last to nullptr, do it for the other one, too:
first = last = nullptr;
push_back won't work, if the list is empty, last is nullptr itself then. Try this instead:
if(!last)
{
first = last = newNode;
}
else
{
last->setNext(newNode);
last = newNode;
}
++numNodes;
The Node class should be an internal detail of your list class, else you allow to modify the list externally without the List knowing about it! Make Node an inner class, and change the List's public interface such that it handles a Book* wherever you had a Node* before. Create and delete the Nodes within the List's functions then - and do not delete the Book from within the Node's destructor. You can still use the Node class to iterate the List, though, so it would correspond to the ::std::list's iterator.
And you can assign newNext directly: even if it is a null pointer, next still will be assigned exactly this, so the check is simply superfluous:
void Node::setNext(Node* newNext)
{
// if (newNext == nullptr)
//{
// next = nullptr;
//}
//else
//{
next = newNext;
//}
}
May I assume that you wrote the list for learning? Otherwise, you would probably have used ::std::list from the STL...
I allowed myself to rewrite your list class so you can compare. Hope this helps you...
class List
{
public:
List();
~List();
class Node
{
public:
Node const* getNext() const
{
return next;
}
Book* getBook() const
{
return book;
}
private:
friend class List;
Node(Book*);
Node* next;
Book* book;
};
void push_front(Book* book);
void push_back(Book* book);
Book* pop_front();
Book* pop_back();
Book* getFirst() const;
Book* getLast() const;
Node const* getFirstNode() const
{
return first;
}
private:
Node* first;
Node* last;
unsigned int numNodes;
};
List::Node::Node(Book* newBook)
: next(nullptr), book(newBook)
{
}
List::List()
: first(nullptr), last(nullptr), numNodes(0)
{
}
List::~List()
{
while (first)
{
Node* tempNode = first;
first = first->next;
delete tempNode;
}
}
void List::push_front(Book* book)
{
Node* node = new Node(book);
if (first)
{
node->next = first;
first = node;
}
else
{
first = last = node;
}
++numNodes;
}
void List::push_back(Book* book)
{
Node* node = new Node(book);
if (last)
{
last->next = node;
last = node;
}
else
{
first = last = node;
}
++numNodes;
}
Book* List::pop_front()
{
Book* book = nullptr;
if (first)
{
book = first->book;
Node* tempNode = first;
first = first->next;
if (!first)
{
last = nullptr;
}
delete tempNode;
--numNodes;
}
return book;
}
Book* List::pop_back()
{
Book* book = nullptr;
if (last)
{
book = last->book;
Node* tempNode = first;
if (first == last)
{
first = last = nullptr;
}
else
{
while(tempNode->next != last)
{
tempNode = tempNode->next;
}
delete last;
last = tempNode;
last->next = 0;
}
--numNodes;
}
return book;
}
Book* List::getFirst() const
{
return first ? first->book : nullptr;
}
Book* List::getLast() const
{
return last ? last->book : nullptr;
}
Your crash seems to be related to this part of code:
Node* List::pop_front()
{
if (first != nullptr)
{
Node* tempNode = first;
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
first = first->getNextNode();
numNodes--;
return tempNode;
}
return nullptr;
}
Both cases:
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
return first;
}
if (first == last)
{
numNodes--;
first = nullptr;
return first;
}
Seem to serve the purpose of a single-element list (first == last and next == nullptr). So, the first one should be:
if (tempNode->getNextNode() == nullptr)
{
numNodes--;
first = nullptr;
return tempNode;
}
Additionally this:
Node::Node(Book* newBook)
{
next = nullptr;
book = newBook;
}
Node::~Node()
{
if (next != nullptr)
{
delete book;
}
}
is a 100% leak in case of invocation like:
new Node(new Book("Test Book 1", "nobody", 1.25))
since next is still a nullptr, and book should be owned by Node, but isn't. Also, the last element will always leak.
#include "node.h"
#include <iostream>
// List class
class List
{
node *head; // head is an object that stores the address of the first node
public:
// constructor that initializes every list to null
List()
{
head = NULL;
}
// prtototype of the list member functions
void Print();
void Insert(float sal, int en);
void Delete(float sal, int en);
};
//linklist.h above
#include "linklist.h"
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
/**
* Append a node to the linked list
*/
void List::Insert(float sal, int en)
{
// Create a new node
node* newNode = new node();
newNode->SetData(sal, en);
newNode->setNext(NULL);
// Create a temp pointer
node *tmp = head;
if ( tmp != NULL )
{
// Nodes already present in the list
// Parse to end of list
/*while ( tmp->Next() != NULL )
{
tmp = tmp->Next();
}*/
// Point the last node to the new node
tmp->setNext(head);
}
else
{
// First node in the list
head = newNode;
}
}
/**
* Delete a node from the list
*/
void List::Delete(float salary, int data)
{
// Create a temp pointer
node *tmp = head;
// No nodes
if ( tmp == NULL )
return;
// Last node of the list
if ( tmp->Next() == NULL )
{
delete tmp;
head = NULL;
}
else
{
// Parse thru the nodes
node *prev;
do
{
if ( tmp->Epnum() == data && tmp->Salary()== salary )
break;
prev = tmp;
tmp = tmp->Next();
} while ( tmp != NULL );
// Adjust the pointers
prev->setNext(tmp->Next());
// Delete the current node
delete tmp;
}
}
/**
* Print the contents of the list
*/
void List::Print()
{
// Temp pointer
node *tmp = head;
// No nodes
if ( tmp == NULL )
{
cout << "EMPTY" << endl;
return;
}
// One node in the list
if ( tmp->Next() == NULL )
{
cout << tmp->Salary() + tmp->Epnum();
cout << " --> ";
cout << "NULL" << endl;
}
else
{
// Parse and print the list
do
{
cout << tmp->Epnum();
cout << " --> ";
tmp = tmp->Next();
}
while ( tmp != NULL );
cout << "NULL" << endl;
}
}
//linlist.cpp above
#include <iostream>
#include <cstdlib>
#include "linklist.h"
using namespace std;
void menu(List &);
int main()
{
// New list
List list;
menu(list);
return 0;
}
void menu(List &list)
{ char choice;
int item;
float salary;
do{
system("CLS"); // use #include <cstdlib>
cout << "\t\t\tMain Menu\n\n";
cout << "\tInsert{A}\n";
cout << "\tDelete\n";
cout << "\tPrint{P}\n";
cout << "\tExit\n";
cout << "\t\t What? ";cin >>choice;
choice = toupper(choice);
cin.ignore();
switch (choice)
{ case 'A':
cout << "Enter Employee numbers to insert and salary : "; cin >> item; cin>>salary;
list.Insert(salary, item);
cout << item<< " Inserted \n"; cin.get();
break;
/*case 'D':
cout << "Enter Item to Delete : "; cin >> item;
list.Delete(item);
cout << item<< " Deleted\n";cin.get();
break;*/
case 'P':
list.Print();cin.get();
break;
}
}while (choice != 'E');
}
//main.cpp above
//node.h
//#ifndef NODE_H
#define NODE_H
//node class
class node {
int epnum;
float salary;
node* next;
public:
node()
{} //null constructor
//stores argument passed in func.
void SetData(float _salary, int _epnum){
salary = _salary;
epnum = _epnum;
}
//stores in next the address of the next node
void setNext (node* anext){
next = anext;
}
//returns epnum stored
int Epnum(){
return epnum;
}
float Salary(){
return salary;}
//returns addres of next node
node* Next(){
return next;
}
};
//node.h above
I need to create a linked list that inserts a node at the front of the list as a program and of course print it out. I am unable to insert the node at the front of the list for some reason and I run into an infinte loop while trying to print it. It does something but I do not know exactly what. Please help.
In List::Insert, you have:
node *tmp = head;
followed by
tmp->next = head;
Hence, you have circular link in the object. Its next points to itself. This leads to infinite loop in the print function.
What you need is very simple:
void List::Insert(float sal, int en)
{
// Create a new node
node* newNode = new node();
newNode->SetData(sal, en);
newNode->setNext(head);
head = newNode;
}
In your insert method when you have 1 element already present tmp is set to head, then tmp->setNext(head); will create a reference to itself. This is the reason of infinite loop in your print method. Try the following insert code instead.
void List::Insert(float sal, int en)
{
// Create a new node
node* newNode = new node();
newNode->SetData(sal, en);
newNode->setNext(head);
head = newNode;
}
I would also note that in you print method there is no corner case for the list with 1 element. Your loop will perfectly handle this case. You will get the same result if you omit One node in the list branch.
void List::Print()
{
// Temp pointer
node *tmp = head;
// No nodes
if ( tmp == NULL )
{
cout << "EMPTY" << endl;
return;
}
// Parse and print the list
do
{
cout << tmp->Epnum();
cout << " --> ";
tmp = tmp->Next();
}
while ( tmp != NULL );
cout << "NULL" << endl;
}
void List::Insert(float sal, int en)
{
// Create a new node
node* newNode = new node();
newNode->SetData(sal, en);
newNode->setNext(NULL);
//set the newNode next to point to head
newNode->setNext(head);
//set the new head as the newNode
head = newNode;
// Create a temp pointer
//node *tmp = head;
/*if ( tmp != NULL )
{
// Nodes already present in the list
// Parse to end of list
/*while ( tmp->Next() != NULL )
{
tmp = tmp->Next();
}
// Point the last node to the new node
tmp->setNext(head);
}
else
{
// First node in the list
head = newNode;
}*/
}
/**
* Delete a node from the list
*/
void List::Delete(float salary, int data)
{
// Create a temp pointer
node *tmp = head;
// No nodes
if ( tmp == NULL )
return;
// Last node of the list
if ( tmp->Next() == NULL )
{
delete tmp;
head = NULL;
}
else
{
// Parse thru the nodes
node *prev;
do
{
if ( tmp->Epnum() == data && tmp->Salary()== salary )
break;
prev = tmp;
tmp = tmp->Next();
} while ( tmp != NULL );
// Adjust the pointers
prev->setNext(tmp->Next());
// Delete the current node
delete tmp;
}
}
/**
* Print the contents of the list
*/
void List::Print()
{
// Temp pointer
node *tmp = head;
// No nodes
if ( tmp == NULL )
{
cout << "EMPTY" << endl;
return;
}
// One node in the list
if ( tmp->Next() == NULL )
{
cout << tmp->Salary() + tmp->Epnum();
cout << " --> ";
cout << "NULL" << endl;
}
else
{
// Parse and print the list
do
{
cout << tmp->Epnum();
cout << " --> ";
tmp = tmp->Next();
}
while ( tmp != NULL );
cout << "NULL" << endl;
}
}
nvm i was tired when i was writting this code and just realized what I did wrong.
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;