在生命周期有限的许多模块之间共享std :: function

时间:2014-08-12 18:28:58

标签: c++ c++11 dll

我在模块上创建一个std :: function并将其共享给另一个模块。创建std :: function的模块具有有限的生命周期,因此当另一个模块试图释放std :: function时,由于模块已经被释放,因此它不具备。

// A.dll
// A.h
__declspec(dllexport) void add_handler(std::function<void()> _handler);

// A.cpp
std::function<void()> handler;
void add_handler(std::function<void()> _handler)
{
   handler = _handler;
}

// B.dll
void main(void)
{
   std::function<void()> handler_tmp = []()
   {
      // something
   };

   add_handler(handler_tmp);
}
// B.dll is freed before A.dll

如果B.dll可能在A.dll之前被释放,我如何安全地在B.dll中创建一个std :: function并与A.dll共享?

1 个答案:

答案 0 :(得分:1)

这里有几点需要注意。首先,正如您在评论中指出的那样,如果一个对象添加了一个处理程序,它应该可以删除它。所以你对RAII对象提出了一个很好的观点。

class B
{
public:
    B() { add_handler([](){ /* handler code */ }); }
    ~B() { add_handler(nullptr); }
};

在上面的示例中,dll将在离开main时卸载。我理解您的上述代码是一个非常简单的示例,并且是概念性的。然而,lambda的范围很重要,可能会产生一些意想不到的后果。举个例子:

// B.dll
void main(void)
{
    { // <-- providing arbitrary scope for b
        B b { };
        // [ call other functions on b ]
    }

    // [ additional code which ends up calling the handler ]
}

如您所知,std :: function只是可调用对象的包装器。在上面的代码(可复制函数对象)中,即使没有B上的dtor,代码也会毫无问题地执行处理程序。 'b'不再在范围内,但是lambda的副本被移动(完整地,包括任何捕获的值和引用)到全局持有的std :: function中,并且只要全局处理程序将保持有效抓住它。

跟踪每个分配,您将看到整个lambda被传递[R-Value Ref]并被转发和交换。显然,由于这个处理程序的实际实现可能会以某种方式在'b'上运行,这是不可取的。但是,C ++的优点之一并不是让你付出处理所有这些场景的开销,并且执行清理,那么必须小心。