【问题标题】:is it safe to delete std::function from withing its stored callback [duplicate]从存储的回调中删除 std::function 是否安全[重复]
【发布时间】:2019-12-21 11:43:23
【问题描述】:

我有一个类,它在std::function 中存储回调并执行一些操作,然后将结果通知回调目标。如果目标看到结果无效或不想继续工作,它将删除包含std::function 回调的调用者类。

我知道如果std::function 在删除后不访问其成员,这将是安全的,但我没有看到这样的保证。我的事情是调用者类在调用回调后不会访问其成员,但是回调可以选择再次做更多的工作,在这种情况下它不会删除调用者。

这个例子说明了这个问题:

using callback = std::function<void(Caller& c, bool failed)>;

class Caller
{
    callback cb;
    bool Work()
    {
        return true;
    }

public:

    Caller(const callback& cb) : cb{ cb } {}

    void DoWork()
    {
        bool result = Work();
        cb(*this, result);
    }

};


int main(int argc, char **argv)
{

    for (int i = 0; i < 1000; ++i)
    {
        Caller *c = new Caller{
            [](Caller& c, bool failed)
        {
            if (failed)
                delete &c;
            else
                c.DoWork();
        }
        };
        c->DoWork();
    }
}

我使用 msvc 在 windows 上编译了代码,它可以正常工作(或者可能是一些隐藏的堆损坏?)

我不希望建议使用另一种设计,因为我使用的真实代码很复杂,调用者类就像工作人员和回调之间的一个 proky,并且是按操作分配的,重构代码也会很多工作

编辑:我知道这不是最好的 c++ 实践,但我正在处理 c api 特别是 iocp。我的代码是这样工作的:io 对象(套接字)发出一个 io 请求,需要为重叠的结构和操作数据分配内存,这个结构将保存回调,当操作完成时,它将调用回调,如果有错误回调可能不会继续,否则它将发出下一个请求。调用回调后,结构将自行删除,从回调开始的新操作将像这样继续。

现在假设我有一个半双工协议的会话,比如 http,那么一次只有一个操作,所以我可以对所有操作使用相同的结构而无需再次分配,所以当第二个操作开始时我破坏了旧结构并在适当位置构造新结构,这是在第一个结构的回调中完成的

【问题讨论】:

  • 这里为什么不用shared_ptr?这样您就不必删除任何内容。
  • 这能回答你的问题吗? Is delete this allowed? 我认为这相当于 delete &amp;c; 应该与 delete this; 相同
  • 即使它不是 UB 而是操作代码 - 你也不应该这样做。您多次违反 C++ 准则。你最好现在就做额外的工作和重构,而不是继续以这种方式编写代码并创建一个更大的难以管理的可憎。

标签: c++ memory-management


【解决方案1】:

看起来不错。我会用指针替换回调中的Caller&amp; 引用,但这并不重要。

我在您的代码中看到的唯一潜在问题是,您在那里的递归可能会导致堆栈溢出。如果您的 Work() 函数将返回 false = 没有失败几十万次(或者在调试构建中仅失败 600 次,由于缺乏优化,它们会消耗更多的堆栈空间),您的应用程序将耗尽堆栈并崩溃。如果你知道你的Work() 函数在返回true 之前只返回了几次false,那么你很好。但如果您的工作函数需要能够运行多次迭代,请考虑将递归替换为循环。

【讨论】:

  • 是的,我知道这个堆栈溢出,因为我在编写示例时将它放在调试器中。但是是的,这只是一个与真实代码相去甚远的示例
  • @dev65 顺便说一句,我建议用指针替换引用的原因 - 在编写 lambda 表达式时很容易忘记 &amp;,然后尝试删除堆栈分配的值会崩溃。 C++ 不会自动将值转换为指针,因此如果您忘记了 *,它将无法编译。
猜你喜欢
  • 1970-01-01
  • 2016-03-27
  • 1970-01-01
  • 2010-10-07
  • 2014-01-05
  • 2017-02-06
  • 2017-07-27
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多