当前位置:  开发笔记 > 编程语言 > 正文

使用功能指针的好处

如何解决《使用功能指针的好处》经验,为你挑选了5个好方法。

我已经编程了几年了,并且在某些情况下使用了函数指针.我想知道的是,出于性能原因何时使用它们是合适的,我的意思是在游戏环境中,而不是商业软件.

函数指针很快,John Carmack在Quake和Doom源代码中使用了它们,因为他是天才:)

我想更多地使用函数指针,但我想在最适合的地方使用它们.

现在,在C,C++,C#和Java等现代c风格语言中,函数指针的最佳和最实用的用途是什么?



1> jalf..:

关于函数指针没有什么特别"快"的东西.它们允许您调用在运行时指定的函数.但是你有来自任何其他函数调用(加上额外的指针间接)的完全相同的开销.此外,由于要在运行时确定要调用的函数,因此编译器通常不能像在其他任何地方那样内联函数调用.因此,在某些情况下,函数指针可能比常规函数调用慢得多.

函数指针与性能无关,永远不应该用于获得性能.

相反,它们对函数式编程范例非常轻微,因为它们允许您在另一个函数中传递函数作为参数或返回值.

一个简单的例子是通用排序功能.它必须有一些方法来比较两个元素,以确定它们应该如何排序.这可能是传递给sort函数的函数指针,实际上c ++的std :: sort()可以完全像这样使用.如果要求它对未定义小于运算符的类型的序列进行排序,则必须传入可调用的函数指针以执行比较.

这使我们很好地找到了一个更好的选择.在C++中,您不仅限于函数指针.您经常使用仿函数 - 即,重载operator()的类,以便它们可以被"调用",就好像它们是函数一样.与函数指针相比​​,函子有几个很大的优点:

它们提供了更大的灵活性:它们是完整的类,包含构造函数,析构函数和成员变量.它们可以维护状态,并且它们可以暴露周围代码可以调用的其他成员函数.

它们更快:与函数指针不同,函数指针的类型只编码函数的签名(类型的变量void (*)(int)可以是任何接受int并返回void的函数.我们无法知道哪一个),函子的类型编码精确的函数应该被调用(因为仿函数是一个类,称之为C,我们知道要调用的函数是,并且将始终是C :: operator()).这意味着编译器可以内联函数调用.这是使通用std :: sort与专为您的数据类型设计的手动编码排序功能一样快的神奇之处.编译器可以消除调用用户定义函数的所有开销.

它们更安全:函数指针中的类型安全性非常小.您无法保证它指向有效的功能.它可能是NULL.指针的大多数问题也适用于函数指针.它们很危险且容易出错.

函数指针(在C中)或仿函数(在C++中)或委托(在C#中)都解决了同样的问题,具有不同的优雅和灵活性:它们允许您将函数视为一等值,将它们传递给您任何其他变量.您可以将函数传递给另一个函数,它将在指定的时间调用您的函数(当计时器到期时,窗口需要重绘时,或者需要比较数组中的两个元素时)

据我所知(我可能错了,因为我多年没有使用过Java),Java没有直接的等价物.相反,您必须创建一个实现接口的类,并定义一个函数(例如,将其称为Execute()).然后调用foo.Execute()而不是调用用户提供的函数(以函数指针,仿函数或委托的形式).类似于原则上的C++实现,但没有C++模板的通用性,并且没有允许您以相同方式处理函数指针和函子的函数语法.

这就是你使用函数指针的地方:当更复杂的替代品不可用时(即你被困在C中),你需要将一个函数传递给另一个函数.最常见的情况是回调.您可以定义希望系统在X发生时调用的函数F. 因此,您创建一个指向F的函数指针,并将其传递给相关系统.

所以,真的,忘掉约翰卡马克,不要以为你在他的代码中看到的任何东西都会神奇地使你的代码更好,如果你复制它.他使用了函数指针,因为你提到的游戏是用C编写的,其中没有优秀的替代品,而不是因为它们是一些神奇的成分,它们的存在使代码运行得更快.



2> jheriko..:

如果在运行时(例如CPU功能,可用内存)之前您不知道目标平台支持的功能,它们将非常有用.显而易见的解决方案是编写如下函数:

int MyFunc()
{
  if(SomeFunctionalityCheck())
  {
    ...
  }
  else
  {
    ...
  }
}

如果在重要循环内部调用此函数,那么最好使用MyFunc的函数指针:

int (*MyFunc)() = MyFunc_Default;

int MyFunc_SomeFunctionality()
{
  // if(SomeFunctionalityCheck())
  ..
}

int MyFunc_Default()
{
  // else
  ...
}

int MyFuncInit()
{
  if(SomeFunctionalityCheck()) MyFunc = MyFunc_SomeFunctionality;
}

当然还有其他用途,如回调函数,从内存执行字节代码或创建解释语言.

在Windows上执行Intel兼容的字节代码,这可能对解释器有用.例如,这里是一个stdcall函数,返回42(0x2A)存储在一个可以执行的数组中:

code = static_cast(VirtualAlloc(0, 6, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
// mov eax, 42
code[0] = 0x8b;
code[1] = 0x2a;
code[2] = 0x00;
code[3] = 0x00;
code[4] = 0x00;
// ret
code[5] = 0xc3;
// this line executes the code in the byte array
reinterpret_cast(code)();

...

VirtualFree(code, 6, MEM_RELEASE);

);


非常有见地,感谢代码示例

3> Jonathan All..:

无论何时在C#中使用事件处理程序或委托,您实际上都在使用函数指针.

不,他们不是速度.函数指针是关于方便的.

乔纳森



4> Kyle Walsh..:

在许多情况下,函数指针用作回调.一种用途是作为排序算法中的比较函数.因此,如果您尝试比较自定义对象,则可以提供指向知道如何处理该数据的比较函数的函数指针.

那就是说,我将从我以前的一位教授那里得到一句话:

对待一个新的C++功能就像你在一个拥挤的房间里对待一个装载的自动武器:永远不要使用它只是因为它看起来很漂亮.等到你明白后果,不要变得可爱,写下你所知道的,并知道你写的是什么.



5> 小智..:

现在,现代c风格语言中整数的最佳和最实用的用途是什么?

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有