Reputation: 5387
I have class implementing callbacks using std::function template. I recently noticed, that I destroy the object inside callback (i.e. std::function destroys itself).
Is it a well-known issue? Does std::function designers foresaw that it may happen (is std::function resilient to such situations)?
Sample code:
struct Sample
{
std::function<void()> callback;
void emit_callback ()
{
callback ();
}
}
void callback(Sample* object)
{
delete object;
}
/////////////////////////////////////////////
Sample* object = new Sample();
object->callback = std::bind(&callback, object);
object->emit_callback ();
Upvotes: 2
Views: 1576
Reputation:
This can become quite a design issue in a very complex system leaning heavily on signals and slots-type event/notification designs.
If you connect an object-bound function/method to a signal and the object is destroyed, there's no inherent, built-in kind of way to protect against the signal being invoked a second time and accessing a destroyed object through a dangling pointer if you forget to disconnect the slot when the object is destroyed.
One way to mitigate this issue is to bind shared_ptrs to objects rather than pointers. However, that can exchange a dangling pointer for a logical resource leak that may not only leak memory but also trigger unpredictable behaviors by preventing objects from being destroyed when they should. It also requires you to put all such objects on the heap.
Binding weak references to objects solves this without these issues, but can still leave a lot of destroyed slots in your signal unless you find times appropriate to clean up all the slots that have been destroyed. It also imposes a runtime check every time when invoking the callback to see if the weak reference is pointing to an object that has already been destroyed which can become a non-trivial overhead, especially since a weak reference validity check often involves converting it to a strong reference and doing some atomic CAS-type operations for thread safety. You also still have to put all such objects on the heap.
Another way to mitigate this is to have your signal design return a strong reference (shared_ptr
or sometimes a unique_ptr
is fine, e.g.) to a kind of proxy object, which, when destroyed, automatically disconnects the slot from the signal. Then clients connecting to the signal can capture those objects and store it somewhere in a way that is tied to their lifetime.
You do, unfortunately, have to kind of roll up your sleeves and settle on a higher-level design for these cases. Things like std::function
won't prevent these safety issues on their own.
Another headache inducer just to give you a taste of the pain is in a very dynamic system that allows loading and even unloading plugins on the fly without restarting. In those cases, something like std::function
may not merely be bound to point to resources that have already been destroyed in a way where even shared_ptr/weak_ptr
can't protect you, but the function it's pointing to may not even be around in memory anymore, leading to a kind of 'dangling function pointer'. As pointed out in the other answer, protecting against these issues often involves additional states and processing that may be overkill for a number of common scenarios. So std::function
doesn't prevent them by default: you have to build those safety constructs on top if you need it. A lot of the C++ standard lib is useful as building blocks for higher-level constructs, but are not necessarily at that level on their own.
Upvotes: 3
Reputation: 1643
As Bjarne Stroustrup said: "C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off". Look here
You can do this:
class NotGonaMakeItToTheEnd
{
public:
NotGonaMakeItToTheEnd ( )
{
delete this;
}
};
And compiler will allow this.
All this things with pointers is done in C++ for C-compatiblity and for don't pay for what you don't use idiom. If you realy want to make a neat cabinet of pointers instead of garbage pile you should use shared_ptr, unique_ptr
.
Upvotes: -1