在 C++ 标准模板库中,有一个“功能”部分,其中许多类都重载了它们的()
运算符。
在 C++ 中将函数用作对象是否带来了便利?
为什么我们不能只使用函数指针呢?有什么例子吗?
在 C++ 标准模板库中,有一个“功能”部分,其中许多类都重载了它们的()
运算符。
在 C++ 中将函数用作对象是否带来了便利?
为什么我们不能只使用函数指针呢?有什么例子吗?
当然,总是可以使用函数指针而不是函数对象,但是函数对象比函数指针提供了某些优点,即:
更好的性能:
最明显和最重要的优势之一是它们更有可能产生更好的性能。在函数对象的情况下,在编译时可以获得更多详细信息,以便编译器可以准确地确定并因此内联要调用的函数,这与函数指针的情况不同,在函数指针的情况下,指针的取消引用使编译器难以确定实际函数那将被调用。
函数对象是智能函数:
函数对象可能具有其他成员函数和属性。这意味着函数对象具有状态。实际上,同一个函数,由一个函数对象表示,可能同时具有不同的状态。这对于普通功能是不可能的。函数对象的另一个优点是您可以在使用/调用它们之前在运行时初始化它们。
普通函数只有在签名不同时才能有不同的类型。但是,即使它们的签名相同,函数对象也可以具有不同的类型。实际上,函数对象定义的每个函数行为都有自己的类型。这是使用模板进行泛型编程的一项重大改进,因为可以将功能行为作为模板参数传递。
为什么我们不能只使用函数指针呢?有什么例子吗?
使用 C 风格的函数指针不能利用内联的优势。函数指针通常需要额外的间接查找。
但是,如果operator ()
重载,那么编译器很容易编译inline
代码并节省额外的调用,从而提高性能。
重载的另一个优点operator ()
是,可以设计一个隐含地将函数对象视为参数的函数;无需将其作为单独的函数传递。更少的手工编码程序,更少的错误和更好的可读性。
Bjarne Stroustrup(C++ 发明者)网页上的这个问题很好地解释了这个方面。
如果需要, C++ 标准(模板)库使用带有重载的函数式编程operator ()
。
> 在 C++ 中使用函数作为对象是否带来了便利?
是:C++ 模板机制允许所有其他 C/C++ 编程风格(C 风格和 OOP 风格,见下文)。
> 为什么我们不能只使用函数指针呢?有什么例子吗?
但是我们可以:一个简单的 C 函数指针也是一个具有良好定义的 operator() 的对象。如果我们设计一个库,我们不想强迫任何人在不需要时使用那种 C 指针样式。这通常与强迫一切/每个人都采用/使用 OOP 风格一样不受欢迎;见下文。
从 C 程序员和函数式程序员的角度来看,OOP 不仅速度更慢而且更冗长,并且在大多数情况下是错误的抽象方向(“信息”不是也不应该是“对象”)。因此,每当在其他上下文中使用“对象”一词时,人们往往会感到困惑。
在 C++ 中,任何具有所需属性的东西都可以被视为对象。在这种情况下,一个简单的 C 函数指针也是一个对象。这并不意味着在不需要时使用 OOP 范式;这只是使用模板机制的正确方法。
要了解性能差异,请比较编程(语言)风格/范式及其可能的优化:
C风格:
C++(和 Java)OOP 风格:
C++ 模板样式:
C++ 模板的通用性足以支持上述其他两种样式,并且在内联的情况下它们甚至可以胜过……</p>
编译的函数式语言:(由于缺少“正确的尾调用”,不包括 JVM 和 Javascript 作为目标平台)
主要区别在于函数对象比普通函数指针更强大,因为它们可以保持状态。大多数算法采用模板函数而不是普通函数指针,这使得强大的构造可以用作绑定器,通过用存储在仿函数或C++11中更新的lambdas上的值填充额外的参数来调用具有不同签名的函数。一旦算法被设计为采用函子,在库中提供一组预定义的通用函数对象就很有意义。
除此之外,还有一些潜在的优势,在大多数情况下,这些仿函数是简单的类,编译器对其有完整的定义,并且可以执行函数调用的内联以提高性能。这就是为什么可以比C 库std::sort
快得多的原因。qsort