C# 使用函数指针的好处

C# 使用函数指针的好处,c#,c++,c,programming-languages,function-pointers,C#,C++,C,Programming Languages,Function Pointers,我已经编程几年了,在某些情况下使用了函数指针。我想知道的是,出于性能原因,什么时候使用它们合适还是不合适?我的意思是在游戏环境中,而不是在商业软件中 函数指针速度很快,John Carmack在《地震与毁灭》源代码中使用了它们,甚至滥用了它们,因为他是个天才:) 我想更多地使用函数指针,但我想在最合适的地方使用它们 这些函数指针在C++、C++、C语言和java等现代C语言中的最好和最实用的用法是什么? < p>在C语言中,只要你使用事件处理程序或委托,就可以有效地使用函数指针。 不,它们与速度

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

函数指针速度很快,John Carmack在《地震与毁灭》源代码中使用了它们,甚至滥用了它们,因为他是个天才:)

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


这些函数指针在C++、C++、C语言和java等现代C语言中的最好和最实用的用法是什么?

< p>在C语言中,只要你使用事件处理程序或委托,就可以有效地使用函数指针。 不,它们与速度无关。函数指针是关于方便性的


Jonathan

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

也就是说,我将引用我从前的一位教授的话:

对待一个新的C++特性,就像你在拥挤的房间里对待装载的自动武器一样:永远不要因为它看起来漂亮而使用它。等到你明白了后果,不要变得可爱,写你知道的,知道你写的

说到C#,函数指针在C#上到处都有使用。委托和事件(以及Lambdas等)都是隐藏的函数指针,因此几乎所有C#项目都会被函数指针所困扰。基本上,每个事件处理程序、每个LINQ查询附近的事件处理程序等都将使用函数指针

函数指针很快

在什么情况下?与之相比

听起来您只是想使用函数指针来使用它们。那太糟糕了


指向函数的指针通常用作回调或事件处理程序。

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

如果您在运行时之前不知道目标平台支持的功能(例如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上执行,这对于解释器可能很有用。例如,下面是一个stdcall函数,返回存储在数组中的42(0x2A),该数组可以执行:

code = static_cast<unsigned char*>(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<unsigned int (_stdcall *)()>(code)();

...

VirtualFree(code, 6, MEM_RELEASE);
code=static_cast(VirtualAlloc(0,6,MEM_COMMIT | MEM_RESERVE,PAGE_EXECUTE_READWRITE));
//mov eax,42岁
代码[0]=0x8b;
代码[1]=0x2a;
代码[2]=0x00;
代码[3]=0x00;
代码[4]=0x00;
//ret
代码[5]=0xc3;
//此行执行字节数组中的代码
重新解释(代码)();
...
VirtualFree(代码6,MEM_发布版);

))

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


函数指针与性能无关,不应用于提高性能

相反,它们是对函数式编程范式的一个非常轻微的点头,因为它们允许您将函数作为参数或返回值传递给另一个函数

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

这让我们很好地找到了一个更好的选择。在C++中,你不局限于函数指针。您通常使用函子,即重载运算符
()
的类,以便可以像调用函数一样“调用”它们。与函数指针相比,函子有两大优势:

  • 它们提供了更大的灵活性:它们是成熟的类,具有构造函数、析构函数和成员变量。它们可以维护状态,并且可以公开周围代码可以调用的其他成员函数
  • 它们的速度更快:与函数指针不同,函数指针的类型只编码函数的签名(类型为
    void(*)(int)
    的变量可能是任何接受int并返回void的函数。我们不知道是哪一个函数),函子的类型编码应该调用的精确函数(因为函子是一个类,所以称它为C,我们知道要调用的函数是,并且永远是,
    C::operator()
    )。这意味着编译器可以内联函数调用。这就是使泛型
    std::sort
    与专门为数据类型设计的手动编码排序函数一样快的魔力。编译器可以消除调用用户定义函数的所有开销
  • 它们更安全:函数指针中几乎没有类型安全性。您无法保证它指向有效的函数。它可能为NULL。指针的大多数问题也适用于函数指针。它们很危险,而且容易出错
函数指针(C)或函子(C)
BOOL EnumWindows(          
    WNDENUMPROC lpEnumFunc,
    LPARAM lParam
);
switch(sample_var)
{

case 0:
          func1(<parameters>);
          break;

case 1:
          func2(<parameters>);
          break;



up to case n:
          funcn(<parameters>);
          break;

}