在c++中,何时以及如何使用回调函数?
编辑: 我想看一个简单的例子来写一个回调函数。
在c++中,何时以及如何使用回调函数?
编辑: 我想看一个简单的例子来写一个回调函数。
当前回答
C语言中也有回调的方法:函数指针
// Define a type for the callback signature,
// it is not necessary but makes life easier
// Function pointer called CallbackType that takes a float
// and returns an int
typedef int (*CallbackType)(float);
void DoWork(CallbackType callback)
{
float variable = 0.0f;
// Do calculations
// Call the callback with the variable, and retrieve the
// result
int result = callback(variable);
// Do something with the result
}
int SomeCallback(float variable)
{
int result;
// Interpret variable
return result;
}
int main(int argc, char ** argv)
{
// Pass in SomeCallback to the DoWork
DoWork(&SomeCallback);
}
现在,如果你想将类方法作为回调函数传入,对这些函数指针的声明会有更复杂的声明,例如:
// Declaration:
typedef int (ClassName::*CallbackType)(float);
// This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
// Class instance to invoke it through
ClassName objectInstance;
// Invocation
int result = (objectInstance.*callback)(1.0f);
}
//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
// Class pointer to invoke it through
ClassName * pointerInstance;
// Invocation
int result = (pointerInstance->*callback)(1.0f);
}
int main(int argc, char ** argv)
{
// Pass in SomeCallback to the DoWork
DoWorkObject(&ClassName::Method);
DoWorkPointer(&ClassName::Method);
}
其他回答
回调函数是传递给例程的方法,在某个时刻被传递给它的例程调用。
这对于开发可重用软件非常有用。例如,许多操作系统API(如Windows API)大量使用回调。
例如,如果你想处理文件夹中的文件,你可以用你自己的例程调用一个API函数,并且你的例程在指定的文件夹中对每个文件运行一次。这使得API非常灵活。
公认的答案是全面的,但与问题有关,我只是想在这里放一个简单的例子。我有一个很久以前写的代码。我想以有序的方式遍历树(左节点然后根节点然后右节点),每当我到达一个节点,我希望能够调用任意函数,这样它就可以做任何事情。
void inorder_traversal(Node *p, void *out, void (*callback)(Node *in, void *out))
{
if (p == NULL)
return;
inorder_traversal(p->left, out, callback);
callback(p, out); // call callback function like this.
inorder_traversal(p->right, out, callback);
}
// Function like bellow can be used in callback of inorder_traversal.
void foo(Node *t, void *out = NULL)
{
// You can just leave the out variable and working with specific node of tree. like bellow.
// cout << t->item;
// Or
// You can assign value to out variable like below
// Mention that the type of out is void * so that you must firstly cast it to your proper out.
*((int *)out) += 1;
}
// This function use inorder_travesal function to count the number of nodes existing in the tree.
void number_nodes(Node *t)
{
int sum = 0;
inorder_traversal(t, &sum, foo);
cout << sum;
}
int main()
{
Node *root = NULL;
// What These functions perform is inserting an integer into a Tree data-structure.
root = insert_tree(root, 6);
root = insert_tree(root, 3);
root = insert_tree(root, 8);
root = insert_tree(root, 7);
root = insert_tree(root, 9);
root = insert_tree(root, 10);
number_nodes(root);
}
Boost的signals2允许您以线程安全的方式订阅泛型成员函数(没有模板!)
示例:文档-视图信号可用于实现灵活 文档视图架构。该文档将包含到的信号 每个视图都可以连接。下面的文档类 定义支持多视图的简单文本文档。请注意, 它存储了一个信号,所有视图都将连接到这个信号。
class Document
{
public:
typedef boost::signals2::signal<void ()> signal_t;
public:
Document()
{}
/* Connect a slot to the signal which will be emitted whenever
text is appended to the document. */
boost::signals2::connection connect(const signal_t::slot_type &subscriber)
{
return m_sig.connect(subscriber);
}
void append(const char* s)
{
m_text += s;
m_sig();
}
const std::string& getText() const
{
return m_text;
}
private:
signal_t m_sig;
std::string m_text;
};
接下来,我们可以开始定义视图。下面的TextView类 提供文档文本的简单视图。
class TextView
{
public:
TextView(Document& doc): m_document(doc)
{
m_connection = m_document.connect(boost::bind(&TextView::refresh, this));
}
~TextView()
{
m_connection.disconnect();
}
void refresh() const
{
std::cout << "TextView: " << m_document.getText() << std::endl;
}
private:
Document& m_document;
boost::signals2::connection m_connection;
};
公认的答案是非常有用和相当全面的。然而,OP声明
我想看一个简单的例子来写一个回调函数。
从c++ 11开始,你有std::function,所以不需要函数指针和类似的东西:
#include <functional>
#include <string>
#include <iostream>
void print_hashes(std::function<int (const std::string&)> hash_calculator) {
std::string strings_to_hash[] = {"you", "saved", "my", "day"};
for(auto s : strings_to_hash)
std::cout << s << ":" << hash_calculator(s) << std::endl;
}
int main() {
print_hashes( [](const std::string& str) { /** lambda expression */
int result = 0;
for (int i = 0; i < str.length(); i++)
result += pow(31, i) * str.at(i);
return result;
});
return 0;
}
顺便说一下,这个例子在某种程度上是真实的,因为您希望使用哈希函数的不同实现来调用print_hashes函数,为此我提供了一个简单的例子。它接收一个字符串,返回一个int(提供的字符串的哈希值),所有你需要记住的语法部分是std::function<int (const std::string&)>,它将这样的函数描述为将调用它的函数的输入参数。
Scott Meyers举了一个很好的例子:
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter
{
public:
typedef std::function<int (const GameCharacter&)> HealthCalcFunc;
explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
: healthFunc(hcf)
{ }
int healthValue() const { return healthFunc(*this); }
private:
HealthCalcFunc healthFunc;
};
我认为这个例子说明了一切。
std::function<>是编写c++回调函数的“现代”方式。