10

我意识到这是一个新手问题,但是当我尝试学习 C++ 时,我经常遇到这个表达“回调”的问题。我用谷歌搜索了它并检查了维基百科,但没有找到一个好的解释。我熟悉一些 Java 和 C#,但听起来不太可能,我从来没有真正理解回调的含义。

如果有人知道如何向一个简单的外行解释这个术语,我将非常感激。

4

7 回答 7

16

I am familiar with some Java and C#

A callback is an event or delegate in those languages - a way to get your code run by somebody else's code in it's context. Hence, the term "callback":

  1. You call some other piece of code
  2. It runs, perhaps calculating an intermediate value
  3. It calls back into your code, perhaps giving you that intermediate value
  4. It continues running, eventually passing control back to you by completing

A canonical example is a sort routine with a user defined comparison function (the callback). Given a sort routine such as:

void Sort(void* values, int length, int valueSize, 
          int (*compare)(const void*, const void*) 
{
   for (int i = 0; i < length; i = i + 2) {
      // call the callback to determine order
      int isHigher = compare(values[i], values[i + 1]);
      /* Sort */
   }
}

(The specifics of how the sort is performed isn't important - just focus on the fact that any sorting algorithm needs to compare 2 values and determine which is higher.)

So, now we can define some comparison functions:

int CompareInts(const void* o, const void* p) {
   int* a = (int*) o;
   int* b = (int*) p;

   if (a == b) return 0;
   return (a < b) ? -1 : 1;
}

int ComparePersons(const void* o, const void* p) {
   Person* a = (Person*) o;
   Person* b = (Person*) p;

   if (a == b) return 0;
   return (a->Value() < b=>Value()) ? -1 : 1;
}

And reuse the same sort function with them:

int intValues[10];
Person personValues[10];

Sort(intValues, 10, sizeof(intVaues[0]), CompareInts);
Sort(personValues, 10, sizeof(personVaues[0]), ComparePersons);

Things get a bit more complicated if you're using member functions, as you have to manage the this pointer - but the concept is the same. As with most things, it's easier to explain them in C first. ;)

于 2009-08-12T12:21:13.500 回答
5

When you send something a callback, you send it a way of addressing a function (for example, a function pointer in C++) so that the code you're sending it to can call that function later, when it's completed some process.

The difference between

start_some_process(some_value, some_function())   # not using a callback

and

start_some_process(some_value, some_function)     # using a callback

is that in the first instance you're sending the result of the function, while in the second instance you're sending the function itself.

于 2009-08-12T11:48:55.193 回答
3

这有你的答案和代码参考: 回调

于 2009-08-12T11:47:15.463 回答
3

A callback is a hook into the code that is executing to allow you to provide customised features at known points in the process. It allows for generalised control structures to perform customised operations which are specified by your code which is called from within them, hence the term "call back" - it calls back into your code.

Generally this is done by you providing a function pointer with a specific pre-defined signature and the generic code doing the call back will pass parameters to your function and expect a return value of a certain type.

It is a really powerful pattern which allows for code re-use and quite simple customisation/extension without completely re-writing.

Custom sort functions are a great example. The sort algorithm is generic, the comparison function is specific to what is being sorted. Many algorithms allow you to provide a function which takes two arguments of generic types, being the objects to compare, and expects you to return a +ve, -ve or zero value depending on the result of the compare.

You then write the comaprison function yourself and provide a function pointer to the sort algorithm which it "calls back" during sorting.

于 2009-08-12T11:48:34.663 回答
2

用最简单的术语来说,回调是您传递给另一个方法的代码。

例如,您有一个 A 类,它调用 B 类的一个方法,但是当它完成时,您需要从 A 类运行一些代码。您将代码放在 A 类上自己​​的新方法中,并在调用 B 类上的方法时传递方法名称。当 B 类上的方法完成其工作时,它可以“回调”到 A 类中。

现在,您实际上不需要将回调代码放在它自己的方法中:您可以使用匿名方法和 lambda 表达式。我认为学习使用匿名方法可能最不令人困惑(至少在 C# 中),直到你得到它。

祝你好运!

PS 我也一样:在我真正理解 C# 之前,我已经编写 C# 多年了。

于 2009-08-12T11:52:20.833 回答
0

我为另一个问题发布了这个答案,但它似乎在这里同样适用。

以下是在 C++ 中实现回调的方法,从(大致)最灵活到最不灵活:

信号和插槽

这里列出了几个信号和槽的实现(特别是 Boost.Signal)。这些对于实现多个对象对接收通知感兴趣的观察者设计模式很有用。

升压函数

您可以注册boost::function回调。boost::function是任何可调用实体的包装器:自由函数、静态函数、成员函数或函数对象。要包装成员函数,请使用boost::bind示例中所示的方法。使用示例:

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>

typedef boost::function<void (void)> MouseCallback;

class Mouse
{
public:
    void registerCallback(MouseCallback callback) {callback_ = callback;}

    void notifyClicked() {if (callback_) callback_();}

private:
    MouseCallback callback_;
};

class Foo
{
public:
    void mouseClicked() {std::cout << "Mouse clicked!";}
};

int main()
{
    Mouse mouse;
    Foo foo;
    mouse.registerCallback(boost::bind(&Foo::mouseClicked, &foo));
    mouse.notifyClicked();
}

快速代表

有一个委托实现,称为FastDelegate,它比boost::function. 它使用了 C++ 标准不支持的“丑陋黑客”,但几乎所有编译器都支持。

还有标准支持的The Impossibly Fast C++ Delegates,但并非所有编译器都支持。

“监听器”接口(抽象类)

您可以注册一个指向从回调接口(抽象类)派生的对象的指针。这是进行回调的传统 Java 方式。例子:

class MouseInputListener
{
public:
    virtual void mouseClicked() = 0;
    virtual void mouseReleased() = 0;
};

class Mouse
{
public:
    Mouse() : listener_(0) {}
    void registerListener(MouseInputListener* listener) {listener_ = listener;}
    void notifyClicked() {if (listener_) listener_->mouseClicked();}
    void notifyReleased() {if (listener_) listener_->mouseReleased();}

private:
    MouseInputListener* listener_;
};

class Foo : public MouseInputListener
{
public:
    virtual void mouseClicked() {cout << "Mouse clicked!";}
    virtual void mouseReleased() {cout << "Mouse released!";}
};

C 风格的回调

您注册了一个指向回调自由函数的指针,以及一个额外的“上下文”空指针。在回调函数中,您将void*转换为将处理事件的对象类型,并调用正确的方法。例如:

typedef void (*MouseCallback)(void* context); // Callback function pointer type

class Mouse
{
public:
    Mouse() : callback_(0), context_(0) {}

    void registerCallback(MouseCallback callback, void* context = 0)
        {callback_ = callback; context_ = context;}

    void notifyClicked() {if (callback_) callback_(context_);}

private:
    MouseCallback callback_;
    void* context_;
};

class Foo
{
public:
    void mouseClicked() {cout << "Mouse clicked!";}

    static void callback(void* context)
        {static_cast<Foo*>(context)->mouseClicked();}
};

int main()
{
    Mouse mouse;
    Foo foo;
    mouse.registerCallback(&Foo::callback, &foo);
    mouse.notifyClicked();
}

基准

我发现了一些性能基准:

他们应该让您了解哪种回调机制适合不同的性能要求。

从数字可以看出,在性能甚至成为问题之前,必须每秒调用 10,000 到 100,000 次 Boost 信号。

于 2011-07-21T18:57:33.900 回答
0

在 C++ 中查看 boost::function

http://www.boost.org/doc/libs/1_39_0/doc/html/function.html

于 2009-08-13T02:26:26.740 回答