views:

61

answers:

4

I have the following class:

class Stack {
  struct Link {
    void* data;
    Link* next;
    void initialize(void* dat, Link* nxt);
  }* head;
public:
  void initialize();
  void push(void* dat);
  void* peek();
  void* pop();
  void cleanup();
};

The pop method is:

void* Stack::pop() {
  if(head == 0) return 0;
  void* result = head->data;
  Link* oldHead = head;
  head = head->next;
  delete oldHead;
  return result;
}

oldHead is a pointer to a struct Link, which has a void pointer as member. So by deleting oldHead I'm implicitly deleting that void pointer, right?

I'm reading Thinking in C++ by Bruce Eckel, and it says that deleting void pointers doesn't clean things up properly because delete needs to know the type of the pointer.

This code is implicitly deleting the void pointer data, so: Can someone explain why is this (implicit) way of deleting a void pointer different from deleting with delete <void pointer>?

A: 

By deleting Link, that void* memory space is not being deleted. You need to define a destructor which deletes the memory that has been allocated. Each new needs one delete. An example of this for the Link-struct would be to add a destructor that deletes data. If your assumption would be correct, then next would also be deleted, causing your entire linked-list to be deleted which would be a terrible behavior.

Calling delete on a pointer will call the destructor of the pointed to type. If that type has no destructor, no such destructor will be called. This is the case for a void pointer which doesn't have a destructor. In the case of inheritance, the destructor should always be virtual so that the deepest class in the hierarchy will have their destructor called. The memory will be correctly free'd even if you cast your pointers to the wrong types - it's just that the destructor will be called incorrectly.

Simon
A: 

"I'm implicitly deleting that void pointer, right?"

Well, you're deleting the pointer itself when you delete oldHead. You're not deleting or freeing its target, which is what you seem to be wanting, and is what happens when you call delete on a pointer.

(To see why this is the case, consider that you might define a struct with a void* pointer that points to something outside the struct. You wouldn't want the target to be freed just because the struct was deleted.)

Brooks Moses
He's returning the target, so he shouldn't delete it in the first place. Because of the void pointers, the stack class has no idea what the target is. Managing the stored values is entirely up to the user. (In C++, such classes are normally written with templates, and void pointers are not used unless there's a very good reason - compatibility with old C code and the like.)
UncleBens
+1  A: 

Your terminology is causing ambiguity, but let me explain. Let's say you have:

struct foo
{
    void* bar;
};

Whenever a foo ends its lifetime, bar simply stops existing too. So if you have:

{
    foo f = { new int; }
}

You've leaked, as new int is never deleted. Likewise, when you do:

{
    foo* f = new foo;
    f->bar = new int;
    delete f;
}

You've still leaked, since when delete f is run, you simply end the lifetime of what f is pointing to (just like what happened automatically above), ergo bar simply ceases to exist and the new int is not deleted.

To summarize, when an object's lifetimes ends, delete is not called on the members that are a pointer.

So when you call delete on a Link, it's the same situation as bar in foo above: you're deleteing the memory for a Link causing data to stop existing, but not actually deleting what it's pointing at.

GMan
Thank you everyone for your answers.
tyomero
A: 

One problem with deleting a void pointer happens when you are pointing to something with a destructor:

#include <iostream>

struct foo
{
    ~foo() { std::cout << "important work" << std::endl; }
};

int main()
{
    foo *f = new foo;

    void *v = f;

    delete v;
}

If you run the code sample above, you will see that the destructor is never called.

R Samuel Klatchko