什么是C ++委托?

时间:2012-03-05 14:18:29

标签: c++ delegates delegation

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

我想首先以“黑匣子”的方式了解它们,但是关于这些事情的内容的一些信息也会很棒。

这不是最纯粹或最干净的C ++,但我注意到我工作的代码库拥有丰富的代码库。我希望能够充分理解它们,所以我可以使用它们而不必深入研究可怕的嵌套模板可怕性。

这两篇The Code Project文章解释了我的意思,但并不是特别简洁:

6 个答案:

答案 0 :(得分:152)

您有很多选择来实现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:指向成员函数的指针(最快的解决方案)

请参阅 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);
}

答案 1 :(得分:33)

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

以下是一个例子:

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

答案 2 :(得分:16)

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

  1. std::function()使用堆操作(并且无法进行严格的嵌入式编程)。

  2. 所有其他实现都对可移植性或标准符合性做出较大或较小程度的让步(请通过检查此处和代码项目中的各种委托实现来验证)。我还没有看到一个不使用野生reinterpret_casts的实现,嵌套类“原型”希望产生与用户传入的大小相同的函数指针,编译器技巧如第一个前向声明,然后是typedef然后再次声明,这次继承自另一个类或类似的阴暗技术。虽然对于构建它的实现者来说这是一项伟大的成就,但对于C ++如何发展仍然是一个悲伤的证据。

  3. 很少有人指出,现在超过3个C ++标准版本,代表没有得到妥善解决。 (或者缺乏允许直接委托实现的语言功能。)

  4. 使用标准定义C ++ 11 lambda函数的方式(每个lambda具有匿名,不同类型),在某些用例中情况才得到改善。但是对于在(DLL)库API中使用委托的用例,单独使用lambdas 仍然无法使用。这里常见的技术是首先将lambda打包成std :: function,然后将其传递给API。

答案 3 :(得分:6)

非常简单,委托提供函数指针应该如何工作的功能。 C ++中的函数指针存在许多局限性。委托使用一些幕后模板讨厌来创建一个模板类函数 - 指针类型的东西,它以你想要的方式工作。

即 - 您可以将它们设置为指向给定的功能,您可以随时随地传递它们并随时随地调用它们。

这里有一些很好的例子:

答案 4 :(得分:3)

C ++中的委托选项(此处未提及)是使用函数ptr和context参数进行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);
    ...

答案 5 :(得分:1)

Windows Runtime等效于标准C ++中的函数对象。可以使用整个函数作为参数(实际上是函数指针)。它主要与事件一起使用。代表代表事件处理程序非常满足的合同。它有助于函数指针的工作方式。