如何保证对象的生命周期与成员函数的持续时间相匹配?

时间:2012-04-03 15:36:52

标签: c++ lifetime

我一直在利用回调来减少某些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_ptrsenable_shared_from_this,但我从未使用过它。由于这些回调在处理能力有限的移动设备上运行非常频繁(每秒40次以上),我也担心创建和传递那么多shared_ptrs的开销。

委托是Objective-C中非常常见的模式。我对常见的C ++设计模式不太熟悉。这个问题有没有快速简便的解决方法?如果没有,这个设计通常如何在C ++中完成?

3 个答案:

答案 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)

绕过不能再执行的代码的常用方法是抛出异常。这应该由被调用者在删除调用者之后通常返回给调用者的点完成。异常将在函数末尾的调用者代码中捕获。

我不能说我喜欢这个解决方案,但我认为这源于被叫方拥有来电者的不寻常情况。

我不知道智能指针会有多大帮助,因为没有人拥有指针的第二个副本。