1

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;
}
KQball
  • 107
  • 1
  • 1
  • 6
  • The final assignment of `first` to 0 is really unnecessary, as it's about to be destroyed anyway. – chris Aug 11 '13 at 06:19
  • 1
    [This test works.](http://coliru.stacked-crooked.com/view?id=396a5b4a2cfd3206de8924261cd800cc-3afcc3bc1f2cd10b247c33a6cde9edad) It was the most minimal thing I could come up with using the code you showed. – chris Aug 11 '13 at 06:24
  • How the nodes were allocated? If you used new[], you need delete[] in your destructor [see here](http://stackoverflow.com/questions/10769563/list-destructor-in-c) – cpp Aug 11 '13 at 06:26
  • You should check for "NULL", not "0", just as a matter of good practice, seeing as you initialise your pointers to "NULL" in your constructor. – Ozraptor Aug 11 '13 at 06:27
  • Better yet, use `nullptr`, which doesn't have the problems `NULL` has. – chris Aug 11 '13 at 06:28
  • @chris: it's ok to use delete if nothing was allocated with new? – cpp Aug 11 '13 at 06:28
  • 1
    @GrzegorzWilanowski, If it's a null pointer, yes. – chris Aug 11 '13 at 06:29
  • Could 'currentNode.next' be a non-null value? I.e. not properly initialized. – Boris Remus Aug 11 '13 at 06:29
  • An example of the way the Node was allocated: `groceries.push_front(new Node(1, "gallon", "milk") );` – KQball Aug 11 '13 at 06:36
  • Is a precondition of `push_back` that `newNode->getNext() == nullptr`? – DrYap Aug 11 '13 at 06:45
  • The problem may as well be in Node's destructor. I don't think you've shared Node code as well. – Artem Tokmakov Aug 11 '13 at 07:02
  • @biocomp You are right I made the changes offered below and still getting the same error. The Node class has been added. – KQball Aug 11 '13 at 07:04
  • @KQball: Are you running this on Windows 7/8/Vista by any chance if yes, try running it as administrator, – Xinus Aug 11 '13 at 07:09
  • @Xinus I am running it in Windows, using Visual Studio. I ran the .exe, as an Admin, by itself (not through VS) and had it throw this: `Expression:_BLOCK_TYPE_IS_VALID()pHead->nBlockUse)` – KQball Aug 11 '13 at 07:14
  • Ok, Node doesn't seem to be a problem. Do you, by any chance, delete node returned by getLast() or getFirst() anywhere in your code? – Artem Tokmakov Aug 11 '13 at 07:19
  • @biocomp I don't believe so. – KQball Aug 11 '13 at 07:31

3 Answers3

1

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.

Artem Tokmakov
  • 1,135
  • 10
  • 7
  • I've removed the last two deletes on the items popped back items - and it doesn't seem to be throwing an exception so this is definitely it. – Jeremy Aug 11 '13 at 07:52
  • This looks to be the case. The iteration over the list goes one element too far. Changing the for loop to use -2 instead should fix it. – Boris Remus Aug 11 '13 at 08:08
0

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.

Boris Remus
  • 191
  • 1
  • 4
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.

Uchia Itachi
  • 5,287
  • 2
  • 23
  • 26