C++ 为什么要使用函数指针?

C++ 为什么要使用函数指针?,c++,c,programming-languages,C++,C,Programming Languages,我希望这是一个极其重复的问题。我提前向所有觉得这很烦人的观众道歉 虽然我是一位经验丰富的程序员,但我不能证明在直接调用上使用函数指针是合理的。我无法找到差异的场景是- 1) 回调-直接调用也可以实现同样的效果 2) 异步或同步事件处理-无论如何,必须根据更新的函数指针数组中的元素号来识别事件。但同样的事情也可以通过直接通话来完成 3) 在一些帖子中,我看到有人评论说,当它不知道调用哪个函数时,它将被使用。我没有得到任何适当的理由 如果有人能用实际且非常简单的现实例子来解释我使用上述场景,我将不胜

我希望这是一个极其重复的问题。我提前向所有觉得这很烦人的观众道歉

虽然我是一位经验丰富的程序员,但我不能证明在直接调用上使用函数指针是合理的。我无法找到差异的场景是-

1) 回调-直接调用也可以实现同样的效果

2) 异步或同步事件处理-无论如何,必须根据更新的函数指针数组中的元素号来识别事件。但同样的事情也可以通过直接通话来完成


3) 在一些帖子中,我看到有人评论说,当它不知道调用哪个函数时,它将被使用。我没有得到任何适当的理由


如果有人能用实际且非常简单的现实例子来解释我使用上述场景,我将不胜感激。

编程中有一个概念叫DRY——不要重复你自己

假设您的UI中有121个按钮。它们中的每一个的行为几乎相同,只是当您按下按钮时,会发生不同的操作

您可以(A)使用虚拟继承来分派到正确的操作(每个按钮需要一个类),或者(B)使用存储在类中的函数指针(或者
std::function
)来调用右“点击”处理程序,或者(C)使每个按钮都是不同的类型

我所研究的每个编译器中都实现了一个虚拟函数,它是一个复杂的表,最终是函数指针的集合

因此,您可以选择函数指针或生成121个完全不同的按钮,这些按钮的行为几乎相同

在任何情况下,如果希望将调用方和被调用方解耦,必须使用类似于函数指针的东西。从工作队列到线程关闭任务、回调等,情况多得离谱

在所有东西都是硬编码的小程序中,硬编码每个调用都可以工作。但是像这样硬编码的东西是不可伸缩的。当您想要更新每只手实现的121个按钮时,了解它们的自定义点将是非常困难的。它们将失去同步

121是数量适中的按钮。一个有10000个的应用程序呢?你想更新每个按钮的行为来处理基于触摸的输入吗

更重要的是,当您键入erase时,可以显著减小二进制大小。121个实现按钮的类的副本将占用比1个类更多的可执行空间,每个类存储一个或两个函数指针

函数指针只是“类型擦除”的一种类型。类型擦除减少了二进制大小,在提供者和使用者之间提供了更清晰的契约,并使围绕类型擦除的数据重构行为更容易

1) 回调-同样可以通过直接调用来实现

并非在所有情况下,因为调用方可能不知道在编译时必须调用什么函数。例如,这在库中是典型的,因为它们无法预先知道您的代码

但是,这也可能发生在您自己的代码中:每当您希望部分重用函数时,您可以:

  • 创建该函数的多个版本,每个版本调用不同的函数。代码重复,维护很差。除非代码膨胀,否则性能良好
  • 传递函数指针(或在C++中通常可调用)。在某些情况下,灵活、代码更少的性能可能会受到影响
  • 创建一组分支(
    if
    /
    switch
    chain),前提是您事先知道要调用的一组可能函数。刚性,但对于少量分支,可能比函数指针更快
  • 在C++中,创建模板化版本。与第一个案例相同,但自动化;保养得好。代码膨胀可能是一个问题
  • 找出通用代码,以便调用者可以逐个调用他们需要的任何东西。有时这是不可能的/不容易的——特别是当参数化您希望保持可重用的复杂算法时(例如,
    qsort()
    )。在C++中,参见STL(标准模板库)。
2) 异步或同步事件处理-无论如何,必须根据更新的函数指针数组中的元素号来标识事件。但同样的事情也可以通过直接通话来完成

某些事件系统的设计使您可以简单地配置在给定事件发生时将触发哪些功能。如果这是一个带有C接口的外部库,他们别无选择,只能使用函数指针

其他一些系统允许您创建自己的事件循环,您可以以某种方式获取事件并对其执行任何操作;因此,他们避免回调


3) 在一些帖子中,我看到有人评论说,当不知道调用哪个函数时,可以使用它。我没有得到任何正当的理由


请参阅第一种情况。

如果没有函数指针,如何实现计算任何实值函数积分的函数

typedef double (*Function)(double);

double Integral(Function f, double a, double b);
1) 回调-同样可以通过直接调用来实现

不是真的。对于直接调用,调用方在编译代码时必须知道函数名和签名,并且只能调用该函数。回调是在运行时定义的,可以动态更改,而调用方只需要知道签名,而不需要知道名称。此外,对象的每个实例可能具有不同的回调,而对于直接调用,所有实例都必须调用相同的函数

2) 异步或同步事件处理-无论如何,事件必须 已标识,基于函数指针数组中的元素号获取 更新。但同样的事情也可以通过直接通话来完成

不确定您的意思,但事件处理程序只是一种回调。事件可以由调用方和通过指针调用的不同回调处理程序来标识。你的观点只有在t的情况下才成立
int compare (int* a, int* b)
{
  //User defined body based on problem requirement
}
int (*fptr[10]) (void) =
{
    function1;     //function for starting LED
    function2;     //function for relay operation
      .
      .
    function10;    //function for motor control
}
fptr[i]();