函数指针的作用是什么?
我很难看到函数指针的效用。 我想在某些情况下它们可能是有用的(毕竟它们是存在的),但我不能想到使用函数指针更好或者不可避免的情况。
你可以给一些很好的使用函数指针的例子(在C或C ++中)?
大多数例子归结为callback :你调用函数f()
传递另一个函数g()
的地址, f()
调用g()
来执行某个特定的任务。 如果你传递f()
的地址h()
来代替,那么f()
会调用回h()
。
基本上,这是一种参数化函数的方法:其行为的一部分不是硬编码到f()
,而是硬编码到callback函数中。 调用者可以通过传递不同的callback函数来使f()
行为不同。 经典的是来自C标准库的qsort()
,它将其sorting标准作为指向比较函数的指针。
在C ++中,这通常使用函数对象 (也称为函子)来完成。 这些是重载函数调用操作符的对象,因此可以将它们称为函数。 例:
class functor { public: void operator()(int i) {std::cout << "the answer is: " << i << '\n';} }; functor f; f(42);
这背后的想法是,与函数指针不同,函数对象不仅可以携带algorithm,还可以携带数据:
class functor { public: functor(const std::string& prompt) : prompt_(prompt) {} void operator()(int i) {std::cout << prompt_ << i << '\n';} private: std::string prompt_; }; functor f("the answer is: "); f(42);
另一个优点是有时候比通过函数指针调用内联调用函数对象更容易。 这是为什么在C ++中sorting有时比在C中sorting更快的原因
那么,我通常在跳转表中使用它们(专业)(请参阅此StackOverflow问题 )。
跳转表通常(但不是唯一的)在有限状态机中使用,以使其数据驱动。 而不是嵌套的开关/大小写
switch (state) case A: switch (event): case e1: .... case e2: .... case B: switch (event): case e3: .... case e1: ....
你可以做一个2D数组或函数指针,只需调用handleEvent[state][event]
例子:
- 自定义sorting/search
- 不同的模式(如战略,观察员)
- callback
函数指针的用处的“经典”例子是C库qsort()
函数,它实现了快速sorting。 为了对用户可能想到的任何和所有数据结构都是通用的,它需要一些无效指针来分类数据和一个指向知道如何比较这些数据结构的两个元素的函数的指针。 这使得我们可以创build我们的工作selectfunction,事实上甚至可以在运行时select比较function,例如用于sorting升序或降序。
我最近使用了函数指针来创build一个抽象层。
我有一个用纯C编写的程序,可以在embedded式系统上运行。 它支持多种硬件变种。 根据我运行的硬件,它需要调用一些函数的不同版本。
在初始化时,程序会计算出正在运行的硬件并填充函数指针。 程序中的所有更高级别的例程只是调用指针引用的函数。 我可以添加对新硬件变体的支持,而不需要触及更高级别的例程。
我曾经使用switch / case语句来select正确的函数版本,但随着程序越来越多地支持越来越多的硬件变体,这变得不切实际。 我必须在所有地方添加个案陈述。
我也尝试了中间function层来找出使用哪个函数,但是他们没有什么帮助。 每当我们添加新的变体时,我仍然需要在多个地方更新病例陈述。 使用函数指针,我只需要改变初始化函数。
在C中,经典用法是qsort函数 ,其中第四个参数是指向用于在sorting中执行sorting的函数的指针。 在C ++中,人们倾向于使用函子(看起来像函数的对象)来处理这种事情。
同意以上所有,再加上….当您在运行时dynamic加载DLL时,您将需要函数指针来调用函数。
我将在这里逆stream而上。
在C中,函数指针是实现定制的唯一方法,因为没有OO。
在C ++中,可以使用函数指针或函子(函数对象)来获得相同的结果。
与原始函数指针相比,函子有很多优点,由于它们的对象性质,特别是:
- 他们可能会出现一些
operator()
重载operator()
- 他们可以有状态/参考现有的variables
- 他们可以在现场(
lambda
和bind
)
我个人比较喜欢函子指针(尽pipe是样板代码),主要是因为函数指针的语法可以很容易地得到(从函数指针教程 ):
typedef float(*pt2Func)(float, float); // defines a symbol pt2Func, pointer to a (float, float) -> float function typedef int (TMyClass::*pt2Member)(float, char, char); // defines a symbol pt2Member, pointer to a (float, char, char) -> int function // belonging to the class TMyClass
我唯一见过的函数指针在Boost.Spirit中无法使用。 他们完全滥用语法来传递任意数量的参数作为单个模板参数。
typedef SpecialClass<float(float,float)> class_type;
但是,由于可变参数模板和lambdas在angular落,我不确定我们现在将长时间在纯C ++代码中使用函数指针。
函数指针可以在C中用来创build一个接口来编程。 根据运行时所需的特定function,可以将不同的实现分配给函数指针。
我的主要用途是CALLBACKS:当你需要保存某个函数的信息以后再调用 。
假设你正在写炸弹人。 人员放下炸弹5秒后,应该爆炸(称explode()
函数)。
现在有两种方法来做到这一点。 一种方法是通过“探测”屏幕上的所有炸弹来查看它们是否准备在主循环中爆炸。
foreach bomb in game if bomb.boomtime() bomb.explode()
另一种方法是将callback附加到您的时钟系统。 当一颗炸弹被种植的时候,当时机成熟的时候 ,你会添加一个callback让它调用bomb.explode() 。
// user placed a bomb Bomb* bomb = new Bomb() make callback( function=bomb.explode, time=5 seconds ) ; // IN the main loop: foreach callback in callbacks if callback.timeToRun callback.function()
这里的callback.function()
可以是任何函数 ,因为它是一个函数指针。
就像上面所说的Rich一样,Windows中的函数指针通常引用一些存储函数的地址。
当你在Windows平台上用C language
进行编程时,你基本上会在主内存中加载一些DLL文件(使用LoadLibrary
),并使用存储在DLL中的函数来创build函数指针并指向这些地址(使用GetProcAddress
)。
参考文献:
-
调用LoadLibrary
-
GetProcAddress的
我广泛使用函数指针来模拟具有1字节操作码的微处理器。 一个256个函数指针的数组是实现这个的自然方法。
函数指针的一个用法可能是我们可能不想修改函数被调用的代码(这意味着调用可能是有条件的,在不同的条件下,我们需要做不同的处理)。 这里函数指针是非常方便的,因为我们不需要在函数被调用的地方修改代码。 我们只需使用具有适当参数的函数指针调用该函数即可。 可以使函数指针有条件地指向不同的函数。 (这可以在初始化阶段的某个地方完成)。 而且上面的模型是非常有帮助的,如果我们不能修改它被调用的代码(假设它是一个我们不能修改的库API)。 API使用函数指针来调用适当的用户定义的函数。
对于OO语言来说,在后台执行多态调用(这对我猜到的C也是有效的)。
而且,它们在运行时向另一个函数(foo)注入不同的行为是非常有用的。 这使得函数具有更高阶的function。 除了它的灵活性之外,这使得foo代码更具可读性,因为它可以让你从其中抽取额外的“if-else”逻辑。
它使Python中的许多其他有用的东西像发生器,闭包等
不同的angular度,除了这里的其他好的答案:
在C中, 只有函数指针,没有任何函数。
我的意思是,你写函数,但是你不能操作函数。 没有像这样的函数的运行时表示。 你甚至不能调用“一个函数”。 当你写:
my_function(my_arg);
你实际上说的是“用指定参数执行对my_function
指针的调用”。 您正在通过函数指针进行调用。 这个衰减到函数指针意味着下面的命令等同于前面的函数调用:
(&my_function)(my_arg); (*my_function)(my_arg); (**my_function)(my_arg); (&**my_function)(my_arg); (***my_function)(my_arg);
等等(谢谢@LuuVinhPhuc)。
所以,你已经使用函数指针作为值 。 显然你会想要这些值的variables – 这里是其他所有用途的用法:多态/定制(如在qsort中),callback,跳转表等
在C ++中,事情有点复杂,因为我们有lambdaexpression式,并且有operator()
对象,甚至是一个std::function
类,但是原理大体上是一样的。