1

I've been learning how Linked Lists work,and started building an implementation in C++ to reinforce the concepts. It was going well until I made a function to remove all nodes. I figured out a solution (which is the commented code) but I can't seem to figure out why the other code didn't work.

The Node object is an instance of a class that was created using 'new'. Therefore 'delete' is used to, well, delete it.

I thought it may have been related to deleting the object and reusing the pointer variable. Then I came across this: What happens to a pointer itself after delete? I've been staring at it for a while trying to figure out what it could be, and nothing I have researched seems to be providing an answer.

I don't believe it is something with my implementation so far because the programs works as expected when replacing the code with the solution code.

The code outputs every address, but it doesn't seem to actually delete the object. If I run the program in Windows, the program will actually lock and never leave the while loop. Not an infinite loop, it just gets stuck and the function never finishes. If I run it on C4Droid the program doesn't lock, but the nodes still exist after the function exits.

So my question is, why doesn't the current code work? (Ignore the commented code. That is a working solution.) Is there something simple I'm overlooking with pointer variables? Thank you in advance.

void LinkedList::deleteAll() {
Node *pCurrent = pHead;

while(pCurrent){
    Node *pNext = pCurrent->pNext;
    std::cout << pCurrent << std::endl;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;

//  pHead = pHead->pNext;
//  delete pCurrent;
//  pCurrent = pHead;
}
}

Node class

class  Node{
    public:
        Node(string content):data(content){}
        string getData(){
            return data;
        }
        Node *pNext = nullptr;
    private:
        string data;
    };

LinkedList.h

/*
 * LinkedList.h
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#ifndef LINKEDLIST_H_
#define LINKEDLIST_H_

#include<string>
using std::string;

class LinkedList {
public:
    LinkedList();
    virtual ~LinkedList();
    int length();
    void addNode(string nodeContent);
    void deleteNode(string nodeContent);
    void deleteAll();
private:
    class  Node{
    public:
        Node(string content):data(content){}
        string getData(){
            return data;
        }
        Node *pNext = nullptr;
    private:
        string data;
    };

    Node *pHead = nullptr;

};

#endif /* LINKEDLIST_H_ */

LinkedList.cpp

    /*
 * LinkedList.cpp
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#include "LinkedList.h"
#include <iostream>
LinkedList::LinkedList() {
    // TODO Auto-generated constructor stub

}

LinkedList::~LinkedList() {
    // TODO Auto-generated destructor stub
}

int LinkedList::length() {
    Node *current = pHead;
    int count = 0;
    while(current){
        count++;
        current = current->pNext;
    }
    return count;
}

void LinkedList::addNode(std::string nodeContent) {
    Node *newNode = new Node(nodeContent);
    newNode->pNext = pHead;
    pHead = newNode;
}

void LinkedList::deleteNode(std::string nodeContent) {
}

void LinkedList::deleteAll() {
    Node *pCurrent = pHead;

    while(pCurrent){
        Node *pNext = pCurrent->pNext;
        std::cout << pCurrent->pNext << std::endl;
        delete pCurrent;
        pCurrent = nullptr;
        pCurrent = pNext;

    //  pHead = pHead->pNext;
    //  delete pCurrent;
    //  pCurrent = pHead;
    }
}

main.cpp

/*
 * main.cpp
 *
 *  Created on: Oct 3, 2015
 *      Author: Anthony
 */

#include<iostream>
#include "LinkedList.h"

int main(int argc, char **argv){

    using namespace std;

    LinkedList list = LinkedList();
    list.addNode(string("Test"));
    list.addNode(string("Test1"));

    list.deleteAll();
    cout << list.length() << endl;

    return 0;
}
Community
  • 1
  • 1
Izodine
  • 95
  • 1
  • 6
  • Does your `Node` class have a destructor? If so, please post it. *I've been staring at it for a while* -- Use your debugger to debug the code. No need to stare at the program. – PaulMcKenzie Oct 03 '15 at 23:02
  • @PaulMcKenzie There is no destructor. The code for the Node class has been placed in the description. – Izodine Oct 03 '15 at 23:08
  • I guess you stack somewhere after this function (check it by debugger or by adding some print at the end), just try to set `pHead` to null at the end (after the loop). – SHR Oct 03 '15 at 23:12
  • Maybe your linked list is already corrupted or is put together incorrectly by the time you call the function. You also didn't set the`head` pointer to NULL after the loop. – PaulMcKenzie Oct 03 '15 at 23:12

2 Answers2

3

Assuming (and this is a big assumption) that your linked list is put together correctly, the issues with why the commented code works and the new code doesn't is rather simple.

  pHead = pHead->pNext;
  delete pCurrent;
  pCurrent = pHead;

In the above code, you are moving the pHead pointer through the list while you're looping. When the loop has finished, the pHead pointer is nullptr, which is correct since the list is now empty.

Node *pNext = pCurrent->pNext;
    std::cout << pCurrent << std::endl;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;

With your new, uncommented code, you did not set the pHead pointer after the end of the loop, thus leaving it pointing to garbage. Any usage of the linked list after that becomes invalid.

So it isn't the function isn't deleting all of the nodes, it is that after you've deleted the nodes, the linked list has a wild pHead pointer, and using the linked list's pHead node in any subsequent functions becomes unstable.

Try the following:

void LinkedList::deleteAll() {
Node *pCurrent = pHead;
while(pCurrent){
    Node *pNext = pCurrent->pNext;
    delete pCurrent;
    pCurrent = nullptr;
    pCurrent = pNext;
}
pHead = nullptr;  // Sets the head pointer to nullptr, denoting that the list is empty.
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • Thanks a lot! I understand now. It hinges off the fact that I was using an extra Node pointer instead of operating directly on the list which the commented code does. pHead was used to start the traversal but never operated on itself. I appreciate your time and detailed answer! – Izodine Oct 03 '15 at 23:57
2

There are two major memory regions involved when you create an object with the "new" keyword, the "call stack," which keeps track of local variables and which functions have been called, and the "heap," which is designed to hold larger amounts of data at the expense of speed.

When you declare your local variable pCurrent, a pointer gets created on the "call stack," just like an local integer variable would be put on the stack with the declaration "int a;" Local variables, variables on the stack, do not need to be deleted.

All objects created with "new" need to be deleted, though, as they are created on the heap.

As PaulMcKenzie wrote, make sure to set your head pointer to null as well.

void LinkedList::deleteAll() {

    Node *pCurrent = pHead;

    while(pCurrent){
        Node *pNext = pCurrent->pNext;
        std::cout << pCurrent << std::end;
        delete pCurrent;

        pCurrent = pNext;
    }

    pHead = nullptr;
}
cal2u
  • 68
  • 4
  • nullptr is a keyword included with C++11. I'm using delete on a pointer to the Object that was new'd. – Izodine Oct 03 '15 at 23:23
  • Oh, gotcha. Didn't know about that. – cal2u Oct 03 '15 at 23:24
  • I still don't get why you would set pCurrent to nullptr and then immediately reassign it. Of course it doesn't hurt anything, but it seems kind of useless. – cal2u Oct 03 '15 at 23:32
  • Because once the object is deleted the pointer is pointing to invalid memory. I got into the habit of making it a nullptr. If I go back and add code between those 2 lines that messed with the pointer it would be very dangerous. It isn't evident in this small program, but on a larger program it would be much harder to pinpoint the error. In my opinion anyway. – Izodine Oct 03 '15 at 23:38
  • Right, that's general good practice. But that's not an issue for a reassignment. If my pointer is pointing to memory address 0xfff and I reassign it to 0xaaa, there's no opportunity for me to access the memory at 0xfff anymore. – cal2u Oct 03 '15 at 23:40