Pergunta

I have got some code which uses a lot of pointers pointing to the same address. Given a equivalent simple example:

int *p =  new int(1);
int *q = p;
int *r = q;

delete r; r = NULL; // ok
// delete q; q = NULL; // NOT ok
// delete p; p = NULL; // NOT ok

How to safely delete it without multiple delete? This is especially difficult if I have a lot of objects which having pointers all pointing to the same address.

Foi útil?

Solução

The answer, without resorting to managed pointers, is that you should know whether or not to delete a pointer based on where it was allocated.

Your example is kind of contrived, but in a real world application, the object responsible for allocating memory would be responsible for destroying it. Methods and functions which receive already initialized pointers and store them for a time do not delete those pointers; that responsibility lies with whatever object originally allocated the memory.

Just remember that your calls to new should be balanced by your calls to delete. Every time you allocate memory, you know you have to write balancing code (often a destructor) to deallocate that memory.

Outras dicas

Your tool is shared_ptr of the boost library. Take a look at the documentation: http://www.boost.org/doc/libs/1_44_0/libs/smart_ptr/shared_ptr.htm

Example:

void func() {
  boost::shared_ptr<int> p(new int(10));
  boost::shared_ptr<int> q(p);
  boost::shared_ptr<int> r(q);

  // will be destructed correctly when they go out of scope.
}

The "modern" answer is to use a smart pointer and don't do any manual deletes.

boost::shared_ptr<int> p(new int(1));
boost::shared_ptr<int> q = p;
boost::shared_ptr<int> r = q;

End of story!

The problem you are facing is that the ownership semantics in your program are not clear. From a design point of view, try to determine who is the owner of the objects at each step. In many cases that will imply that whoever creates the object will have to delete it later on, but in other cases ownership can be transferred or even shared.

Once you know who owns the memory, then go back to code and implement it. If an object is the sole responsible for a different object, the it should hold it through a single-ownership smart pointer (std::auto_ptr/unique_ptr) or even a raw pointer (try to avoid this as it is a common source of errors) and manage the memory manually. Then pass references or pointers to other objects. When ownership is transfered use the smart pointer facilities to yield the object to the new owner. If the ownership is truly shared (there is no clear owner of the allocated object) then you can use shared_ptr and let the smart pointer deal with the memory management).

Why are you trying to delete pointers arbitrarily? Every dynamically allocated object is allocated in one place, by one owner. And it should be that one owners responsibility to ensure the object is deleted again.

In some cases, you may want to transfer ownership to another object or component, in which case the responsibility for deleting also changes.

And sometimes, you just want to forget about ownership and use shared ownership: everyone who uses the object shares ownersip, and as long as at least one user exists, the object should not be deleted.

Then you use shared_ptr.

In short, use RAII. Don't try to manually delete objects.

There are some very rare cases where you might not be able to use a smart pointer (probably dealing with old code), but cannot use a simple "ownership" scheme either.

Imagine you have an std::vector<whatever*> and some of the whatever* pointers point to the same object. Safe cleanup involves ensuring you don't delete the same whatever twice - so build an std::set<whatever*> as you go, and only delete the pointers that aren't already in the set. Once all the pointed-to objects have been deleted, both containers can safely be deleted as well.

The return value from insert can be used to determine whether the inserted item was new or not. I haven't tested the following (or used std::set for a while) but I think the following is right...

if (myset.insert (pointervalue).second)
{
  //  Value was successfully inserted as a new item
  delete pointervalue;
}

You shouldn't design projects so that this is necessary, of course, but it's not too hard to deal with the situation if you can't avoid it.

It is not possible to know whether the memory referenced by a pointer has already been deleted, as in your case.
If you can not use a library with smart pointers, that do reference counting, and you can not implement your own reference counting schema (if indeed you need to do what you describe in your post) try to call realloc on the pointers.
I have read in other posts that depending on the implementation the call to realloc may not crash but return back a null pointer. In this case you know that the memory referenced by that pointer has been freed.
If this works as a dirty solution, it will not be portable, but if you have no other option, try it. The worse of course will be to crash your application :)

I would say that some times, smart pointers can actually slow down your application, albiet not by a lot. What I would do is create a method, like so:

void safeDelete(void **ptr)
{
  if(*ptr != NULL)
  {
     delete ptr;
     *ptr = NULL;
  }
}

I'm not sure if I did it 100% correctly, but what you do is you pass the pointer into this method that takes the pointer of a pointer, checks to make sure the pointer it points to isn't set to NULL, then deletes the object and then sets the address to 0, or NULL. Correct me if this isn't a good way of doing this, I'm also new to this and someone told me this was a great way of checking without getting complicated.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top