【问题标题】:Shared Pointer set in Lambda after out of scope超出范围后在 Lambda 中设置的共享指针
【发布时间】:2018-06-03 03:48:17
【问题描述】:

我遇到了共享指针、lambda 和范围的问题。

我的情况是我有一个我调用的方法,所以我可以稍后使用返回的值。到这里为止有点正常。当我在此方法中有一个异步方法时,问题就来了,我必须对 shared_pointer 进行一组异常。我正在使用 lambda 函数来处理来自此异步方法的回调。在这个 lambda 中,我必须使用我的 shared_pointer 并在其上设置一个异常(来自其类的方法) resultPtr->setException();

我首先使用了 shared_ptr,然后在 lambda 中使用了 weak_ptr(因为我在某些地方读到 shared_ptr 可能导致内存泄漏)。但直到现在我想我真的需要使用 shared_ptr。

我必须测试是否在第二个 .cpp 中设置了此异常,因此我调用该方法并使用返回值。我让线程等待大约 500 毫秒,然后我测试它是否有异常,但它没有。但是,在极少数情况下,它“有异常”。我可以看到正在调用 lambda,并且它通过我的日志设置了异常,但是它似乎并没有改变我首先返回的值(就像它指向不同的地方一样)。

我提到的第一种方法的输入对我的情况并不重要,只是它们是指针。

在我的情况下,我有类似的东西:

file1.cpp
typedef shared_ptr<Result> ResultPtr;
ResultPtr method_name(TypePtr args)
{
    if(args != nullptr)
    {
        ResultPtr result = make_shared<Result>();
        stuff->callAsync([this, result](input)
        {
            if(result == nullptr)
            {
                result->setException();
            }
        });
    }
    else
    {
        result->setError();
    }
    return result;
}

file2.cpp
bool testingMethod()
{
    ResultPtr result = file1::methodName(TypePtr args)
    std::this_thread::sleep_for(std::chrono::milliseconds(500));
    test(result->hasException);
}

为了测试,我正在使用另一个类,我用某些输入调用此方法(这里不重要),我需要比较 resultPtr 的对象值。发生的情况是,一旦超出范围(返回 a),我就无法再访问该对象。在我调用这个方法后,我让线程等待了大约 1 秒,但它永远不会改变状态(因为它不能指向被破坏的对象)

有没有办法克服这个问题?其他建议?

谢谢!

【问题讨论】:

  • 什么是stuff
  • “一旦超出范围(返回 a),我就无法再访问该对象了。”这不是真的,所以不清楚你有什么问题。提供minimal reproducible example
  • 您的示例在很多方面都令人困惑。将其缩小到与您的问题相关的事情。你有没有类型的“args”,在任何地方都没有使用,“method_name”是一个成员函数,但是没有类,没有类型的“input”......stackoverflow.com/help/mcve如果无法编译是问题“返回一个; ",将其缩小到无法编译的行。
  • @JonathanMee 一些与此场景无关的代码。可能只是一些日志或什么都没有,所以我决定将其保留。
  • @lars 我明白了。实际上,我在这上面花了太多时间,我的大脑本身就很困惑。我试图把我认为不必要的东西抽象化。 args 在这里并不重要,只是为了表明如果 args 是/是 nullptrs,我正在设置一个异常。一切都编译得很好,并且没有崩溃。检查我的编辑,也许它有助于更​​好地理解我的问题。

标签: c++ lambda scope shared-ptr


【解决方案1】:

当然,您不能再访问该对象,因为当shared pointer 离开创建它的if(args) 块的范围时,它就是destroyed。您需要让对象位于方法之外,例如。 G。将其作为类成员或 ( forbid!) 存储在全局变量中。

是否需要将其包装在共享指针中尚不清楚,您需要提供 MCVE 以及更清晰的 what you want to achieve 描述。

【讨论】:

    【解决方案2】:

    如果您的 callAsync 确实是异步的,那么当局部变量 resulta 已经超出范围时,您的 lambda 可能会开始执行。您应该将所需的变量作为参数传递,例如与std::bind绑定:

    typedef shared_ptr<Result> ResultPtr;
    ResultPtr method_name(args)
    {
        if(args)
        {
            ResultPtr a = make_shared<Result>();
            std::weak_ptr<Result> result(a);
            stuff->callAsync(std::bind([this](std::weak_ptr<Result> result_param)
            {
                auto resultPtr = result_param.lock();
                if(resultPtr)
                {
                    resultPtr->setValue()
                    ...other stuff...
                }
            }, result));
        }
        else
        {
            a->setError();
        }
        return a;
    }
    

    是的,你的例子令人困惑

    【讨论】:

    • 方法是异步的。我只是为它提供了参数,然后我收到了一个带有我需要的回调(这个异步方法不是我实现的,我也无法更改它)。所以我想绑定在这里不起作用。我的问题是我需要在 lambda 中更改 shared_ptr 的值,但我在 lambda 执行之前返回它。当我尝试在另一个类中使用返回值时(我正在测试它),即使 lambda 执行并将值设置为正确的值,它似乎也不会更新我在第二类中使用的指针。
    【解决方案3】:

    如果您需要 lambda 来保证它使对象保持活动状态,为什么要使用弱指针而不是共享指针来传递?弱指针的目的是您所看到的行为,它本身并不能使对象保持活动状态。

    因为你只捕获一个弱指针,所以当唯一的共享指针超出范围时,如果它在你锁定弱指针之前超出范围,它不会保持共享指针的活动。但是您可以只捕获共享指针(按值,因此获取副本)而不是使用弱指针。然后引用计数会增加,并且 lambda 内部的共享指针会保持活动状态,并且只有当两个共享指针都超出范围时才会被删除。

    这样的共享所有权是共享指针的原因,如果希望在共享指针引用计数达到零时对象将不再可访问,则应仅使用弱指针。

    【讨论】:

    • 是的,我现在知道了,但我不确定我的想法是否正确。原始代码已将 shared_pointers 传递给 lambda。在您回答后,我决定将 shared_ptr 保留在我的解决方案中。但是那样它仍然不能解决我的问题。当我使用该方法的返回值时,lambda 接收回调。但是这个值不会更新我在用于测试的其他方法中收到的值。
    猜你喜欢
    • 2015-01-08
    • 2021-10-17
    • 1970-01-01
    • 2011-01-17
    • 1970-01-01
    • 2013-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多