1

In a bigger OpenSource project we have the problem of a dangling pointer somewhere. Instances of a polymorphic object are passed around via a pointer to it and stored in a couple of places. More specifically: A ware is passed around as a Ware* which is allocated at one point and then registered in e.g. the building that ordered it and the carrier that currently holds it (and a couple of more places) The 'owner' is the place the physically 'has' it (e.g. the carrier). So when the carrier dies, he tells the ware to tell the building that ordered it that it no longer comes --> Building removes its pointer. Then the ware is deleted and no instance should have a pointer to it.

This works in most of the cases but there seems to be some conditions where this fails and an instance still has a pointer. I want to detect this. So I thought about replacing the Ware* by a class WarePtr that acts like a shared pointer (ref-counted). So when delete is called, it can check, if the refCount==1 and assert this.

The problem: For most of the usages it is a drop-in replacement. So syntactic changes besides the replacement Ware*->WarePtr. But I'd also need to change the delete ware calls which I would like to avoid so this could be removed without the need to change this back.

Is it possible to create a class or an overload so I can actually call delete ware where it is defined as WarePtr ware?
The called function would need to check the assertion and call delete on the contained pointer. Overloading the delete operator would only allow me to intercept calls to deleting a WarePtr*...

Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • Am I missing something here? Make the one, true owner (the object responsible for deleting it) hold a shared_ptr and only ever hand out weak_ptr when returning the Ware from this object. – Robinson Oct 26 '15 at 18:37
  • It sounds like what you really want are smart pointers, which are part of the C++11 subset. https://en.wikipedia.org/wiki/Smart_pointer https://msdn.microsoft.com/en-us/library/hh279674.aspx http://stackoverflow.com/questions/106508/what-is-a-smart-pointer-and-when-should-i-use-one – Xirema Oct 26 '15 at 18:37
  • 1
    You can overload `new` and `delete` at class level, but this is almost always not what you really want to do (at a semantical level). – πάντα ῥεῖ Oct 26 '15 at 18:38
  • I cannot use C++11 but I can use boost (this is why it does not make a difference here) With shared_ptr and weak_ptr I can prevent the problem, but not detect it. I also don't want the overhead incurred by those smart pointers as it is (should be) guaranteed, that the owner deletes the pointer and makes sure, no one has another one. So this is really about detection of a constraint violation without overhead in release mode – Flamefire Oct 26 '15 at 18:48

2 Answers2

4

If WarePtr has a non-explicit conversion function to Ware*, then you can call delete on a WarePtr.

If the operand has a class type, the operand is converted to a pointer type by calling the above-mentioned conversion function, and the converted operand is used in place of the original operand for the remainder of this section.

([expr.delete]/2)

Other than that, the operand must be a pointer.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • This would be a solution. So I could implement the conversion operator and overload the delete operator of Ware? Can I then simply call ::delete(ptr) inside the overloaded delete operator to do the actual deletion? – Flamefire Oct 26 '15 at 18:50
  • @Flamefire I believe that's correct. But you should probably try it out. – Brian Bi Oct 26 '15 at 18:58
0

I understand

detection of a constraint violation without overhead in release mode

I still suggest that you switch to shared_ptr, it's the right thing to do. The overhead over plain pointers will be very small, well worth the benefits.

You can then test for the constraint violation in your destructor (or wherever you delete the pointer you gave out) like this:

  • create a weak pointer.
  • reset() the shared pointer that was holding your object to delete it.
  • assert() that the weak pointer is expired().
Fozi
  • 4,973
  • 1
  • 32
  • 56
  • This also looks reasonable. I found that boost has an `intrusive_ptr` which is a `shared_ptr` with the ref-counter stored in the object. I find those to have less overhead as no extra (very small) memory must be allocated. Is it possible to use this while keeping forward declarations to Ware? That is avoiding the include of the Ware.hpp in header files that use the pointers only (no dereferencing)? – Flamefire Oct 27 '15 at 09:47
  • @Flamefire I'm not sure about the boost `intrusive_ptr`, it certainly is with the `shared_ptr`, as we are doing exactly that. There are some gotchas though, like the fact that the destruction of the `shared_ptr` is a use of the class pointed to. – Fozi Oct 27 '15 at 15:06