在c++中,何时以及如何使用回调函数?

编辑: 我想看一个简单的例子来写一个回调函数。


当前回答

在c++中并没有明确的回调函数的概念。回调机制通常通过函数指针、函子对象或回调对象实现。程序员必须显式地设计和实现回调功能。

根据反馈进行编辑:

尽管这个答案收到了负面的反馈,但它并没有错。我会试着更好地解释我的观点。

C和c++拥有实现回调函数所需的一切。实现回调函数最常见和最简单的方法是将函数指针作为函数参数传递。

However, callback functions and function pointers are not synonymous. A function pointer is a language mechanism, while a callback function is a semantic concept. Function pointers are not the only way to implement a callback function - you can also use functors and even garden variety virtual functions. What makes a function call a callback is not the mechanism used to identify and call the function, but the context and semantics of the call. Saying something is a callback function implies a greater than normal separation between the calling function and the specific function being called, a looser conceptual coupling between the caller and the callee, with the caller having explicit control over what gets called. It is that fuzzy notion of looser conceptual coupling and caller-driven function selection that makes something a callback function, not the use of a function pointer.

例如,IFormatProvider的. net文档说“GetFormat是一个回调方法”,即使它只是一个普通的接口方法。我认为没有人会认为所有的虚方法调用都是回调函数。使GetFormat成为回调方法的不是它如何传递或调用的机制,而是调用方选择调用哪个对象的GetFormat方法的语义。

一些语言包含带有显式回调语义的特性,通常与事件和事件处理相关。例如,c#的事件类型的语法和语义显式地围绕回调的概念设计。Visual Basic有它的Handles子句,它显式地将一个方法声明为回调函数,同时抽象了委托或函数指针的概念。在这些情况下,回调的语义概念集成到语言本身中。

另一方面,C和c++几乎没有显式地嵌入回调函数的语义概念。机制在那里,集成的语义不在那里。你可以很好地实现回调函数,但是为了获得更复杂的、包含显式回调语义的东西,你必须在c++提供的基础上构建它,比如Qt对它们的信号和插槽所做的事情。

简而言之,c++拥有实现回调所需的一切,通常使用函数指针非常简单。它没有的是语义特定于回调的关键字和特性,比如raise、emit、Handles、event +=等。如果您来自具有这些类型的元素的语言,那么c++中的本机回调支持将感觉很中性。

其他回答

回调函数是传递给例程的方法,在某个时刻被传递给它的例程调用。

这对于开发可重用软件非常有用。例如,许多操作系统API(如Windows API)大量使用回调。

例如,如果你想处理文件夹中的文件,你可以用你自己的例程调用一个API函数,并且你的例程在指定的文件夹中对每个文件运行一次。这使得API非常灵活。

在c++中并没有明确的回调函数的概念。回调机制通常通过函数指针、函子对象或回调对象实现。程序员必须显式地设计和实现回调功能。

根据反馈进行编辑:

尽管这个答案收到了负面的反馈,但它并没有错。我会试着更好地解释我的观点。

C和c++拥有实现回调函数所需的一切。实现回调函数最常见和最简单的方法是将函数指针作为函数参数传递。

However, callback functions and function pointers are not synonymous. A function pointer is a language mechanism, while a callback function is a semantic concept. Function pointers are not the only way to implement a callback function - you can also use functors and even garden variety virtual functions. What makes a function call a callback is not the mechanism used to identify and call the function, but the context and semantics of the call. Saying something is a callback function implies a greater than normal separation between the calling function and the specific function being called, a looser conceptual coupling between the caller and the callee, with the caller having explicit control over what gets called. It is that fuzzy notion of looser conceptual coupling and caller-driven function selection that makes something a callback function, not the use of a function pointer.

例如,IFormatProvider的. net文档说“GetFormat是一个回调方法”,即使它只是一个普通的接口方法。我认为没有人会认为所有的虚方法调用都是回调函数。使GetFormat成为回调方法的不是它如何传递或调用的机制,而是调用方选择调用哪个对象的GetFormat方法的语义。

一些语言包含带有显式回调语义的特性,通常与事件和事件处理相关。例如,c#的事件类型的语法和语义显式地围绕回调的概念设计。Visual Basic有它的Handles子句,它显式地将一个方法声明为回调函数,同时抽象了委托或函数指针的概念。在这些情况下,回调的语义概念集成到语言本身中。

另一方面,C和c++几乎没有显式地嵌入回调函数的语义概念。机制在那里,集成的语义不在那里。你可以很好地实现回调函数,但是为了获得更复杂的、包含显式回调语义的东西,你必须在c++提供的基础上构建它,比如Qt对它们的信号和插槽所做的事情。

简而言之,c++拥有实现回调所需的一切,通常使用函数指针非常简单。它没有的是语义特定于回调的关键字和特性,比如raise、emit、Handles、event +=等。如果您来自具有这些类型的元素的语言,那么c++中的本机回调支持将感觉很中性。

请参阅上面的定义,其中声明将回调函数传递给其他函数,并在某个时刻调用它。

在c++中,让回调函数调用类方法是可取的。当您这样做时,您可以访问成员数据。如果你使用C语言定义回调函数,你必须将它指向一个静态成员函数。这不是很理想。

Here is how you can use callbacks in C++. Assume 4 files. A pair of .CPP/.H files for each class. Class C1 is the class with a method we want to callback. C2 calls back to C1's method. In this example the callback function takes 1 parameter which I added for the readers sake. The example doesn't show any objects being instantiated and used. One use case for this implementation is when you have one class that reads and stores data into temporary space and another that post processes the data. With a callback function, for every row of data read the callback can then process it. This technique cuts outs the overhead of the temporary space required. It is particularly useful for SQL queries that return a large amount of data which then has to be post-processed.

/////////////////////////////////////////////////////////////////////
// C1 H file

class C1
{
    public:
    C1() {};
    ~C1() {};
    void CALLBACK F1(int i);
};

/////////////////////////////////////////////////////////////////////
// C1 CPP file

void CALLBACK C1::F1(int i)
{
// Do stuff with C1, its methods and data, and even do stuff with the passed in parameter
}

/////////////////////////////////////////////////////////////////////
// C2 H File

class C1; // Forward declaration

class C2
{
    typedef void (CALLBACK C1::* pfnCallBack)(int i);
public:
    C2() {};
    ~C2() {};

    void Fn(C1 * pThat,pfnCallBack pFn);
};

/////////////////////////////////////////////////////////////////////
// C2 CPP File

void C2::Fn(C1 * pThat,pfnCallBack pFn)
{
    // Call a non-static method in C1
    int i = 1;
    (pThat->*pFn)(i);
}

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++回调函数的“现代”方式。

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