jeudi 23 août 2018

How to prevent dangling pointer of captured "this" destroyed by shared_ptr?

#include <iostream>
#include <functional>
#include <memory>

class Event
{
public:
    void RegisterHandler(std::function<void(bool)> handler)
    {
        mHandler = handler;
    }  
    void Fire(bool value)
    {
        if (mHandler)
        {
            mHandler(value);
        }
    }
private:
    std::function<void(bool)> mHandler;
};

class EventListener
{
public:
    explicit EventListener(const std::string& value) : mValue{value} 
    {
        std::cout << mValue << " constructor" << std::endl;
    }
    ~EventListener()
    {
        std::cout << mValue << " destructor" << std::endl;
    }
    void Listen(Event& event)
    {
        event.RegisterHandler(std::bind(&EventListener::Handler, this, std::placeholders::_1));
    }
private:
    void Handler(bool value)
    {
        std::cout << mValue << " event " << value << std::endl;
    }
    std::string mValue;
};

int main()
{
    {
        Event event{};

        auto handler {std::make_shared<EventListener>("first")};
        handler->Listen(event);

        event.Fire(true);
    }
    {
        Event event{};

        {
            auto handler {std::make_shared<EventListener>("second")};
            handler->Listen(event);
        }
        std::make_shared<std::string>("Hello from dangling pointer");

        event.Fire(false);
    }
}

Output:

first constructor
first event 1
first destructor
second constructor
second destructor
om dangling pointer event 0

Test this code online


std::enable_shared_from_this seems to be a solution.

class EventListener : public std::enable_shared_from_this<EventListener>
{
public:
    void Listen(Event& event)
    {
        event.RegisterHandler([sharedThis{shared_from_this()}](bool value)
        {
            sharedThis->Handler(value);
        });
    }

Output:

first constructor
first event 1
first destructor
second constructor
second event 0
second destructor

Test this code online


The two rules I am aware of:

  1. After capturing this make sure you remove/unregister/cleanup the capture in destructor.
  2. Use shared_from_this() to don't bother about the lifetime.

Are there better solutions? Any advice how to prevent/handle such cases?

Aucun commentaire:

Enregistrer un commentaire