在C++中委托的一般概念是什么?它们是什么,如何使用它们以及它们的用途是什么?
我想首先用一个“黑盒子”方式学习它们,但是了解这些东西的内部机理也很好。
虽然这不是C ++最纯净或最清晰的表现方式,但我注意到我工作的代码库中有大量这样的使用。我希望足够理解它们,以便我只需使用它们而不必深入研究可怕的嵌套模板。
这两篇The Code Project文章解释了我的意思,但并不特别简洁:
在C++中委托的一般概念是什么?它们是什么,如何使用它们以及它们的用途是什么?
我想首先用一个“黑盒子”方式学习它们,但是了解这些东西的内部机理也很好。
虽然这不是C ++最纯净或最清晰的表现方式,但我注意到我工作的代码库中有大量这样的使用。我希望足够理解它们,以便我只需使用它们而不必深入研究可怕的嵌套模板。
这两篇The Code Project文章解释了我的意思,但并不特别简洁:
在C++中,您有很多选择来实现委托。以下是我想到的。
选项1:函数对象:
通过实现operator()
可以创建一个函数对象。
struct Functor
{
// Normal class/struct members
int operator()(double d) // Arbitrary return types and parameter list
{
return (int) d + 1;
}
};
// Use:
Functor f;
int i = f(3.14);
选项2:Lambda表达式(仅限C++11)
// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);
选项三:函数指针
int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);
选项4:成员函数指针(最快的解决方案)
请参阅 Fast C++ Delegate(在The Code Project上)。
struct DelegateList
{
int f1(double d) { }
int f2(double d) { }
};
typedef int (DelegateList::* DelegateType)(double d);
DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);
选项 5: std::function
(或者如果您的标准库不支持它,可以使用boost::function
)。它速度较慢,但是它是最灵活的。
#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions
选项6:绑定(使用 std::bind)
允许预先设置一些参数,方便调用成员函数。
struct MyClass
{
int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};
std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11
选项7: 模板
只要与参数列表匹配,就接受任何东西。
template <class FunctionT>
int DoSomething(FunctionT func)
{
return func(3.14);
}
std::bind
返回的对象,它们实际上做相同的事情,只是lambda不能像多态一样接受不同的参数类型。 - Xeobinding
)的函数。你不能使用函数指针实现这一点。 - J.N.委托是一个类,它将一个指针或引用包装到一个对象实例、该对象的类的成员方法要在该对象实例上调用,并提供一个触发调用的方法。
这里有一个例子:
template <class T>
class CCallback
{
public:
typedef void (T::*fn)( int anArg );
CCallback(T& trg, fn op)
: m_rTarget(trg)
, m_Operation(op)
{
}
void Execute( int in )
{
(m_rTarget.*m_Operation)( in );
}
private:
CCallback();
CCallback( const CCallback& );
T& m_rTarget;
fn m_Operation;
};
class A
{
public:
virtual void Fn( int i )
{
}
};
int main( int /*argc*/, char * /*argv*/ )
{
A a;
CCallback<A> cbk( a, &A::Fn );
cbk.Execute( 3 );
}
Execute()
的方法,该方法触发委托所包装对象上的函数调用。 - Grimm The Opinerstd::function()
使用堆操作(并且对于严肃的嵌入式编程来说无法使用)。
所有其他实现都在不同程度上向可移植性或标准一致性做出妥协(请通过检查此处和codeproject上的各种委托实现来验证)。我还没有看到一个不使用野生reinterpret_casts、嵌套类“原型”(希望产生与用户传入的函数指针大小相同的函数指针),编译器诡计(比如先前置声明,然后typedef再声明,这次从另一个类继承或类似的不良技巧)的实现。虽然对于构建它的实现者来说这是一个伟大的成就,但这仍然是C++进化方式的悲哀见证。
很少有人指出,现在已经超过三个C++标准修订版本,委托没有得到适当的解决。(或者缺乏语言功能,允许简单直接的委托实现。)
随着C++11 lambda函数按标准定义(每个lambda都有匿名的、不同类型),情况只在某些用例中得到改善。但对于在(DLL)库API中使用委托的用例来说,仅使用lambda函数仍然无法使用。这里常见的技巧是先将lambda打包到std::function中,然后通过API传递。
std::function
并不总是动态分配内存。 - Lightness Races in Orbit简单来说,委托提供了函数指针应该工作的功能。在 C++ 中,函数指针有很多限制。委托使用一些幕后模板技巧来创建一个类似于模板类函数指针类型的东西,以满足你可能想要的工作方式。
也就是说 - 你可以将它们设置为指向给定的函数,随时随地传递它们并调用它们。
这里有一些很好的例子:
在C++中,委托的一种选项是使用函数指针和上下文参数进行C风格的实现。这可能是许多提出此问题的人试图避免的模式。但是,该模式具有可移植性、高效性,并且可用于嵌入式和内核代码。
class SomeClass
{
in someMember;
int SomeFunc( int);
static void EventFunc( void* this__, int a, int b, int c)
{
SomeClass* this_ = static_cast< SomeClass*>( this__);
this_->SomeFunc( a );
this_->someMember = b + c;
}
};
void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);
...
SomeClass* someObject = new SomeObject();
...
ScheduleEvent( SomeClass::EventFunc, someObject);
...
Windows Runtime 中等同于标准 C++ 中函数对象的概念。可以将整个函数作为参数(实际上是函数指针)。它通常与事件一起使用。委托代表了事件处理程序必须遵守的契约。它简化了函数指针的使用。