我一直在利用回调来减少某些C ++类之间的耦合。定义术语:我将调用类使回调成为调用者,并将接收回调的类称为被调用者。通常(但不一定),被叫方将拥有呼叫者。根据设计,调用者不了解被调用者。
我遇到了与调用者对象的生命周期有关的问题:它无法保证在进行任意回调后它仍然存在。拿这个基本的例子:
void caller::f()
{
/* Some work */
if (...)
{
/* [1] Execute callback */
_callee->callback(this);
}
/* [2] Some more work */
}
假设被调用者已动态分配调用者,并且已经注册了回调,专门用于等待某个条件发生。如果是,被调用者将从[1]的回调中删除调用者。如果是这种情况,控制将返回到caller :: f,但this
将被删除,[2]中的任何代码都可能会崩溃。
在一般情况下,调用者不能假设有关被调用者的任何内容。它不知道被叫方是否拥有this
,或者它是否可以解除分配this
,因此我需要一些通用方法来防止对调用方成员函数范围的重新分配。
我认为可能的解决方案围绕boost::shared_ptrs
和enable_shared_from_this
,但我从未使用过它。由于这些回调在处理能力有限的移动设备上运行非常频繁(每秒40次以上),我也担心创建和传递那么多shared_ptrs
的开销。
委托是Objective-C中非常常见的模式。我对常见的C ++设计模式不太熟悉。这个问题有没有快速简便的解决方法?如果没有,这个设计通常如何在C ++中完成?
答案 0 :(得分:1)
当被调用者delete
的调用者调用调用者的析构函数时。在那里你应该确保f
已经完成。
我猜f
是一个线程,所以最简单的解决方案是:
主题:
running = true;
while (!must_exit)
/* do something */
destuctor:
thread->must_exit = true;
while (thread->running)
sleep(a_little);
/* continue with destruction */
如果f
不是一个线程,那么同样的原则可以适用于f
使其对象(以及它的析构函数)知道它何时运行以及何时不运行的情况。
如果你不想使用析构函数方法,你仍然可以通过被调用者调用的函数实现这个功能,告诉f
永远不再运行并等到它停止。然后被叫方继续删除呼叫者。
这样的事情:
void caller::f()
{
if (being_deleted)
return;
running = true;
/* Some work */
if (...)
{
/* [1] Execute callback */
_callee->callback(this);
}
/* [2] Some more work */
running = false;
}
void caller::make_f_stop()
{
being_deleted = true;
while (running)
sleep(a_little);
}
答案 1 :(得分:1)
继续使用共享指针,但如果可能,请使用std::shared_ptr
而不是boost::shared_ptr
。它位于(当前)标准库中,因此无需添加不必要的boost依赖项。如果你已经使用了助推器,那也没关系。
您没有说明您正在谈论的移动设备类型,但现代智能手机中的处理器运行数百或数千兆赫兹,甚至低功耗手机也经常运行Java程序(垃圾收集)就好了。共享指针基本上是引用计数。这不是一项资源密集型活动。
如果你的设备能够实际每秒运行40次以上的回调,我怀疑它会对共享指针造成任何麻烦。不要过早地优化执行速度。过早地优化安全性和理智。
答案 2 :(得分:1)
绕过不能再执行的代码的常用方法是抛出异常。这应该由被调用者在删除调用者之后通常返回给调用者的点完成。异常将在函数末尾的调用者代码中捕获。
我不能说我喜欢这个解决方案,但我认为这源于被叫方拥有来电者的不寻常情况。
我不知道智能指针会有多大帮助,因为没有人拥有指针的第二个副本。