许多函数接受函数指针作为参数。 atexit
和call_once
就是很好的例子。如果这些更高级别的函数接受了一个void *参数,例如atexit(&myFunction, &argumentForMyFunction)
,那么我可以通过传递函数指针和数据块来轻松地包装我喜欢的任何函子来提供有状态。
原样,在很多情况下我希望我可以使用参数注册回调,但是注册函数不允许我传递任何参数。 atexit
只接受一个参数:一个带0个参数的函数。我无法在我的对象之后注册一个清理函数,我必须注册一个在类的所有对象之后清理的函数,并强制我的类维护一个需要清理的所有对象的列表。
我一直认为这是一种疏忽,似乎没有正当理由说明为什么你不允许传递4或8字节的指针,除非你是在极其有限的微控制器上。我一直认为他们根本没有意识到额外的论点是多么重要,直到重新定义规范为时已晚。在call_once
的情况下,posix版本不接受任何参数,但C ++ 11版本接受一个仿函数(实际上相当于传递一个函数和一个参数,只有编译器为你做了一些工作) )。
有没有理由选择不允许这个额外的论点?仅接受带有0个参数的#34; void函数"?
是否有优势答案 0 :(得分:1)
我认为atexit
只是一个特殊情况,因为你传递给它的任何函数都应该被调用一次。因此,无论它需要做什么状态,它都可以保存在全局变量中。如果今天设计atexit
,可能需要void*
才能避免使用全局变量,但这实际上不会给它任何新功能;在某些情况下,它只会使代码更清晰。
但是,对于许多API,允许回调采取其他参数,并且不允许它们这样做将是一个严重的设计缺陷。例如,pthread_create
确实允许您传递void*
,这是有道理的,因为否则您需要为每个线程创建一个单独的函数,并且编写一个生成一个函数的程序是完全不可能的。可变数量的线程。
答案 1 :(得分:0)
许多接受缺少传递参数的函数指针的接口只是来自不同的时间。但是,他们的签名无法在不破坏现有代码的情况下进行更改。这有点不合理,但事后才很容易说出来。整体编程风格已经转移到通常非功能性编程语言中的函数式编程的有限使用。此外,当时创建了许多这样的接口,甚至在" normal"上存储了任何额外的数据。计算机暗示了可观察到的额外成本:除了使用的额外存储空间外,即使没有使用额外的参数也需要通过。当然,atexit()
几乎不会成为性能瓶颈,因为它只被调用一次,但如果你在任何地方传递一个额外的指针,你肯定也会有一个qsort()
&# 39;比较函数。
特别是对于像atexit()
这样的东西,可以合理地直接使用自定义全局对象,在退出时调用函数对象,只需注册一个函数atexit()
调用所有函数在所述全球对象中注册。另请注意,atexit()
仅保证最多可注册32个函数,尽管实现可能支持更多已注册的函数。将它用作对象清理函数的注册表而不是调用对象清理函数的函数似乎是不明智的,因为其他库也可能需要注册函数。
那就是说,我无法想象为什么atexit()
在C ++中特别有用,其中对象在程序终止时会自动销毁。当然,这种方法假设所有对象都以某种方式被保留,但通常以某种形式或其他形式通常是必需的,并且通常使用适当的RAII对象来完成。