0

I have this small piece of code in my IntList implementation to push_back nodes. My question is if the *pnode cause memory leak or do I need to delete it at the end.

void IntList::push_back(int data){
    if (first){
        IntNode *pNode = first;
        while(pNode->next!=0){pNode = pNode->next;}
        pNode->next = new IntNode(data);
    } else
        first = new IntNode(data);
}
arodriguezdonaire
  • 5,396
  • 1
  • 26
  • 50
CPlusPlus
  • 5
  • 1
  • 4
  • As always: `delete` only stuff that comes from `new`, and that exactly once. Same for `new[]` and `delete[]`. – Baum mit Augen Aug 06 '15 at 18:56
  • You don't need to `delete` anywhere in that function, but you should be sure to `delete` all nodes in the `IntList` destructor. – caps Aug 06 '15 at 19:01
  • 1
    Use [valgrind](http://valgrind.org/docs/manual/QuickStart.html) on linux or [Dr.Memory](https://github.com/dynamorio/drmemory) on Windows to check for memory leaks. – ChajusSaib Aug 06 '15 at 19:02
  • Just use `std::list`. – Christian Hackl Aug 06 '15 at 19:03
  • 1
    Perhaps use smart pointers – Ed Heal Aug 06 '15 at 19:04
  • Try to avoid advanced concepts like manual `new` and `delete` if possible. Smart pointers would work just fine here, relieving you from worrying about leaks. – Emil Laine Aug 06 '15 at 19:15
  • @ChristianHackl [`std:queue`](http://www.cplusplus.com/reference/queue/queue/) or [`std::stack`](http://www.cplusplus.com/reference/stack/stack/) are more like the example. However, I think @CPlusPlus only wants to learn C++. – Piotr Siupa Aug 06 '15 at 19:58

5 Answers5

1

No you don't need to call delete on pNode. You only call delete on things created with new. With the code as it is now pNode is a stack object and will automatically be destroyed when it goes out of scope at the end of the function.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
0

You have to delete the IntNodes created with new eventually, likely in the destructor of the container and the pop_back function. pNode itself (the pointer) was allocated on the stack, and not with new, so does not need to be deleted.

Ethan Fine
  • 519
  • 3
  • 8
0

You don't need delete pNode. Moreover, you can't do it in this particular case. After you create something with new you must delete it exactly one time - once you'll never use it. After removing the object with delete, attempt to read its contents is an undefined behavior. The pointers are generally one of the most bug generating part of the , so it is good that you are trying to understand it better.

You can visualize yourself this way: You can buy a house (house is a piece of memory) with new. It returns you address if it. It is your house now and can do with it what you want. You can also sell it with delete. Until this is done, you can give your friend your home address so that they can come to you. Distribution of the address, causing copying it (e.g. IntNode *pNode = first;). However, you still have only one house. So no matter how many times you copy your home address, you can sell the house only once.

I would advise using smart pointers (e.g. std::unique_ptr), but I think this program is for learning programing, so don't do it ;)

Community
  • 1
  • 1
Piotr Siupa
  • 3,929
  • 2
  • 29
  • 65
  • It'd be fine to use `std::unique_ptr` in a program that's for learning about linked lists. In a program that's for learning about resource management you'd instead implement your own `unique_ptr`. – bames53 Aug 07 '15 at 00:47
0

You need delete nodes on each function that removes nodes from the list, not when inserting. Not doing so would cause a leak.

If there are still allocated nodes when you destruct the object you need to iterate the whole list to remove all the nodes too. Not doing so would cause a leak too.

I suppose that this is a college assignment, as there are millions of battle-tested linked list implementations out there.

You'd do better if you maintain a tail node up to date on all functions, so you could avoid iterating the whole list to insert a node on the tail.

Rafael Gago
  • 126
  • 1
  • 3
0

The code you show is not sufficient to tell if you're leaking memory or not. You show the routine responsible for allocation, but not the code where you're performing deallocation.

If you don't have any code performing deallocation, then yes, obviously that leaks. C++ does not perform automatic garbage collection as you may be used to in some other languages.

You are using naked new, so even if you do have some other code attempting to do deallocation, there's a good change it's being done incorrectly.


In C++ you generally shouldn't be using the new operator directly, and you should learn to use RAII to handle resources instead. IntList presumably uses owning raw pointers, which is another thing to be avoided. In this case you should probably be using unique_ptr<IntNode>. For example:

struct IntList {
  struct IntNode {
    unique_ptr<IntNode> next;
    int data;

    IntNode(int data) : data(data) {}
  };

  unique_ptr<IntNode> first;

  // returns a reference to the null pointer which terminates the linked list
  unique_ptr<IntNode> &get_tail() {
    if (!first) {
      return first;
    }
    IntNode *pNode = first.get(); // pNode is a non-owning pointer
    while (pNode->next) {
      pNode = pNode->next.get();
    }
    return pNode->next;
  }

  void push_back(int data) {
    get_tail() = make_unique<IntNode>(data);
  }
};

There actually is a problem with the above code, but the issue is unrelated to memory leaks. When the list is destroyed, first is automatically destroyed, which automatically destroys first->next, and so on. It's possible to insert so many elements into the list that this chain of destruction 'smashes' the function stack, leading to undefined behavior.

The problem can be fixed with an IntList destructor that destroys the nodes in the opposite order:

IntList::~IntList() {
  while (first) {
    unique_ptr<IntNode> tmp = std::move(first);
    first = std::move(tmp->next);
  }
}

It's interesting that this is an example of an exception to the Rule of Three.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • We may not use unique_ptr. How would you do the destructor if you each node has an 'next' reference and the intlist has an 'first' reference? – CPlusPlus Aug 06 '15 at 21:18
  • @CPlusPlus If you're not allowed to use `std::unique_ptr` then you should write your own version of it and use it exactly as you would use `std::unique_ptr`. The idea is to encapsulate the ownership semantics for an allocated `IntNode` into a class that does nothing except to correctly manage a dynamically allocated `IntNode`'s lifetime. (and note that implementing your own `unique_ptr` is one of the places it's appropriate to use `new`.) The point is that code for managing a resource's lifetime should not be mixed up with other code using that resource. – bames53 Aug 06 '15 at 22:31