C ++ डेलीगेट क्या है?


147

C ++ में एक प्रतिनिधि का सामान्य विचार क्या है? वे क्या हैं, उनका उपयोग कैसे किया जाता है और उनका उपयोग किस लिए किया जाता है?

मैं पहले उनके बारे में 'ब्लैक बॉक्स' तरीके से सीखना चाहूंगा, लेकिन इन चीजों की हिम्मत के बारे में थोड़ी जानकारी भी बहुत अच्छी होगी।

यह C ++ अपने शुद्धतम या साफ-सुथरे तरीके से नहीं है, बल्कि मैंने नोटिस किया है कि जहां मैं काम करता हूं वहां कोडबेस उनके पास प्रचुर मात्रा में होता है। मैं उन्हें पर्याप्त रूप से समझने की उम्मीद कर रहा हूं, इसलिए मैं बस उनका उपयोग कर सकता हूं और भयानक नेस्टेड टेम्पलेट की भयावहता में तल्लीन नहीं करना है।

ये दो कोड प्रोजेक्ट लेख बताते हैं कि मेरा क्या मतलब है लेकिन विशेष रूप से सफलतापूर्वक नहीं:


4
क्या आप .NET के तहत प्रबंधित C ++ के बारे में बात कर रहे हैं?
डेस्ब्लिंकीनेलाइट


5
delegatec ++ parlance में एक सामान्य नाम नहीं है। आपको उस संदर्भ को शामिल करने के लिए प्रश्न में कुछ जानकारी जोड़ना चाहिए जिसमें आपने इसे पढ़ा है। ध्यान दें कि जब पैटर्न सामान्य हो सकता है, तो उत्तर अलग-अलग हो सकते हैं यदि आप सामान्य रूप से प्रतिनिधि के बारे में बात करते हैं , या सी ++ सीएलआई या किसी अन्य पुस्तकालय के संदर्भ में जिसमें प्रतिनिधि का एक विशेष कार्यान्वयन है ।
डेविड रोड्रिगेज -

6
2 साल प्रतीक्षा करें?)
रैंक 1

6
अभी भी इंतजार ...
ग्रिम द ओपिनर

जवाबों:


173

आपके पास 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: लंबोदर भाव ( 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: सदस्य कार्यों के लिए सूचक (सबसे तेज़ समाधान)

देखें फास्ट सी ++ प्रतिनिधि (पर 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: एसटीडी :: फ़ंक्शन

(या 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। हालाँकि, केवल दो वास्तव में यहां प्रतिनिधियों के रूप में गिना जाता है - लैम्ब्डा पर कब्जा करना और ऑब्जेक्ट से लौटा std::bind, और दोनों वास्तव में एक ही काम करते हैं, सिवाय लैम्बदास इस अर्थ में बहुरूपिक नहीं हैं कि वे विभिन्न तर्क प्रकारों को स्वीकार कर सकते हैं।
Xeo

1
@ जेएन: आप फ़ंक्शन पॉइंटर्स का उपयोग न करने की अनुशंसा क्यों करते हैं, लेकिन सदस्य विधियों के पॉइंटर्स का उपयोग करके ठीक प्रतीत होते हैं? वे सिर्फ समान हैं!
मैथ्यू एम।

1
@MatthieuM। : निष्पक्ष बिंदु। मैं फंक्शन पॉइंटर्स को विरासत मान रहा था, लेकिन यह केवल मेरा व्यक्तिगत स्वाद है।
जेएन

1
@ Xeo: एक प्रतिनिधि का मेरा विचार बल्कि अनुभवजन्य है। शायद मैं बहुत ज्यादा फंक्शन ऑब्जेक्ट और डेलिगेट मिक्सिग कर रहा हूं (मेरे पूर्व C # अनुभव को दोष दें)।
जेएन

2
@SirYakalot कुछ ऐसा है जो एक फ़ंक्शन की तरह व्यवहार करता है, लेकिन यह एक ही समय में राज्य को पकड़ सकता है और किसी अन्य चर की तरह हेरफेर किया जा सकता है। उदाहरण के लिए एक प्रयोग एक ऐसा कार्य है जो दो मापदंडों को लेता है और 1 पैरामीटर को एक पैरामीटर ( bindingमेरी सूची में) के साथ एक नया फ़ंक्शन बनाने के लिए मजबूर करता है । आप फ़ंक्शन पॉइंटर्स के साथ इसे प्राप्त नहीं कर सकते।
जेएन

37

एक प्रतिनिधि एक वर्ग है जो किसी वस्तु उदाहरण के लिए एक सूचक या संदर्भ को लपेटता है, उस वस्तु के वर्ग का एक सदस्य तरीका उस वस्तु उदाहरण पर बुलाया जाता है, और उस कॉल को ट्रिगर करने के लिए एक विधि प्रदान करता है।

यहाँ एक उदाहरण है:

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()कि प्रतिनिधि कॉल ऑब्जेक्ट पर कॉल फ़ंक्शन को ट्रिगर करता है।
ग्रिम द ऑपिनर

4
Execute के बजाय, मैं आपके मामले में कॉल ऑपरेटर - शून्य CCallback :: ऑपरेटर () (int) को ओवरराइड करने की अत्यधिक सलाह दूंगा। इसका कारण जेनेरिक प्रोग्रामिंग में है, कॉल करने योग्य वस्तुओं को एक फ़ंक्शन की तरह कहा जाता है। o.Execute (5) असंगत होगा, लेकिन o (5) एक कॉल करने योग्य टेम्पलेट तर्क के रूप में अच्छी तरह से फिट होगा। इस वर्ग को भी सामान्यीकृत किया जा सकता है, लेकिन मुझे लगता है कि आपने इसे संक्षिप्तता के लिए सरल रखा (जो अच्छी बात है)। उस नाइटपिक के अलावा, यह एक बहुत ही उपयोगी वर्ग है।
डेविड पीटरसन

18

C ++ प्रतिनिधि कार्यान्वयन की आवश्यकता C ++ समुदाय के लिए एक लंबे समय तक चलने वाला दूतावास है। हर C ++ प्रोग्रामर उन्हें पसंद आएगा, इसलिए वे अंततः उन तथ्यों के बावजूद उनका उपयोग करते हैं:

  1. std::function() हीप संचालन का उपयोग करता है (और गंभीर एम्बेडेड प्रोग्रामिंग के लिए पहुंच से बाहर है)।

  2. अन्य सभी कार्यान्वयन बड़े या कम डिग्री के लिए या तो पोर्टेबिलिटी या मानक अनुरूपता के लिए रियायतें देते हैं (कृपया यहां और प्रतिनिधि पर विभिन्न प्रतिनिधि कार्यान्वयन का निरीक्षण करके सत्यापित करें)। मुझे अभी तक एक कार्यान्वयन को देखना है जिसमें जंगली रीइंटरटेनमेंट_कास्ट, नेस्टेड क्लास "प्रोटोटाइप" का उपयोग नहीं किया गया है, जो उम्मीद करता है कि उपयोगकर्ता द्वारा पारित किए गए समान आकार के फ़ंक्शन पॉइंटर्स का उत्पादन करेंगे, पहले आगे की घोषणा की तरह कंपाइलर ट्रिक, फिर टाइपडिफ फिर से घोषित करें। इस समय एक अन्य वर्ग या इसी तरह की छायादार तकनीकों से विरासत में मिला। हालांकि इसे लागू करने वालों के लिए एक बड़ी उपलब्धि है कि यह अभी भी एक दुखद गवाही है कि C ++ कैसे विकसित होता है।

  3. केवल शायद ही कभी यह इंगित किया गया है, कि अब 3 C ++ मानक संशोधन, प्रतिनिधियों को ठीक से संबोधित नहीं किया गया था। (या भाषा सुविधाओं की कमी जो सीधे प्रतिनिधि कार्यान्वयन के लिए अनुमति देती है।)

  4. जिस तरह से C ++ 11 लैम्ब्डा कार्यों को मानक द्वारा परिभाषित किया गया है (प्रत्येक लैम्ब्डा में गुमनाम, अलग प्रकार है), स्थिति केवल कुछ उपयोग मामलों में सुधार हुई है। लेकिन (DLL) लाइब्रेरी APIs में डेलिगेट्स के उपयोग के मामले के लिए, लैम्ब्डा अकेले अभी भी उपयोग करने योग्य नहीं हैं। यहाँ सामान्य तकनीक है, सबसे पहले लैम्ब्डा को एक std :: function में पैक करना है और फिर इसे पूरे API में पास करना है।


मैंने एल्बर्ट माई के जेनेरिक कॉलबैक का एक संस्करण C ++ 11 में किया है जो अन्य विचारों के आधार पर कहीं और देखा गया है, और यह 2 में आपके द्वारा उद्धृत अधिकांश मुद्दों को स्पष्ट करता है)। मेरे लिए केवल शेष नागिंग मुद्दा मैं प्रतिनिधि बनाने के लिए क्लाइंट कोड में एक मैक्रो का उपयोग करके फंस गया हूं। इसमें से शायद एक टेम्पलेट मैजिक तरीका है जो मैंने अभी तक हल नहीं किया है।
kert

3
मैं एम्बेडेड सिस्टम में ढेर के बिना std :: फ़ंक्शन का उपयोग कर रहा हूं और यह अभी भी काम करता है।
प्रसाद

1
std::functionहमेशा गतिशील रूप से आवंटित नहीं करता है
ऑर्बिट

3
यह उत्तर एक वास्तविक उत्तर की तुलना में एक शेख़ी से अधिक है
लाइटवेट रेज़ इन ऑर्बिट

8

बहुत सरलता से, एक प्रतिनिधि एक फ़ंक्शन पॉइंटर SHOULD कैसे काम करता है, इसके लिए कार्यक्षमता प्रदान करता है। C ++ में फ़ंक्शन पॉइंटर्स की कई सीमाएं हैं। एक प्रतिनिधि एक टेम्पलेट-क्लास फंक्शन-पॉइंटर-टाइप-चीज़ बनाने के लिए कुछ बैक-द-सीन्स टेम्पलेट नॉस्टनेस का उपयोग करता है जो आपके इच्छित तरीके से काम करता है।

यानी - आप उन्हें किसी दिए गए फ़ंक्शन पर इंगित करने के लिए सेट कर सकते हैं और आप उन्हें चारों ओर से गुजर सकते हैं और जब भी और जहाँ भी आप चाहें उन्हें कॉल कर सकते हैं।

यहाँ कुछ बहुत अच्छे उदाहरण हैं:


4

C ++ में प्रतिनिधियों के लिए एक विकल्प जो अन्यथा यहाँ उल्लेखित नहीं है, यह एक फ़ंक्शन ptr और एक संदर्भ तर्क का उपयोग करके सी शैली करना है। यह शायद एक ही पैटर्न है कि कई इस सवाल को पूछने से बचने की कोशिश कर रहे हैं। लेकिन, पैटर्न पोर्टेबल, कुशल है, और एम्बेडेड और कर्नेल कोड में प्रयोग करने योग्य है।

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

1

मानक सी ++ में एक फ़ंक्शन ऑब्जेक्ट के बराबर विंडोज रनटाइम। एक पूरे फ़ंक्शन को एक पैरामीटर (वास्तव में एक फ़ंक्शन पॉइंटर) के रूप में उपयोग कर सकता है। यह ज्यादातर घटनाओं के साथ संयोजन में उपयोग किया जाता है। प्रतिनिधि एक अनुबंध का प्रतिनिधित्व करता है जो इवेंट हैंडलर बहुत पूरा करते हैं। यह सुविधा देता है कि फ़ंक्शन पॉइंटर किस प्रकार काम कर सकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.