Search code examples
c++pointerslinked-listnullptr

Object does not point to NULL after deleting


I have a Node class. The members are an int id, and a pointer to the next node: Node* next. I implemented the constructor and destructor as follows:

#include <iostream>

class Node
{
public:
  int id;
  Node *next;
  Node() : id(0), next(nullptr) {}
  explicit Node(int id) : id(id), next(nullptr) {}

  ~Node() {
    std::cout << (*this) << " destructed \n";
    delete next;
    next = nullptr;
  }

  friend std::ostream &operator<<(std::ostream &os, const Node &node) {
    os << "Node(" << node.id << ")";
    return os;
  }
};

int main()
{
  Node *node0;
  Node *node1;
  node0 = new Node(0);
  node1 = new Node(1);
  node0->next = node1;

  delete node0; // outputs Node(0) destructed\nNode(1) destructed
  node0 = nullptr;
  std::cout << (nullptr == node0) << '\n'; // outputs 1
  std::cout << (nullptr == node1) << '\n'; // outputs 0
  std::cout << *node1 << " \n"; // outputs Node(8430)
  return 0;
}

I thought node1 should already point to nullptr since when calling the destructor for node0, its next is deleted and point to nullptr, but the output suggests that node1 is apparently Node(8430) and is not a nullptr.

I know that when dereferencing a nullptr it could result in undefined behavior. How can I modify this program so that after calling delete node0, node1 can also become nullptr?


Solution

  • What you expect is similar to this:

    void foo(int* ptr) {
        delete ptr;
        ptr = nullptr;
    }
    
    int main() {
       int * x = new int;
       foo(x);
       assert( x == nullptr);   // NOPE !!
    }
    

    ptr is a copy of x. delete ptr deletes the dynamically allocated int. That does not affect the value of x in any way. x still has the same value after calling foo. It just is not a valid pointer anymore. The address where it points holds no int anymore.

    Or even simpler:

    int* a = new int;
    int* b = a;
    delete a;
    a = nullptr;
    assert( b == nullptr ); // NOPE !!!
    

    Actually your misunderstanding can be traced back to an even simpler example:

    int a = 42;
    int b = a;
    a = 0;
    assert( b == 0); // NO !!
    

    With pointers it is no different.

    In your code node0->next = node1; a copy of the pointer is made. Then in the destructor of node0 you delete the node pointed to by node0->next but that has no effect on the pointer in main.

    TL;DR: Do not confuse the pointer and the pointee. delete x deletes the object pointed to by x. If you copy a pointer you have two totally independent pointers that point to the same object, assigning nullptr to one of them has no effect on the other.