什么是 C + + 委托?

C + + 中委托的一般概念是什么?它们是什么,它们是如何使用的,它们是用来做什么的?

我想首先以一种“黑匣子”的方式了解它们,但是了解一些关于这些东西的内部信息也是很好的。

这不是最纯粹或最干净的 C + + ,但我注意到我工作的代码库中有大量这样的代码。我希望对它们有足够的了解,这样我就可以直接使用它们,而不必深究可怕的嵌套模板。

这两篇 代码项目文章解释了我的意思,但不是特别简洁:

186420 次浏览

委托是包装指向对象实例的指针或引用的类,是要对该对象实例调用的该对象的类的成员方法,并提供触发该调用的方法。

这里有一个例子:

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 );
}

要在 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);

选项3: 函数指针

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

选项4: 指向成员函数 (最快解决方案)的指针

译自: 美国《科学》杂志网站(http://www.codeproject.com/article/13287/Fast-C-generate)(在 代码项目)。

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: 函数

(或者 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: 绑定(使用 绑定)

允许预先设置一些参数,方便调用成员函数,例如。

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);
}

非常简单,一个委托提供了一个函数指针应该如何工作的功能。函数指针在 C + + 中有很多局限性。委托使用一些幕后模板来创建一个模板类函数指针类型的东西,按照您希望的方式工作。

你可以设置它们指向一个给定的函数你可以传递它们随时随地调用它们。

这里有一些很好的例子:

对 C + + 委托实现的需求对 C + + 社区来说是一个长久的尴尬。 每个 C + + 程序员都想拥有它们,所以他们最终会使用它们,尽管事实是:

  1. std::function()使用堆操作(对于严格的嵌入式编程来说是无法实现的)。

  2. 所有其他实现都或多或少地在可移植性或标准一致性方面做出了让步(请通过检查这里和代码项目上的各种委托实现来验证)。我还没有看到一个实现不使用野生重新解释 _ 强制转换,嵌套类“原型”,希望产生的函数指针的大小相同的一个传递给用户,编译器技巧,如首先前进声明,然后 typedef,然后再声明,这一次继承自另一个类或类似的阴影技术。尽管这对于构建这个系统的实现者来说是一个巨大的成就,但是对于 C + + 的演化来说,这仍然是一个令人遗憾的试金石。

  3. 只是很少有人指出,现在超过3 C + + 标准的修订,代表没有得到适当的处理。(或者缺乏允许直接委托实现的语言特性。)

  4. 按照标准定义 C + + 11 lambda 函数的方式(每个 lambda 都有匿名的、不同的类型) ,情况只在某些用例中有所改善。但是对于在(DLL)库 API 中使用委托的用例,lambdas 一个人仍然不可用。这里常用的技术是,首先将 lambda 打包成一个 std: : 函数,然后通过 API 传递它。

Windows 运行时等效于标准 C + + 中的函数对象。可以将整个函数用作参数(实际上这是一个函数指针)。它通常与事件一起使用。委托表示一个事件处理程序能够很好地履行的契约。这有助于函数指针的工作。

对于 C + + 中的委托,这里没有提到的一个选项是使用函数 ptr 和上下文参数以 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);
...