什么是回调? 它的用途是什么以及如何在例如 C++ 中实现?

发布于 2024-07-30 08:57:51 字数 164 浏览 9 评论 0原文

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

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

I realise this is a newbie question, but as I'm trying to learn C++ I'm stumpling upon this expression "callback" frequently. I've googled it and checked wikipedia, but without finding a good explination. I am familiar with some Java and C#, but how unlikely it sounds, I have never really understood what a callback means.

If someone know how to explain this term to a simple layman, I would be really thankfull.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

此生挚爱伱 2024-08-06 08:57:51

我熟悉一些Java和C#

回调是这些语言中的事件或委托 - 一种让您的代码在其上下文中由其他人的代码运行的方法。 因此,术语“回调”:

  1. 您调用其他一段代码
  2. 它运行,也许计算一个中间值
  3. 它回调到您的代码,也许给您该中间值
  4. 将控制权传递回给您

它继续运行,最终通过完成规范 示例是带有用户定义的比较函数(回调)的排序例程。 给定一个排序例程,例如:(

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 */
   }
}

如何执行排序的细节并不重要 - 只需关注以下事实:任何排序算法都需要比较 2 个值并确定哪个值更高。

) ,现在我们可以定义一些比较函数:

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;
}

并与它们重用相同的排序函数:

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

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

如果您使用成员函数,事情会变得有点复杂,因为您必须管理 this 指针 - 但概念是一样的。 与大多数事情一样,首先用 C 语言解释它们会更容易。 ;)

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. ;)

我的影子我的梦 2024-08-06 08:57:51

当您向回调发送某些内容时,您会以一种寻址的方式发送函数(例如,C++ 中的函数指针),以便您将其发送到的代码稍后可以调用该函数,当它已经完成了一些过程。

之间的区别

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

start_some_process(some_value, some_function)     # using a callback

在于,在第一个实例中,您发送函数的结果,而在第二个实例中,您发送函数本身

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.

变身佩奇 2024-08-06 08:57:51

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

This has your answer and code references: Callback

撩人痒 2024-08-06 08:57:51

回调是正在执行的代码中的挂钩,允许您在流程中的已知点提供自定义功能。 它允许通用控制结构执行由从其中调用的代码指定的自定义操作,因此术语“回调” - 它回调到您的代码中。

通常,这是通过提供具有特定预定义签名的函数指针来完成的,并且执行回调的通用代码将参数传递给您的函数并期望某种类型的返回值。

这是一个非常强大的模式,允许代码重用和相当简单的定制/扩展,而无需完全重写。

自定义排序函数就是一个很好的例子。 排序算法是通用的,比较函数特定于正在排序的内容。 许多算法允许您提供一个函数,该函数接受两个泛型类型的参数(作为要比较的对象),并期望您根据比较结果返回 +ve、-ve 或零值。

然后,您自己编写 comaprison 函数,并提供一个指向排序算法的函数指针,该算法在排序过程中“回调”。

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.

沉默的熊 2024-08-06 08:57:51

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

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

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

祝你好运!

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

In the simplest terms a callback is a code that you pass into another method.

E.g. you have a class A which calls a method on class B but you need some code running from Class A when it's finished. You put your code in its own new method on class A and you pass the method name in when you call the method on class B. When the method on class B has done its stuff it can 'call back' into class A.

Nowadays, you don't actually need to put the callback code in its own method: you've got anonymous methods and lambda expressions you can use. I think it's probably least confusing (at least, in C#) to learn using anonymous methods until you get it.

Good luck!

P.S. I was the same: I was coding C# for years before I really understood them.

傾城如夢未必闌珊 2024-08-06 08:57:51

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

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

: )

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

Boost.Function

你可以注册一个 boost::function< /code>回调。 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();
}

Fast Delegate

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

标准还支持不可能的快速 C++ 委托,但不支持由所有编译器。

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

您可以注册一个指向从回调接口(抽象类)派生的对象的指针。 这是执行回调的传统 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 指针。 在回调函数中,您将 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();
}

基准测试

我发现了一些性能基准测试:

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

从数字中可以看出,Boost 信号必须每秒调用 10,000 到 100,000 次,性能才会成为问题。

I posted this answer for another question, but it seems to be just as applicable here.

Here are ways to implement callbacks in C++ from (roughly) most flexible to least:

Signals & slots

Several signals and slots implementations are listed here (notably Boost.Signal). These are useful to implement the Observer design pattern where more than one object is interested in receiving notifications.

Boost.Function

You can register a boost::function callback. boost::function is wrapper around any callable entity: free function, static function, member function, or function object. To wrap a member function, you use boost::bind as shown in this example. Usage example:

#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();
}

Fast Delegate

There is a delegate implementation, called FastDelegate that is faster than boost::function. It uses an "ugly hack" that is not supported by the C++ standard, but is supported by practically all compilers.

There is also The Impossibly Fast C++ Delegates that is supported by the standard, but not by all compilers.

"Listener" interfaces (abstract classes)

You can register a pointer to an object derived from a callback interface (abstract class). This is the traditional Java way of doing callbacks. Example:

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-style callbacks

You register a pointer to a callback free function, plus an additional "context" void pointer. In the callback function, you cast the void* to the object type that will handle the event, and invoke the proper method. For example:

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();
}

Benchmarks

I have found some performance benchmarks:

They should give you an idea of what callback mechanism is appropriate for different performance requirements.

As can be seen by the numbers, Boost signals would have to be invoked 10,000 to 100,000 of times per second before performance even becomes an issue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文