通过值将shared_ptr传递给lambda会导致内存泄漏 [英] Passing shared_ptr to lambda by value leaks memory

查看:104
本文介绍了通过值将shared_ptr传递给lambda会导致内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

void MyClass::onOpenModalBtnClicked() {
    uiManager->load(L"data/ui/testmodal.json");
    std::shared_ptr<UIElement> modal = uiManager->getElementById("loginModal");

    if(modal) {
        modal->getElementById("closeButton")->onClicked = [modal]() {
            modal->hide();
        };
    }
}

这很好用,并且单击按钮时模态是关闭的, onClicked std :: function .

This works fine and the modal is closed when the button is clicked, onClicked is a std::function.

我的应用程序开头也有这个内容:

I also have this at the beginning of my app :

#if defined(DEBUG) | defined (_DEBUG)
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif

这会在应用终止时打印出内存泄漏.

This prints out memory leaks when the app terminates.

使用上面的代码,我会发生很多内存泄漏,如果我将代码更改为下面的代码,它们将全部消失:

With the above code I get lots of memory leaks, if I change the code to the below they are all gone :

void MyClass::onOpenModalBtnClicked() {
    uiManager->load(L"data/ui/testmodal.json");
    std::shared_ptr<UIElement> modal = uiManager->getElementById("loginModal");

    if(modal) {
        modal->getElementById("closeButton")->onClicked = [this]() {
            uiManager->getElementById("loginModal")->hide();
        };
    }
}

我假设按值传递 shared_ptr 会使ref计数增加1,然后此引用永远不会超出范围,或者在报告内存泄漏后不会超出范围.所以我尝试在使用shared_ptr之后在lambda内调用reset,但是随后出现此编译器错误:

I am assuming passing in the shared_ptr by value increases the ref count by 1 and then this reference never goes out of scope or it goes out of scope after the mem leaks are reported. So I tried to call reset inside the lambda after I used the shared_ptr but then I get this compiler error :

错误1错误C2662:'void std :: shared_ptr< _Ty> :: reset(void)throw()':无法从'const std :: shared_ptr< _Ty>'转换'this'指针到'std :: shared_ptr< _Ty>&'

所以问题是我该如何使用捕获的 modal 而不出现那些内存泄漏?

So the question is how can I use the captured modal and not get those memory leaks?

修改:因此,我通过向Lambda添加 mutable 摆脱了编译错误.

So I got rid of the compile error by adding mutable to the lambda.

if(modal) {
    modal->getElementById("closeButton")->onClicked = [modal]() mutable {
        modal->hide();
        modal.reset();
    };
}

现在,如果我单击关闭按钮,然后关闭应用程序,则不会发生内存泄漏,因为重置会清除该引用.但是,如果从未单击该按钮,我仍然会泄漏.

Now if I click the close button, and close the app there are no memory leaks, since the reset cleans that reference. But if the button is never clicked I still get the leaks.

推荐答案

您已创建了shared_ptr周期.

You have created a shared_ptr cycle.

modal必须在其引用计数达到0之前销毁.然后,将shared_ptr的副本传递给modal到labmda函数中,以增加其引用计数.然后,您将该lambda函数分配给modal成员.

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.

这意味着模式始终由其回调函数引用.但是,除非模式没有引用计数,否则无法销毁其回调函数.模态最终被卡在ref计数为1的地方.

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.

通常的解决方案是将裸指针或(最好是)弱指针传递到lambda中.

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

这篇关于通过值将shared_ptr传递给lambda会导致内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆