0

I am familiar with std::shared_ptr and std::weak_ptr and know how they work. However, I would like the std::shared_ptr to emit a callback, like a boost signal. This would allow std::weak_ptr, who still refer to the deleted object, to be cleaned up right in time.

Is there maybe already some smart pointer implementation, which will report the destruction?

Explanation

I think there might be a small design flaw in std::weak_ptrs, that can lead to false memory leaks. By "false memory leak" I mean an object, that is still accessible by the program, but whose reason of existence is not valid anymore. The difference to a true memory leak is, that the true memory leak is completely unknown by the program, while the false leak is just a part that wasn't cleaned up properly.

Another description can be found at codingwisdom.com

Use Watchers

  1. One of the problems with freeing an object is that you may have done so while other things are still pointing at it. This introduces dangling pointers and crashes! To combat the evil of dangling pointers, I like to use a basic "watcher" system. This is not unlike the weak reference discussed above. The implementation is like this:

  2. Create a base class "Watchable" that you derive from on objects that should broadcast when they're being deleted. The Watchable object keeps track of other objects pointing at it. Create a "Watcher" smart pointer that, when assigned to, adds itself to the list of objects to be informed when its target goes away.

This basic technique will go a long way toward solving dangling pointer issues without sacrificing explicit control over when an object is to be destroyed.

Example

Let's say we implement the observer pattern using boost Boost Signals2. We have a class Observable, that contains one or more signals and another class Observer, that connects to Observable's signals.

What would happen to Observable's slots, when a observing Observer is deleted? When we do nothing, then the connection would point to nowhere and we would probably receive a segmentation fault, when emitting the signal.

To deal with this issue, boost's slots also offer a method track(const weak_ptr<void>& tracked_object). That means, if I call track and pass a weak_ptr to Observer, then the slot won't be called when the weak_ptr expired. Instead, it will be deleted from the signal.

Where is the problem? Let's we delete an Observer and never call a certain signal of Observable ever again. In this case, the tracked connection will also never be deleted. Next time we emit the signal it will be cleaned up. But we don't emit it. So the connection just stays there and wastes resources.

A solution would be: when owning shared_ptr should emit a signal when it is destroying it's object. This would allow other other objects to clean up right in time. Everything that has a weak_ptr to the deleted object might register a callback and false memory leaks could be avoided.

Mat
  • 202,337
  • 40
  • 393
  • 406
Jack Sabbath
  • 1,398
  • 2
  • 9
  • 12
  • Basically this design requires keeping track of all weak/shared aliases to the same object for notification. There are many reasons why this wont work for a naive implementation (you now need to think about lifetimes of all observers, and the updating of this information, synchronized across threads), and even if you get it to work, this comes with a bigger performance and memory hit, which is too high a tradeoff for something in the standard library. Feel free to implement your own smart pointer to do this, I don't think shared/weak_ptr were designed with such callbacks in mind. – Preet Kukreti May 03 '15 at 11:56

2 Answers2

3
//
// create a C function that does some cleanup or reuse of the object
//
void RecycleFunction
(
   MyClass * pObj
)
{
   // do some cleanup with pObj
}

//
// when you create your object and assign it to a shared pointer register a cleanup function
//    

    std::shared_ptr<MyClass> myObj = std::shared_ptr<MyClass>(  new MyClass,  
                                                                RecycleFunction);

Once the last reference expires "RecyleFunction" is called with your object as parameter. I use this pattern to recycle objects and insert them back into a pool.

Markus Schumann
  • 7,636
  • 1
  • 21
  • 27
1

This would allow std::weak_ptr, who still refer to the deleted object, to be cleaned up right in time.

Actually, they only hold weak references, which do not prevent the object from being destroyed. Holding weak_ptrs to an object does not prevent it's destruction or deletion in any fashion.

The quote you've given sounds to me like that guy just doesn't know which objects own which other objects, instead of having a proper ownership hierarchy where it's clearly defined how long everything lives.

As for boost::signals2, that's what a scoped_connection is for- i.e., you're doing it wrong.

The long and short is that there's nothing wrong with the tools in the Standard (except auto_ptr which is broken and bad and we have unique_ptr now). The problem is that you're not using them properly.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Scoped_connection won't help... they just move the problem. The scoped_connections would have to be stored in a conntainer in the observer. And what happens if the Observable is deleted? The scoped_connections of the Observer wouldn't know about it and the container has junk. – Jack Sabbath May 03 '15 at 11:05
  • I agree with you, that the std pointers are fine and useful. However, sometimes a weak_ptr has to be used to keep a pointer to an object, which might be deleted.. without postponing its deletion. Lets say i post a heavy calculation on a thread-pool and the result should invoke a method on an object, which might be destroyed... that's when you use a weak_ptr, too. But if the object is already deleted, then the heavy calculation might opt to know, that it doesn't need to finish. – Jack Sabbath May 03 '15 at 11:08
  • @JackSabbath: No, you would store them in the Observable. When the observable is destroyed, the scoped_connections are also destroyed, and then goodbye the connections. – Puppy May 03 '15 at 11:09
  • Also, using `weak_ptr` in that fashion is completely fine because it doesn't prevent the object from being deleted in any way. However, this design involves far too much runtime speculation about lifetime and nowhere near enough actual design. – Puppy May 03 '15 at 11:10
  • Any connection, that is stored in the Observable won't be deleted, when the Observer is deleted (at least not unless the signal is invoked). Doesn't matter if it's scoped or not. That's the problem I described in the first place. – Jack Sabbath May 03 '15 at 11:14
  • @JackSabbath: If you destroy the scoped connection or the slot, and the underlying connection is not destroyed, that is a bug in Boost.Signals2 and you should file it with the maintainer. Your original post does not make any reference to scoped connections, only tracking with weak_ptr. However I'd be interested to know how you determined that they weren't being cleaned up... (also I misread your post and confused Observable and Observer, goddamn stupid patterns) – Puppy May 03 '15 at 11:17
  • That's not what I wanted to say. You proposed to store the scoped_connections in the Observable. Then destroying the Observer won't destroy the scoped_connections, nor any signals nor the connections themselves (everything is owned by the observable). But if you strore the Scoped_Connections in the Observer.. the cleanup is good when the Observer is deleted. But then, if the Observable is deleted, then the "Scoped_Connection" objects *won't* be deleted in the Observer.. even though the connection would be disconnected, we would have garbage objects that should be deleted. – Jack Sabbath May 03 '15 at 11:31
  • That is not what I meant to say. I meant to put them in the Observer, not the Observable. You don't need to destroy both the scoped_connections *and* the slot to destroy the connection.. only one of them. The connection only lives as long as the slot *and* the scoped_connection is active. – Puppy May 03 '15 at 11:39