C# 使用函数指针的好处

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/667410/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 12:32:47  来源:igfitidea点击:

The Benefits of Using Function Pointers

c#c++cprogramming-languagesfunction-pointers

提问by Brock Woolf

I have been programming for a few years now and have used function pointers in certain cases. What I would like to know is when is it appropriate or not to use them for performance reasons and I mean in the context of games, not business software.

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

Function pointers are fast, John Carmack used them to the extent of abuse in the Quake and Doom source code and because he is a genius :)

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

I would like to use function pointers more but I want to use them where they are most appropriate.

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

These days what are the best and most practical uses of function pointers in modern c-style languages such as C, C++, C# and Java, etc?

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

采纳答案by jalf

There is nothing especially "fast" about function pointers. They allow you to call a function which is specified at runtime. But you have exactly the same overhead as you'd get from any other function call (plus the additional pointer indirection). Further, since the function to call is determined at runtime, the compiler can typically not inline the function call as it could anywhere else. As such, function pointers may in some cases add up to be significantly slower than a regular function call.

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

Function pointers have nothing to do with performance, and should never be used to gain performance.

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

Instead, they are a very slight nod to the functional programming paradigm, in that they allow you to pass a function around as parameter or return value in another function.

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

A simple example is a generic sorting function. It has to have some way to compare two elements in order to determine how they should be sorted. This could be a function pointer passed to the sort function, and in fact c++'s std::sort() can be used exactly like that. If you ask it to sort sequences of a type that does not define the less than operator, you have to pass in a function pointer it can call to perform the comparison.

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

And this leads us nicely to a superior alternative. In C++, you're not limited to function pointers. You often use functors instead - that is, classes that overload the operator(), so that they can be "called" as if they were functions. Functors have a couple of big advantages over function pointers:

这让我们很好地找到了一个更好的选择。在 C++ 中,您不仅限于函数指针。你经常使用函子——也就是说,重载 operator() 的类,这样它们就可以像函数一样被“调用”。与函数指针相比​​,函子有两大优势:

  • They offer more flexibility: they're full-fledged classes, with constructor, destructor and member variables. They can maintain state, and they may expose other member functions that the surrounding code can call.
  • They are faster: unlike function pointers, whose type only encode the signature of the function (a variable of type void (*)(int)may be anyfunction which takes an int and returns void. We can't know which one), a functor's type encodes the precise function that should be called (Since a functor is a class, call it C, we know that the function to call is, and will always be, C::operator()). And this means the compiler can inline the function call. That's the magic that makes the generic std::sort just as fast as your hand-coded sorting function designed specifically for your datatype. The compiler can eliminate all the overhead of calling a user-defined function.
  • They are safer: There's very little type safety in a function pointer. You have no guarantee that it points to a valid function. It could be NULL. And most of the problems with pointers apply to function pointers as well. They're dangerous and error-prone.
  • 它们提供了更多的灵活性:它们是成熟的类,具有构造函数、析构函数和成员变量。它们可以维护状态,并且可以公开周围代码可以调用的其他成员函数。
  • 它们更快:与函数指针不同,函数指针的类型只对函数的签名进行编码(类型变量void (*)(int)可以是任何接受 int 并返回 void 的函数。我们不知道是哪一个),函子的类型对精确的函数进行编码应该被调用的(因为函子是一个类,所以称它为 C,我们知道要调用的函数是并且永远是 C::operator())。这意味着编译器可以内联函数调用。这就是使通用 std::sort 与专为您的数据类型设计的手工编码排序函数一样快的神奇之处。编译器可以消除调用用户定义函数的所有开销。
  • 它们更安全:函数指针几乎没有类型安全性。您不能保证它指向一个有效的函数。它可能为 NULL。指针的大部分问题也适用于函数指针。它们很危险且容易出错。

Function pointers (in C) or functors (in C++) or delegates (in C#) all solve the same problem, with different levels of elegance and flexibility: They allow you to treat functions as first-class values, passing them around as you would any other variable. You can pass a function to another function, and it will call your function at specified times (when a timer expires, when the window needs redrawing, or when it needs to compare two elements in your array)

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

As far as I know (and I could be wrong, because I haven't worked with Java for ages), Java doesn't have a direct equivalent. Instead, you have to create a class, which implements an interface, and defines a function (call it Execute(), for example). And then instead of calling the user-supplied function (in the shape of a function pointer, functor or delegate), you call foo.Execute(). Similar to the C++ implementation in principle, but without the generality of C++ templates, and without the function syntax that allows you to treat function pointers and functors the same way.

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

So that is where you use function pointers: When more sophisticated alternatives are not available (ie. you are stuck in C), and you need to pass one function to another. The most common scenario is a callback. You define a function F that you want the system to call when X happens. So you create a function pointer pointing to F, and pass that to the system in question.

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

So really, forget about John Carmack and don't assume that anything you sees in his code will magically make your code better if you copy it. He used function pointers because the games you mention were written in C, where superior alternatives are not available, and not because they are some magical ingredient whose mere existence makes code run faster.

所以说真的,忘掉 John Carmack 吧,不要假设你在他的代码中看到的任何东西,如果你复制它,就会神奇地使你的代码变得更好。他使用函数指针是因为你提到的游戏是用 C 编写的,没有更好的替代品,而不是因为它们是一些神奇的成分,它们的存在使代码运行得更快。

回答by Jonathan Allen

Any time you use a event handler or delegate in C#, you are effectively using a function pointer.

任何时候在 C# 中使用事件处理程序或委托,都是在有效地使用函数指针。

And no, they are not about speed. Function pointers are about convenience.

不,它们与速度无关。函数指针是为了方便。

Jonathan

乔纳森

回答by Kyle Walsh

Function pointers are used as callbacks in many cases. One use is as a comparison function in sorting algorithms. So if you are trying to compare customized objects, you can provide a function pointer to the comparison function that knows how to handle that data.

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

That said, I'll provide a quote I got from a former professor of mine:

也就是说,我将提供我从我的一位前教授那里得到的报价:

Treat a new C++ feature like you would treat a loaded automatic weapon in a crowded room: never use it just because it looks nifty. Wait until you understand the consequences, don't get cute, write what you know, and know what you write.

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

回答by Reed Copsey

Just speaking of C#, but function pointers are used all over C#. Delegates and Events (and Lambdas, etc) are all function pointers under the hood, so nearly any C# project is going to be riddled with function pointers. Basically every event handler, near every LINQ query, etc - will be using function pointers.

只是说到 C#,但在 C# 中到处都使用函数指针。委托和事件(以及 Lambda 等)都是幕后的函数指针,因此几乎所有 C# 项目都将充斥着函数指针。基本上每个事件处理程序,靠近每个 LINQ 查询等 - 都将使用函数指针。

回答by user79755

Function pointers are fast

函数指针很快

In what context? Compared to?

在什么情况下?相比?

It sounds like you just want to use function pointers for the sake of using them. That would be bad.

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

A pointer to a function is normally used as a callback or event handler.

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

回答by user79755

These days what are the best and most practical uses of integers in modern c-style languages?

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

回答by jheriko

They can be useful if you do not know the functionality supported by your target platform until run-time (e.g. CPU functionality, available memory). The obvious solution is to write functions like this:

如果您直到运行时才知道目标平台支持的功能(例如 CPU 功能、可用内存),它们会很有用。显而易见的解决方案是编写这样的函数:

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

If this function is called deep inside of important loops then its probably better to use a function pointer for MyFunc:

如果在重要循环的深处调用此函数,则最好为 MyFunc 使用函数指针:

int (*MyFunc)() = MyFunc_Default;

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

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

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

There are other uses of course, like callback functions, executing byte code from memory or for creating an interpreted language.

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

To execute Intel compatible byte codeon Windows, which might be useful for an interpreter. For example, here is an stdcall function returning 42 (0x2A) stored in an array which can be executed:

在 Windows 上执行Intel 兼容的字节码,这可能对解释器有用。例如,这里有一个 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);

);

);

回答by plinth

In the dim, dark ages before C++, there was a common pattern I used in my code which was to define a struct with a set of function pointers that (typically) operated on that struct in some way and provided particular behaviors for it. In C++ terms, I was just building a vtable. The difference was that I could side-effect the struct at runtime to change behaviors of individual objects on the fly as needed. This offers a much richer model of inheritance at the cost of stability and ease of debugging. The greatest cost, however, was that there was exactly one person who could write this code effectively: me.

在 C++ 之前的昏暗黑暗时代,我在代码中使用了一种常见模式,即定义一个带有一组函数指针的结构,这些函数指针(通常)以某种方式对该结构进行操作并为其提供特定的行为。用 C++ 术语来说,我只是在构建一个 vtable。不同之处在于我可以在运行时对结构产生副作用,以根据需要动态更改单个对象的行为。这以稳定性和易于调试为代价提供了更丰富的继承模型。然而,最大的代价是只有一个人可以有效地编写此代码:我。

I used this heavily in a UI framework that let me change the way objects got painted, who was the target of commands, and so on, on the fly - something that very few UIs offered.

我在 UI 框架中大量使用了它,它让我可以即时更改对象的绘制方式、命令的目标对象等等——很少有 UI 提供这些。

Having this process formalized in OO languages is better in every meaningful way.

用面向对象的语言形式化这个过程在每一个有意义的方式中都会更好。

回答by MichaelGG

Function pointers are a poor man's attempt to be functional. You could even make an argument that having function pointers makes a language functional, since you can write higher order functions with them.

函数指针是一个穷人试图成为函数式的。您甚至可以论证拥有函数指针会使语言具有功能性,因为您可以用它们编写更高阶的函数。

Without closures and easy syntax, they're sorta gross. So you tend to use them far less than desireable. Mainly for "callback" functions.

没有闭包和简单的语法,它们有点粗糙。所以你倾向于使用它们远远低于预期。主要用于“回调”功能。

Sometimes, OO design works around using functions by instead creating a whole interface type to pass in the function needed.

有时,OO 设计通过创建一个完整的接口类型来传递所需的函数来解决使用函数的问题。

C# has closures, so function pointers (which actually store an object so it's not just a raw function, but typed state too) are vastly more usable there.

C# 有闭包,所以函数指针(它实际上存储一个对象,所以它不仅仅是一个原始函数,而且也是类型化状态)在那里更有用。

EditOne of the comments said there should be a demonstration of higher order functions with function pointers. Any function taking a callback function is a higher order function. Like, say, EnumWindows:

编辑评论之一说应该有一个带有函数指针的高阶函数的演示。任何带有回调函数的函数都是高阶函数。比如说,EnumWindows

BOOL EnumWindows(          
    WNDENUMPROC lpEnumFunc,
    LPARAM lParam
);

First parameter is the function to pass in, easy enough. But since there are no closures in C, we get this lovely second parameter: "Specifies an application-defined value to be passed to the callback function." That app-defined value allows you to manually pass around untyped state to compensate for lack of closures.

第一个参数是要传入的函数,很简单。但是由于 C 中没有闭包,我们得到了这个可爱的第二个参数:“指定要传递给回调函数的应用程序定义的值。” 该应用程序定义的值允许您手动传递无类型状态以补偿缺少闭包的情况。

The .NET framework is also filled with similar designs. For instance, IAsyncResult.AsyncState: "Gets a user-defined object that qualifies or contains information about an asynchronous operation." Since the IAR is all you get on your callback, without closures, you need a way to shove some data into the async op so you can cast it out later.

.NET 框架也充满了类似的设计。例如,IAsyncResult.AsyncState:“获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。” 由于 IAR 是您在回调中获得的全部内容,没有闭包,您需要一种方法将一些数据推送到异步操作中,以便稍后将其抛出。

回答by Tim Ring

There are occasions when using function pointers can speed up processing. Simple dispatch tables can be used instead of long switch statements or if-then-else sequences.

在某些情况下,使用函数指针可以加快处理速度。可以使用简单的调度表代替长 switch 语句或 if-then-else 序列。