फ़ंक्शन पॉइंटर के माध्यम से C ++ क्लास के तरीकों को कॉल करना


113

मैं कक्षा सदस्य फ़ंक्शन के लिए फ़ंक्शन पॉइंटर कैसे प्राप्त करूं, और बाद में किसी विशिष्ट ऑब्जेक्ट के साथ उस सदस्य फ़ंक्शन को कॉल करूं? मैं लिखना चाहूंगा:

class Dog : Animal
{
    Dog ();
    void bark ();
}


Dog* pDog = new Dog ();
BarkFunction pBark = &Dog::bark;
(*pBark) (pDog);

यदि संभव हो तो, मैं एक पॉइंटर के माध्यम से भी निर्माता को आमंत्रित करना चाहता हूं:

NewAnimalFunction pNew = &Dog::Dog;
Animal* pAnimal = (*pNew)();    

क्या यह संभव है, और यदि ऐसा है, तो ऐसा करने का पसंदीदा तरीका क्या है?


1
मैं अभी भी वास्तव में 'क्यों' नहीं समझ पा रहा हूं, यदि आप ऑब्जेक्ट ऑब्जेक्ट फ़ंक्शन को कॉल करना चाहते हैं तो बस ऑब्जेक्ट को एक पॉइंटर पास करें? यदि लोग शिकायत करते हैं कि क्योंकि यह आपको कक्षा को बेहतर ढंग से एनकैप्सुलेट करने में सक्षम बनाता है तो एक इंटरफ़ेस वर्ग क्यों नहीं बनाया गया है जो सभी वर्ग से विरासत में मिला है?
चाड

यह कमांड पैटर्न की तरह कुछ को लागू करने में उपयोगी हो सकता है, हालांकि कई लोग कच्चे सदस्य पॉइंटर मैकेनिक को छिपाने के लिए :: फंक्शन का उपयोग करेंगे।
सीबी बेली

9
आप उस कुत्ते को गतिशील रूप से क्यों आवंटित करते हैं? फिर आपको मैन्युअल रूप से ऑब्जेक्ट को हटाना होगा। ऐसा लगता है कि आप जावा, C # या कुछ अन्य तुलनीय भाषा से आ रहे हैं और अभी भी C ++ से लड़ रहे हैं। एक सादा स्वचालित वस्तु ( Dog dog;) अधिक संभावना है कि आप क्या चाहते हैं।
sbi

1
@Chad: मैं ज्यादातर सहमत हूँ लेकिन ऐसे समय होते हैं जहाँ एक संदर्भ पारित करना अधिक महंगा होगा। एक लूप पर विचार करें जो कुछ प्रकार के डेटा (पार्सिंग, गणना, आदि) से अधिक पुनरावृत्ति कर रहा है। कुछ पर आधारित फ़ंक्शन को कॉल करने में सक्षम होने की तुलना में अगर / अन्यथा गणना एक लागत लगाती है, जहां केवल इंगित किए गए फ़ंक्शन को कॉल करने से ऐसा हो सकता है, तो / और जाँच करता है कि क्या ये जाँच पाश में प्रवेश करने से पहले की जा सकती है।
एरिक

जवाबों:


127

इसे विस्तार से पढ़ें :

// 1 define a function pointer and initialize to NULL

int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;

// C++

class TMyClass
{
public:
   int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
   int DoMore(float a, char b, char c) const
         { cout << "TMyClass::DoMore" << endl; return a-b+c; };

   /* more of TMyClass */
};
pt2ConstMember = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore

// Calling Function using Function Pointer

(*this.*pt2ConstMember)(12, 'a', 'b');

24
हैरानी की बात है कि उन्होंने फैसला किया कि यह *this.*pt2Memberकाम करेगा। *अधिक पूर्वता है .*... व्यक्तिगत रूप से, मैंने अभी भी लिखा होगा this->*pt2Member, यह एक कम ऑपरेटर है।
एलेक्सिस विलके

7
आपको इनिशियलाइज़ क्यों करना pt2ConstMemberहै NULL?
सिरो सेंटिल्ली 郝海东 冠状 iro 事件 法轮功 '

@AlexisWilke क्यों हैरानी की बात है? प्रत्यक्ष वस्तुओं के लिए (संकेत नहीं) यह है (object.*method_pointer), इसलिए हम चाहते हैं कि *अधिक प्राथमिकता हो।
सिरो सेंटिल्ली 郝海东 冠状 iro i 法轮功 '

@ TomášZato, अगर मैं गलत नहीं हूँ (और मैं हो सकता है), thisबस यह प्रदर्शित करने के लिए उपयोग किया जा रहा है कि आप जो भी लागू .*करते हैं वह (उप) वर्ग के उदाहरण के लिए एक संकेतक होना चाहिए। हालाँकि, यह मेरे लिए नया वाक्य-विन्यास है, मैं केवल यहाँ से जुड़े अन्य उत्तरों और संसाधनों के आधार पर अनुमान लगा रहा हूँ। मैं इसे और अधिक स्पष्ट करने के लिए एक संपादन का सुझाव दे रहा हूं।
c1moore

1
ओह स्नैप, 100 पर बधाई!
जोनाथन मी

59

मैं कक्षा सदस्य फ़ंक्शन के लिए फ़ंक्शन पॉइंटर कैसे प्राप्त करूं, और बाद में किसी विशिष्ट ऑब्जेक्ट के साथ उस सदस्य फ़ंक्शन को कॉल करूं?

यह एक के साथ शुरू करने के लिए सबसे आसान है typedef। एक सदस्य समारोह के लिए, आप टाइपनाम में वर्गनाम जोड़ते हैं:

typedef void(Dog::*BarkFunction)(void);

फिर विधि को लागू करने के लिए, आप ->*ऑपरेटर का उपयोग करते हैं :

(pDog->*pBark)();

इसके अलावा, यदि संभव हो तो, मैं एक पॉइंटर के माध्यम से भी निर्माता को आमंत्रित करना चाहता हूं। क्या यह संभव है, और यदि ऐसा है, तो ऐसा करने का पसंदीदा तरीका क्या है?

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

मैंने आपके कोड को मूल रूप से संशोधित करने के लिए संशोधित किया है कि आप क्या वर्णन करते हैं। नीचे कुछ कैविएट हैं।

#include <iostream>

class Animal
{
public:

    typedef Animal*(*NewAnimalFunction)(void);

    virtual void makeNoise()
    {
        std::cout << "M00f!" << std::endl;
    }
};

class Dog : public Animal
{
public:

    typedef void(Dog::*BarkFunction)(void);

    typedef Dog*(*NewDogFunction)(void);

    Dog () {}

    static Dog* newDog()
    {
        return new Dog;
    }

    virtual void makeNoise ()
    {
        std::cout << "Woof!" << std::endl;
    }
};

int main(int argc, char* argv[])
{
    // Call member function via method pointer
    Dog* pDog = new Dog ();
    Dog::BarkFunction pBark = &Dog::makeNoise;

    (pDog->*pBark)();

    // Construct instance via factory method
    Dog::NewDogFunction pNew = &Dog::newDog;

    Animal* pAnimal = (*pNew)();

    pAnimal->makeNoise();

    return 0;
}

अब यद्यपि आप सामान्य रूप से बहुरूपता के जादू के लिए धन्यवाद Dog*के स्थान पर उपयोग कर सकते हैं Animal*, फ़ंक्शन पॉइंटर का प्रकार वर्ग पदानुक्रम के लुकअप नियमों का पालन नहीं करता है। तो एक पशु विधि सूचक एक कुत्ते विधि सूचक के साथ संगत नहीं है, दूसरे शब्दों में आप Dog* (*)()प्रकार के एक चर को असाइन नहीं कर सकते हैं Animal* (*)()

स्थैतिक newDogविधि एक कारखाने का एक सरल उदाहरण है, जो बस नए उदाहरण बनाता है और वापस करता है। एक स्थिर कार्य होने के नाते, इसमें एक नियमित typedef(बिना किसी क्लास क्वालिफायर के) है।

उपर्युक्त उत्तर देने के बाद, मुझे आश्चर्य है कि अगर आपको जो चाहिए वह प्राप्त करने का एक बेहतर तरीका नहीं है। कुछ विशिष्ट परिदृश्य हैं जहाँ आप इस तरह का काम करेंगे, लेकिन हो सकता है कि आपको ऐसे अन्य पैटर्न मिलें जो आपकी समस्या के लिए बेहतर काम करें। यदि आप अधिक सामान्य शब्दों में वर्णन करते हैं कि आप क्या हासिल करना चाहते हैं, तो छत्ता-दिमाग और भी उपयोगी साबित हो सकता है!

उपरोक्त से संबंधित, आपको कोई संदेह नहीं होगा कि बूस्ट बाइंड लाइब्रेरी और अन्य संबंधित मॉड्यूल बहुत उपयोगी हैं।


10
मैंने 10 वर्षों से C ++ का उपयोग किया है, और नियमित रूप से कुछ नया सीखता रहता हूं। मैंने पहले कभी नहीं सुना था ->*, लेकिन अब मुझे आशा है कि मुझे इसकी कभी आवश्यकता नहीं होगी :)
थॉमस

31

मुझे नहीं लगता कि किसी ने यहां समझाया है कि एक मुद्दा यह है कि आपको सामान्य फ़ंक्शन पॉइंटर्स के बजाय " सदस्य पॉइंटर्स " की आवश्यकता है ।

कार्य करने के लिए सदस्य संकेत केवल कार्य संकेत नहीं हैं। कार्यान्वयन के संदर्भ में, संकलक एक साधारण फ़ंक्शन पते का उपयोग नहीं कर सकता है, क्योंकि सामान्य तौर पर, आपको कॉल करने के लिए पता नहीं पता होता है जब तक कि आपको पता नहीं है कि किस वस्तु के लिए (आभासी कार्यों को लगता है)। thisअंतर्निहित पैरामीटर प्रदान करने के लिए आपको ऑब्जेक्ट को जानना होगा , निश्चित रूप से।

यह कहते हुए कि आपको उनकी आवश्यकता है, अब मैं कहूँगा कि आपको वास्तव में उनसे बचने की आवश्यकता है। गंभीरता से, सदस्य संकेत एक दर्द है। यह ऑब्जेक्ट-ओरिएंटेड डिज़ाइन पैटर्न को देखने के लिए बहुत अधिक समझदार है जो एक ही लक्ष्य को प्राप्त करते हैं, या boost::functionऊपर बताए गए एक या एक का उपयोग करने के लिए - यह मानते हुए कि आप उस विकल्प को बनाते हैं।

यदि आप मौजूदा कोड के लिए उस फ़ंक्शन पॉइंटर की आपूर्ति कर रहे हैं, तो आपको वास्तव में एक साधारण फ़ंक्शन पॉइंटर की आवश्यकता है, आपको क्लास के स्थिर सदस्य के रूप में एक फ़ंक्शन लिखना चाहिए। एक स्थिर सदस्य फ़ंक्शन समझ में नहीं आता है this, इसलिए आपको ऑब्जेक्ट को एक स्पष्ट पैरामीटर के रूप में पास करना होगा। पुराने सी कोड के साथ काम करने के लिए इन पंक्तियों के साथ एक बार एक असामान्य-असामान्य मुहावरा था जिसे फ़ंक्शन पॉइंटर्स की आवश्यकता होती है

class myclass
{
  public:
    virtual void myrealmethod () = 0;

    static void myfunction (myclass *p);
}

void myclass::myfunction (myclass *p)
{
  p->myrealmethod ();
}

चूँकि myfunctionवास्तव में सिर्फ एक सामान्य फ़ंक्शन (कार्यक्षेत्र अलग है), एक फ़ंक्शन पॉइंटर सामान्य C तरीके से पाया जा सकता है।

EDIT - इस तरह की विधि को "क्लास मेथड" या "स्टैटिक मेंबर फंक्शन" कहा जाता है। एक गैर-सदस्यीय फ़ंक्शन से मुख्य अंतर यह है कि, यदि आप इसे कक्षा के बाहर से संदर्भित करते हैं, तो आपको ::गुंजाइश रिज़ॉल्यूशन ऑपरेटर का उपयोग करके गुंजाइश निर्दिष्ट करनी होगी । उदाहरण के लिए, फ़ंक्शन पॉइंटर प्राप्त करने के लिए, उपयोग करें &myclass::myfunctionऔर इसे कॉल करने के लिए उपयोग करें myclass::myfunction (arg);

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


'myfunction' एक नॉरमल फंक्शन नहीं है अगर सामान्य रूप से आपका मतलब C स्टाइल फंक्शन है। 'माइफ़ंक्शन' को अधिक सटीक रूप से मायक्लास की विधि कहा जाता है। एक वर्ग के तरीके सामान्य कार्यों की तरह नहीं होते हैं, जिसमें उनके पास कुछ सी शैली फ़ंक्शन होता है जो कि 'यह' सूचक नहीं है।
एरिक

3
बूस्ट का उपयोग करने की सलाह देना असंभव है। विधि संकेत का उपयोग करने के लिए व्यावहारिक अच्छे कारण हैं। मैं एक विकल्प के रूप में बढ़ावा देने का उल्लेख नहीं करता, लेकिन जब कोई कहता है कि किसी और को सभी तथ्यों को जानने के बिना इसका उपयोग करना चाहिए। लागत पर बूस्ट आता है! और अगर यह एक एम्बेडेड प्लेटफॉर्म है तो यह एक विकल्प नहीं हो सकता है। इससे परे, मैं वास्तव में आपके लेखन को पसंद करता हूं।
एरिक

@ एरिक - आपके दूसरे बिंदु पर, मैंने यह कहने का इरादा नहीं किया कि "आप बूस्ट का उपयोग करें", और वास्तव में मैंने कभी भी बूस्ट का उपयोग नहीं किया है। इरादा (जहां तक ​​मुझे 3 साल बाद पता है) यह था कि लोगों को विकल्पों की तलाश करनी चाहिए, और कुछ संभावनाओं को सूचीबद्ध करना चाहिए। "या जो भी" इंगित करता है कि सूची का अर्थ संपूर्ण नहीं है। सदस्य बिंदुओं को पठनीयता में लागत होती है। उनका संक्षिप्त स्रोत प्रतिनिधित्व रन-टाइम की लागतों को भी कम कर सकता है - विशेष रूप से एक सदस्य पॉइंटर को गैर-आभासी और आभासी दोनों तरीकों से सामना करना होगा, और पता होना चाहिए कि कौन सा।
स्टीव 314

@ ईरिक - केवल इतना ही नहीं, बल्कि ये मुद्दे सदस्य बिंदुओं के साथ गैर-पोर्टेबिलिटी का एक कारण हैं - विज़ुअल सी ++, कम से कम अतीत में, सदस्य सूचक प्रकारों का प्रतिनिधित्व करने के तरीके के बारे में कुछ अतिरिक्त सुरागों की आवश्यकता है। मैं एक एम्बेडेड सिस्टम के लिए स्थैतिक फ़ंक्शन दृष्टिकोण का उपयोग करूंगा - एक पॉइंटर का प्रतिनिधित्व किसी भी अन्य फ़ंक्शन पॉइंटर के समान है, लागत स्पष्ट हैं, और पोर्टेबिलिटी समस्या नहीं है। और स्थिर सदस्य फ़ंक्शन द्वारा लिपटा गया कॉल जानता है (संकलन समय पर) कि कॉल वर्चुअल है या नहीं - वर्चुअल तरीकों के लिए सामान्य वाइबेट लुक से परे रन-टाइम चेक की आवश्यकता नहीं है।
स्टीव ३१

@ एरिक - अपने पहले बिंदु पर - मुझे पता है कि एक स्थिर सदस्य फ़ंक्शन सी शैली के कार्य के समान नहीं है (इसलिए "एक तरफ समस्याएँ"), लेकिन मुझे शायद नाम शामिल करना चाहिए था।
स्टीव

18
typedef void (Dog::*memfun)();
memfun doSomething = &Dog::bark;
....
(pDog->*doSomething)(); // if pDog is a pointer
// (pDog.*doSomething)(); // if pDog is a reference

2
होना चाहिए: (pDog -> * doSomething) (); // अगर pDog एक पॉइंटर है // (pDog। * doSomething) (); // यदि pDog एक संदर्भ के रूप में () ऑपरेटर की उच्च प्राथमिकता है तो -> * और। *।
टोमेक

12

न्यूनतम रननीय उदाहरण

main.cpp

#include <cassert>

class C {
    public:
        int i;
        C(int i) : i(i) {}
        int m(int j) { return this->i + j; }
};

int main() {
    // Get a method pointer.
    int (C::*p)(int) = &C::m;

    // Create a test object.
    C c(1);
    C *cp = &c;

    // Operator .*
    assert((c.*p)(2) == 3);

    // Operator ->*
    assert((cp->*p)(2) == 3);
}

संकलित करें और चलाएं:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

उबुन्टु 18.04 में परीक्षण किया गया।

आप कोष्ठक के क्रम को बदल नहीं सकते हैं या उन्हें छोड़ नहीं सकते हैं। निम्नलिखित काम नहीं करते हैं:

c.*p(2)
c.*(p)(2)

GCC 9.2 के साथ विफल होगा:

main.cpp: In function int main()’:
main.cpp:19:18: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in p (...)’, e.g. ‘(... ->* p) (...)’
   19 |     assert(c.*p(2) == 3);
      |

सी ++ 11 मानक

.*और इस उद्देश्य के लिए C ++ में ->*एक एकल ऑपरेटर शुरू किया गया है, और सी में मौजूद नहीं है।

C ++ 11 N3337 मानक ड्राफ्ट :

  • 2.13 "ऑपरेटर्स एंड पंक्चुएटर्स" में सभी ऑपरेटरों की एक सूची है, जिसमें सम्‍मिलित है .*और ->*
  • 5.5 "पॉइंटर-टू-मेंबर ऑपरेटर्स" बताते हैं कि वे क्या करते हैं

11

मैं एक विधि से फ़ंक्शन पॉइंटर (एक विधि सूचक नहीं) बनाने का तरीका जानने के लिए यहां आया था, लेकिन यहां कोई भी उत्तर समाधान प्रदान नहीं करता है। यहां वह है जो मैंने जुटाया:

template <class T> struct MethodHelper;
template <class C, class Ret, class... Args> struct MethodHelper<Ret (C::*)(Args...)> {
    using T = Ret (C::*)(Args...);
    template <T m> static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

#define METHOD_FP(m) MethodHelper<decltype(m)>::call<m>

अपने उदाहरण के लिए अब आप क्या करेंगे:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = METHOD_FP(&Dog::bark);
(*bark)(&dog); // or simply bark(&dog)

संपादित करें:
C ++ 17 का उपयोग करना, एक और भी बेहतर समाधान है:

template <auto m> struct MethodHelper;
template <class C, class Ret, class... Args, Ret (C::*m)(Args...)> struct MethodHelper<m> {
    static Ret call(C* object, Args... args) {
        return (object->*m)(args...);
    }
};

जिसका उपयोग सीधे मैक्रो के बिना किया जा सकता है:

Dog dog;
using BarkFunction = void (*)(Dog*);
BarkFunction bark = MethodHelper<&Dog::bark>::call;
(*bark)(&dog); // or simply bark(&dog)

जैसे संशोधक के तरीकों के लिए constआपको कुछ और विशेषज्ञता की आवश्यकता हो सकती है जैसे:

template <class C, class Ret, class... Args, Ret (C::*m)(Args...) const> struct MethodHelper<m> {
    static Ret call(const C* object, Args... args) {
        return (object->*m)(args...);
    }
};

6

कारण यह है कि आप फ़ंक्शन फ़ंक्शन का उपयोग सदस्य फ़ंक्शन को कॉल करने के लिए नहीं कर सकते हैं, यह है कि साधारण फ़ंक्शन पॉइंटर्स आमतौर पर फ़ंक्शन का मेमोरी एड्रेस होता है।

सदस्य फ़ंक्शन को कॉल करने के लिए, आपको दो बातें जानने की आवश्यकता है:

  • किस सदस्य को फोन करना है
  • किस उदाहरण का उपयोग किया जाना चाहिए (जिसका सदस्य फ़ंक्शन)

साधारण फंक्शन पॉइंटर्स दोनों को स्टोर नहीं कर सकते हैं। C ++ सदस्य फ़ंक्शन पॉइंटर्स का उपयोग a) को स्टोर करने के लिए किया जाता है, यही वजह है कि आपको सदस्य फ़ंक्शन पॉइंटर को कॉल करते समय स्पष्ट रूप से उदाहरण निर्दिष्ट करने की आवश्यकता होती है।


1
मैंने इसे वोट दिया लेकिन ओपी को यह पता नहीं है कि आप "किस उदाहरण" का जिक्र कर रहे हैं। मैं 'इस' सूचक को स्पष्ट करने के लिए विस्तार करूंगा।
एरिक

6

एक वर्ग सदस्य के लिए एक फ़ंक्शन पॉइंटर एक ऐसी समस्या है जो वास्तव में बढ़ावा देने के लिए उपयुक्त है :: फ़ंक्शन। छोटा उदाहरण:

#include <boost/function.hpp>
#include <iostream>

class Dog 
{
public:
   Dog (int i) : tmp(i) {}
   void bark ()
   {
      std::cout << "woof: " << tmp << std::endl;
   }
private:
   int tmp;
};



int main()
{
   Dog* pDog1 = new Dog (1);
   Dog* pDog2 = new Dog (2);

   //BarkFunction pBark = &Dog::bark;
   boost::function<void (Dog*)> f1 = &Dog::bark;

   f1(pDog1);
   f1(pDog2);
}

2

एक नई वस्तु बनाने के लिए आप या तो प्लेसमेंट नए का उपयोग कर सकते हैं, जैसा कि ऊपर उल्लेख किया गया है, या आपकी कक्षा एक क्लोन () विधि को लागू करती है जो ऑब्जेक्ट की एक प्रति बनाती है। फिर आप ऑब्जेक्ट के नए उदाहरण बनाने के लिए ऊपर बताए गए सदस्य फ़ंक्शन पॉइंटर का उपयोग करके इस क्लोन विधि को कॉल कर सकते हैं। क्लोन का लाभ यह है कि कभी-कभी आप एक पॉइंटर के साथ बेस क्लास में काम कर सकते हैं जहां आपको ऑब्जेक्ट का प्रकार नहीं पता है। इस स्थिति में एक क्लोन () विधि का उपयोग करना आसान हो सकता है। इसके अलावा, क्लोन () आपको ऑब्जेक्ट की स्थिति की प्रतिलिपि बनाने देगा यदि आप चाहते हैं।


क्लोन महंगे हो सकते हैं और ओपी उनसे बचने की इच्छा कर सकता है यदि प्रदर्शन एक मुद्दा है या कुछ चिंता का विषय है।
एरिक

0

मैंने इसे std :: function और std :: bind के साथ किया।

मैंने इस EventManager वर्ग को लिखा है जो एक unordered_map में हैंडलर के वेक्टर को संग्रहीत करता है जो कि इवेंट प्रकार (जो कि अभी अहस्ताक्षरित int हैं, मेरे पास उनका एक बड़ा नाम-स्कोप्ड एनम है) उस इवेंट प्रकार के हैंडलर के वेक्टर के लिए है।

मेरे EventManagerTests वर्ग में, मैंने एक इवेंट हैंडलर सेट किया, जैसे:

auto delegate = std::bind(&EventManagerTests::OnKeyDown, this, std::placeholders::_1);
event_manager.AddEventListener(kEventKeyDown, delegate);

यहां AddEventListener फ़ंक्शन है:

std::vector<EventHandler>::iterator EventManager::AddEventListener(EventType _event_type, EventHandler _handler)
{
    if (listeners_.count(_event_type) == 0) 
    {
        listeners_.emplace(_event_type, new std::vector<EventHandler>());
    }
    std::vector<EventHandler>::iterator it = listeners_[_event_type]->end();
    listeners_[_event_type]->push_back(_handler);       
    return it;
}

यहाँ EventHandler प्रकार की परिभाषा है:

typedef std::function<void(Event *)> EventHandler;

तब EventManagerTests में वापस :: RaiseEvent, मैं यह करता हूं:

Engine::KeyDownEvent event(39);
event_manager.RaiseEvent(1, (Engine::Event*) & event);

यहाँ EventManager के लिए कोड है :: RaiseEvent:

void EventManager::RaiseEvent(EventType _event_type, Event * _event)
{
    if (listeners_.count(_event_type) > 0)
    {
        std::vector<EventHandler> * vec = listeners_[_event_type];
        std::for_each(
            begin(*vec), 
            end(*vec), 
            [_event](EventHandler handler) mutable 
            {
                (handler)(_event);
            }
        );
    }
}

यह काम। मुझे EventManagerTests में कॉल मिलता है :: OnKeyDown। मुझे हटाने के लिए वैक्टर को समय पर साफ करना पड़ता है, लेकिन एक बार जब मैं करता हूं कि कोई लीक नहीं है। एक घटना को उठाते हुए मेरे कंप्यूटर पर लगभग 5 माइक्रोसेकंड होते हैं, जो कि 2008 है। यह बिल्कुल सुपर फास्ट नहीं है, लेकिन। जब तक मुझे पता है कि मैं काफी गर्म हूं और मैं अल्ट्रा हॉट कोड में इसका इस्तेमाल नहीं करता हूं।

मैं अपने स्वयं के std :: function और std :: bind को रोल करके इसे गति देना चाहूंगा, और शायद वैक्टरों के unordered_map के बजाय सरणियों के एक सरणी का उपयोग कर सकता हूं, लेकिन मुझे यह पता नहीं चला है कि सदस्य फ़ंक्शन को कैसे संग्रहीत किया जाए पॉइंटर और उसे कोड से कॉल करें जो कि वर्ग को बुलाए जाने के बारे में कुछ नहीं जानता है। बरौनी का जवाब बहुत दिलचस्प लगता है ।।

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