2012-03-05 80 views
109

C++中委托的一般概念是什么?他们是什么,他们如何使用,他们用于什么?什么是C++代表?

我想先用'黑匣子'的方式了解它们,但关于这些东西的胆量的一些信息也会很棒。

这不是C++的最纯粹或最干净,但我注意到我工作的代码库有丰富。我希望足够了解它们,所以我可以使用它们,而不必钻研可怕的嵌套模板可怕。

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

+3

您是否在讨论.NET下托管的C++? – dasblinkenlight 2012-03-05 14:20:19

+2

您是否曾在[维基百科]授权网页(http://en.wikipedia.org/wiki/Delegation_%28programming%29)? – 2012-03-05 14:20:57

+3

'代表'不是C++中的常用名称。您应该在问题中添加一些信息以包含您已阅读的内容。请注意,虽然模式可能很常见,但如果您在一般情况下讨论* delegate *,或者在C++ CLI或具有* delegate *的特定实现的任何其他库的上下文中,答案可能会有所不同。 – 2012-03-05 15:00:34

回答

132

你有一个令人难以置信的多种选择,以实现委托在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); 
} 
+2

伟大的列表,+1。然而,在这里只有两个代表 - 作为代表捕获lambdas和从std :: bind返回的对象,两者都做同样的事情,除了lambda在接受不同参数类型的意义上不是多态的。 – Xeo 2012-03-05 14:46:26

+1

@ J.N:为什么你建议不要使用函数指针,但似乎可以使用成员方法指针?他们完全相同! – 2012-03-05 14:46:54

+1

@MatthieuM。 : 有道理。我正在考虑函数指针是遗留的,但这可能只是我个人的口味。 – 2012-03-05 14:51:30

29

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

下面是一个例子:

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

它提供了一种触发呼叫的方法?代表?怎么样?函数指针? – SirYakalot 2012-03-06 11:04:10

+0

委托类将提供像Execute()这样的方法,该方法触发委托包装的对象上的函数调用。 – 2012-03-06 11:15:47

+2

而不是执行,我强烈建议你的情况下重写调用操作符 - void CCallback :: operator()(int)。原因是在泛型编程中,可调用对象有望像函数一样被调用。 o.Execute(5)将不兼容,但o(5)很适合作为可调用的模板参数。这个类也可以是泛化的,但我认为你为了简洁而保持简单(这是一件好事)。除了那个挑剔之外,这是一个非常有用的课程。 – 2014-09-10 15:19:33

5

很简单,委托提供了函数指针应该如何工作的功能。 C++中的函数指针有很多限制。一个委托使用一些幕后的模板恶意来创建一个模板类的函数指针类型的东西,它可以以你想要的方式工作。

ie - 您可以将它们设置为指向给定的函数,并且您可以将它们传递并随时随地调用它们。

有一些非常优秀范例:

14

是否需要C++委托的实现是一个长期持久的尴尬,以C++社群。 每个C++程序员会喜欢有他们,让他们最终使用它们,尽管事实:

  1. std::function()使用堆操作(并且是遥不可及严重嵌入式编程)。

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

  3. 只有很少人指出,现在超过3个C++标准版本,代表没有得到正确处理。 (或缺乏允许直接委托实现的语言功能)。在标准定义C++ 11 lambda函数(每个lambda具有匿名,不同类型)的情况下,情况仅在一些用例。但对于在(DLL)库API中使用委托的用例,单独使用lambdas 仍然不可用。这里常见的技术是首先将lambda打包到std :: function中,然后通过API传递它。

+0

谢谢你这么说 – 2016-01-26 04:25:13

+0

我做的基础上其他地方看到其他的想法C++ 11版本埃尔伯特麦的通用回调,并且它似乎最清楚你在2)引用的问题。 对我来说唯一剩下的唠叨问题是我在客户端代码中使用宏卡住以创建代表。有可能是一个模板魔术的方式,我还没有解决。 – kert 2016-08-25 16:11:38

+1

我在嵌入式系统中使用std :: function没有堆,它仍然有效。 – prasad 2017-10-23 18:28:35

1

Windows运行时等效于标准C++中的函数对象。可以使用整个函数作为参数(实际上是一个函数指针)。它主要与事件一起使用。代表表示事件处理程序完成的合同。它有助于函数指针如何工作。

2

这里没有提到的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); 
    ...