Passing shared_ptr to lambda by value leaks memory

You have created a shared_ptr cycle.

modal cannot be destroyed until its reference count hits 0. You then pass a copy of a shared_ptr to modal into the labmda function, incrementing its reference count. You then assign that lambda function into a member of modal.

This means that modal is always referred to by its callback function. However, its callback function cannot be destroyed until modal has no refcount. Modal ends up getting stuck with a ref count of 1.

The usual solution is to pass either a naked pointer or (preferrably) a weak pointer into the lambda


No.

As solution for this problem i have the following simple test:

class Modal {
public:
    Modal(){ onClick = nullptr; }
    std::function<void()> onClick;
};

class Data {
public:
    string* message;
    Data() { message = nullptr; }
    Data(string s) { message = new string(s); LOG << "CREATED" << NL; }
    Data(Data&& d) { LOG << "MOVE CTR" << NL; message = d.message; d.message = nullptr;}
    Data(const Data& d) { LOG << "COPY CTR" << NL; message = new string(*d.message); }
    virtual ~Data() { if (message) delete message; LOG << "DESTROYED" << NL; }
};


{
    Modal modal;
    {
        std::shared_ptr<Data> p = std::make_shared<Data>(Data("Will it be deleted?"));
        LOG << *(p->message) << " " << p.use_count() << NL;
        modal.onClick = [p](){
            LOG << *(p->message) << " " << p.use_count() << NL;
        };

        modal.onClick();
    }

    modal.onClick();
    modal.onClick = nullptr;
    LOG << "End of test" << NL;
}

Where i get the following picture as output:

Test output

As you can see when you overwrite onClick handler destroy event is called. So there are no need for any reset() calls inside lambda body. See reference counter output. The lambda is functor object and is properly destroyed when holder object (modal in example) no longer exists or field is cleared (or updated).