何时使用std::函数而不是继承
When to use std::function instead of inheritance?
在某些情况下,std::function
可以取代继承。以下两个代码片段非常相似(调用函数时的成本大致相同,在签名中的用法几乎相同,在大多数情况下std::function也不需要我们制作A
的额外副本(:
struct Function
{
virtual int operator()( int ) const =0;
};
struct A
: public Function
{
int operator()( int x ) const override { return x; }
};
使用std::function
,我们可以将其重写为
using Function = std::function<int (int)>;
struct A
{
int operator()( int x ) const { return x; }
};
为了更清楚地说明,这两个片段是如何关联的:它们都可以通过以下方式使用:
int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }
int main( int argc, char **argv )
{
A f;
anotherFunction( f, 5 );
return 0;
}
后一种方法更灵活,因为我们不必从一些公共基类派生类。类Function
对象之间的唯一关系是基于它们的能力。在面向对象编程方面,它可能被认为不那么干净(当然,在函数编程方面则不然(。
除此之外,这两种解决方案之间还有其他区别吗?当使用哪种解决方案时,是否有任何通用指南,或者这只是个人偏好的问题?是否存在一种解决方案性能优于另一种的情况?
首先进行一个小的更正:注意,这个:
int anotherFunction( Function f, int x ) { return f( x ) + f( x ); }
不会使用基于继承的解决方案进行编译,因为Function
是按值获取的,而且它是抽象的。另一方面,如果它不是抽象的,你会得到切片,这不是你想要的。
相反,为了利用多态性,您必须通过引用(可能通过引用const
(获得Function
派生的对象:
int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }
这不像函数式编程,所以如果你热衷于函数式编程(就像你看起来的那样(,你可能会因为这一点而想要避免它。
也就是说,以下是我将提供的指导方针:
如果可以,请使用模板:
template<typename F> int anotherFunction(F f, int x) { return f(x) + f(x); }
一般来说,当它可以使用时,静态(编译时,基于模板(多态性被认为比动态(运行时,基于继承(多态性更可取,因为:
卓越的灵活性:您不必更改类型的定义,并使它们从一个公共基类派生,即可通用。例如,这允许您编写:
anotherFunction([] (int x) { return x * 2; }, 42);
以及:
anotherFunction(myFxn, 42); // myFxn is a regular function
甚至:
anotherFunction(my_functor(), 42); // my_functor is a class
卓越的性能:由于您不是通过虚拟表进行调用,并且编译器知道函数调用将如何解决,因此它可以内联调用以提供更高的性能(如果它认为这是合理的(
如果您不能使用模板,因为要调用的函数将在运行时确定,请使用
std::function
:int anotherFunction(std::function<int (int)> f, int x) { return f(x) + f(x); }
这也将为您提供足够的灵活性来传递lambdas、函数指针、函子,基本上是任何可调用对象。例如,请参阅这个问答;StackOverflow上的A。
对于基于模板的设计,使用
std::function
可能会带来显著的运行时开销,对于基于硬编码继承的解决方案(如您概述的解决方案(,可能也会带来轻微的开销,但它为您提供了灵活性,这是一个标准习惯用法。此外,和往常一样,当性能是一个问题时,进行测量以支持任何假设——你可能会得到令人惊讶的结果。当您想存储任何类型的可调用对象以供以后调用时,通常需要采用这种基于
std::function
的设计,例如在Command设计模式中,或者当您有一个要处理和一般调用的可调用对象的异构集合时。有关何时使用std::function
而不是模板的讨论,请参阅本问答;StackOverflow上的A。那么,您应该在什么时候采用使用继承的硬编码设计?好吧,在所有这些情况下,1。和2。是不可行的——老实说,我现在想不出任何可行的方案,但我相信有人会想出一个角落的方案。请注意,为了使用类似
std::function
的习惯用法,C++11不是必需的,因为Boost有一个boost::function
和boost::bind
实现,它们早于C++11的std::function
和std::bind
。
TL;DR:使用模板或std::function
。
- 继承函数的重载解析
- 如何使用 C++ 中的继承函数访问派生类中的局部变量
- 派生类调用使用非继承成员的继承函数
- 继承函数是否适用于 C++ 中的基类元素或派生类元素?
- 在链表中的某个点插入时出现问题,C++中的继承函数
- std :: is_same-来自integral_constant的继承函数的用例
- 如何将同名的继承函数视为重载函数
- 使用子函数的继承函数
- 执行覆盖的继承函数
- 相对于更专业的继承函数,模板更喜欢子类函数
- C++:继承函数并重新定义它们
- C++ 继承函数指针,可与派生方法一起使用
- 不同对象的向量,对象具有非继承函数
- C++多级继承函数调用
- 基类指向派生类继承函数调用的指针
- C++继承函数覆盖
- 派生类是否C++必须在头文件中包含继承函数/成员的定义
- 继承函数中的参数消失
- C++通过继承函数实现继承的抽象函数
- 构造函数和继承函数 c++