0

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

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

4

3 回答 3

1

当被调用delete者的调用者,调用者的析构函数被调用。在那里你应该确保f已经完成。

我猜f是一个线程,所以最简单的解决方案是:

线:

running = true;
while (!must_exit)
    /* do something */

析构函数:

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);
}
于 2012-04-03T15:46:47.110 回答
1

继续使用共享指针,但如果可能,请使用std::shared_ptr而不是boost::shared_ptr. 它在(当前)标准库中,因此无需添加不必要的 boost 依赖项。如果您已经在使用 boost,那也没关系。

你没有具体说明你说的是哪种移动设备,但现代智能手机中的处理器运行在数百或数千兆赫兹,即使是低功耗手机也经常运行 Java 程序(带有垃圾收集)就可以了。共享指针基本上是引用计数的。这不是一项资源密集型活动。

如果您的设备每秒能够实际运行超过 40 次回调,我怀疑共享指针会遇到任何问题。不要过早地优化执行速度。请过早优化安全性和健全性。

于 2012-04-03T15:54:36.857 回答
1

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

我不能说我喜欢这个解决方案,但我认为这源于被调用者拥有调用者的异常情况。

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

于 2012-04-03T16:24:25.510 回答