我意识到这是一个新手问题,但是当我正在努力学习C ++时,我经常对这个表达式“回调”感到厌烦。我用Google搜索并检查了维基百科,但没有找到一个好的探索。我熟悉一些Java和C#,但听起来不太可能,我从来没有真正理解回调的含义。
如果有人知道如何向一个简单的门外汉解释这个词,我会非常感激。
答案 0 :(得分:16)
我熟悉一些Java和C#
回调是这些语言中的事件或委托 - 一种在其上下文中由其他人的代码运行您的代码的方法。因此,术语“回调”:
规范示例是具有用户定义的比较函数(回调)的排序例程。给出一个排序例程,例如:
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中解释它们更容易。 ;)
答案 1 :(得分:5)
当你发送回调函数时,你会发送一个寻址函数的方式(例如,C ++中的函数指针),这样你发送它的代码就可以调用该函数之后,当它完成一些过程时。
之间的区别
start_some_process(some_value, some_function()) # not using a callback
和
start_some_process(some_value, some_function) # using a callback
是在第一个实例中您发送了该函数的结果,而在第二个实例中,您正在发送函数本身。
答案 2 :(得分:3)
这有您的答案和代码参考:Callback
答案 3 :(得分:3)
回调是执行代码的一个钩子,允许您在流程中的已知点提供自定义功能。它允许通用控制结构执行由您的代码中指定的自定义操作,因此术语“回调” - 它会回调到您的代码中。
通常,这是通过提供具有特定预定义签名的函数指针来完成的,执行回调的通用代码会将参数传递给您的函数并期望返回某种类型的值。
这是一个非常强大的模式,允许代码重用和非常简单的自定义/扩展,而无需完全重写。
自定义排序功能就是一个很好的例子。排序算法是通用的,比较功能特定于正在排序的内容。许多算法允许您提供一个函数,它接受泛型类型的两个参数,作为要比较的对象,并期望您根据比较结果返回+ ve,-ve或零值。
然后,您自己编写comaprison函数,并提供一个函数指针,指向在排序过程中“回调”的排序算法。
答案 4 :(得分:2)
用最简单的术语来说,回调是一种传递给另一种方法的代码。
E.g。你有一个类A,它在类B上调用一个方法,但是当它完成时你需要从类A运行一些代码。你将代码放在A类的自己的新方法中,并在类B上调用方法时传递方法名。当B类的方法完成它的东西时,它可以“回调”到A类。
现在,您实际上并不需要将回调代码放在自己的方法中:您可以使用匿名方法和lambda表达式。我认为在你得到它之前学习使用匿名方法可能是最不容易混淆的(至少在C#中)。
祝你好运!P.S。我是一样的:在我真正理解它之前,我在编写C#多年。
答案 5 :(得分:0)
在C ++中查看boost :: function
答案 6 :(得分:0)
我在另一个问题上发布了这个答案,但它似乎同样适用于此。
以下是从(大致)最灵活到最不实现C ++实现回调的方法:
列出了几个信号和插槽实现here(特别是Boost.Signal)。这些对于实现Observer设计模式很有用,其中多个对象有兴趣接收通知。
您可以注册boost::function
回调。 boost::function
是任何可调用实体的包装:自由函数,静态函数,成员函数或函数对象。要包装成员函数,请使用boost::bind
,如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();
}
有一个名为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!";}
};
您注册了一个指向回调自由函数的指针,以及一个额外的“上下文”无效指针。在回调函数中,将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信号。