2012-03-05 81 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); 
    ...