A friendly guide to the syntax of C++ method pointers

Share it !


यदि आप किसी समस्या को हल करने के लिए प्रदर्शन, जटिलता या कई संभावित समाधानों की तलाश कर रहे हैं, सी ++ हमेशा एक अच्छा उम्मीदवार होता है जब यह चरम सीमा पर आता है। बेशक, कार्यक्षमता आमतौर पर जटिलता के साथ आती है, लेकिन कुछ सी ++ ++ विशिष्टताएं लगभग अवैध हैं। मेरे दृष्टिकोण से, सी ++ विधि संकेत मैं कभी भी भर में आया सबसे जटिल अभिव्यक्ति हो सकता है, लेकिन मैं कुछ सरल के साथ शुरू करेंगे।

इस लेख में उदाहरण मेरे में उपलब्ध हैं गिटहब भंडार

सी: कार्यों के लिए सूचक

आइए कुछ मूल बातों से शुरू करें: मान लें कि आपके पास एक फ़ंक्शन है जो दो पूर्णांकों को तर्क के रूप में लेता है और एक पूर्णांक देता है:

int sum(int a, intb){
    return a+b;
}

सादे C में, आप इस फ़ंक्शन के लिए एक पॉइंटर बना सकते हैं, इसे अपने को असाइन कर सकते हैं sum(...) फ़ंक्शन, और इसे dereferencing द्वारा कॉल करें। फ़ंक्शन के हस्ताक्षर (तर्क, वापसी प्रकार) को सूचक के हस्ताक्षर का अनुपालन करना चाहिए। इसके अलावा, एक फ़ंक्शन पॉइंटर एक साधारण पॉइंटर की तरह व्यवहार करता है:

int (*funcPtrOne)(int, int);

funcPtrOne = ∑

int resultOne = funcPtrOne(2, 5);

अगर आप एक संकेतक को तर्क के रूप में लेते हैं और एक सूचक लौटाते हैं तो यह थोड़ा बदसूरत हो जाता है:

int *next(int *arrayOfInt){
    return ++arrayOfInt;
}

int *(*funcPtrTwo)(int *intPtr);

funcPtrTwo = &next;

int resultTwo = *funcPtrTwo(&array[0]);

सी में फ़ंक्शन पॉइंटर्स एक सबरूटीन का पता संग्रहीत करते हैं।

तरीकों के संकेत

आइए C ++ में कदम रखते हैं: अच्छी खबर यह है कि आपको कुछ दुर्लभ मामलों को छोड़कर, निम्न की तरह, तरीकों की ओर संकेत करने की आवश्यकता नहीं होगी। सबसे पहले, सदस्य कार्यों के साथ एक वर्ग परिभाषित करें जिसे आप पहले से जानते हैं:

class MyClass
{
public:

    int sum(int a, int b) {
        return a+b;
    }

};

1. एक निश्चित वर्ग प्रकार की विधि के लिए एक सूचक को परिभाषित करें

की एक विधि के लिए एक सूचक घोषित करें MyClass प्रकार। इस बिंदु पर, आप उस सटीक विधि को नहीं जानते हैं जिसे आप कॉल करना चाहते हैं। आपने केवल कुछ मनमाने ढंग से सूचक घोषित किया है MyClass तरीका। बेशक, हस्ताक्षर (तर्क, वापसी प्रकार) से मेल खाता है sum(…) विधि जिसे आप बाद में कॉल करना चाहते हैं:

int (MyClass::*methodPtrOne)(int, int);

2. एक निश्चित विधि असाइन करें

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

क्योंकि के हस्ताक्षर sum(…) विधि आपके पॉइंटर की घोषणा से मेल खाती है, आप इसे हस्ताक्षर सौंप सकते हैं:

methodPtrOne = &MyClass::sum;

3. विधि लागू करें

यदि आप पॉइंटर के साथ विधि को लागू करना चाहते हैं, तो आपको श्रेणी प्रकार का एक उदाहरण प्रदान करना होगा:

MyClass clsInstance;
int result = (clsInstance.*methodPtrOne)(2,3);

आप के साथ इंस्टेंस एक्सेस कर सकते हैं . ऑपरेटर, पॉइंटर को पॉइंटर से a *, और इस प्रकार तर्क के रूप में दो पूर्णांक प्रदान करके विधि को कहते हैं। बदसूरत, सही? लेकिन आप अभी भी एक कदम आगे जा सकते हैं।

एक वर्ग के भीतर विधि बिंदुओं का उपयोग करना

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

सबसे पहले, एक डेटा प्रकार को परिभाषित करें जो एक विधि सूचक को एक आरंभीकरण विधि में संग्रहीत कर सकता है (init) और इस विधि का वर्णन किया जाना चाहिए जब जानकारीticks):

template<typename T>
struct DynamicInitCommand {
    void (T::*init)();     // Pointer to additional initialization method
    unsigned int ticks;    // Number of ticks after init() is called
};

यहाँ क्या है Frontend वर्ग ऐसा दिखता है:

class  Frontend
{
public:

    Frontend(){
        DynamicInitCommand<Frontend> init1, init2, init3;

        init1 = { &Frontend::dynamicInit1, 5};
        init2 = { &Frontend::dynamicInit2, 10};
        init3 = { &Frontend::dynamicInit3, 15};

        m_dynamicInit.push_back(init1);
        m_dynamicInit.push_back(init2);
        m_dynamicInit.push_back(init3);
    }
   
   

    void  tick(){
        std::cout << "tick: " << ++m_ticks << std::endl;
       
        /* Check for delayed initializations */
        std::vector<DynamicInitCommand<Frontend>>::iterator  it = m_dynamicInit.begin();

        while (it != m_dynamicInit.end()){
            if (it->ticks < m_ticks){
                 
                if(it->init)
                    ((*this).*(it->init))(); // here it is

                it = m_dynamicInit.erase(it);

            } else {
                it++;
            }
        }
    }
   
    unsigned  int  m_ticks{0};
   
private:

    void  dynamicInit1(){
        std::cout << "dynamicInit1 called" << std::endl;
    };

    void  dynamicInit2(){
        std::cout << "dynamicInit2 called" << std::endl;
    }

    void  dynamicInit3(){
        std::cout << "dynamicInit3 called" << std::endl;
    }

    unsigned  int  m_initCnt{0};
    std::vector<DynamicInitCommand<Frontend> > m_dynamicInit;
};

उपरांत Frontend त्वरित है, tick() बैकेंड द्वारा विधि को निश्चित अंतराल पर कहा जाता है। उदाहरण के लिए, आप इसे प्रत्येक 200ms पर कॉल कर सकते हैं:

int  main(int  argc, char*  argv[]){
    Frontend frontendInstance;

    while(true){
        frontendInstance.tick(); // just for simulation purpose
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

Frontend के पास तीन अतिरिक्त आरंभीकरण विधियाँ हैं जिन्हें मूल्य के आधार पर कहा जाना चाहिए m_ticks। वह जानकारी जिसके बारे में कॉल करने के लिए इनिशियलाइज़ेशन विधि किस टिक पर वेक्टर में संग्रहीत है m_dynamicInit। निर्माता में (Frontend()), इस जानकारी को वेक्टर में जोड़ें ताकि अतिरिक्त आरंभीकरण कार्यों को पांच, 10 और 15 टिक्स के बाद कहा जाए। जब बैकएंड कॉल होता है tick() विधि, मूल्य m_ticks वृद्धि हुई है, और आप वेक्टर पर पुनरावृति करते हैं m_dynamicInit यह जांचने के लिए कि क्या आरंभीकरण विधि को बुलाया जाना है।

यदि यह स्थिति है, तो विधि सूचक को संदर्भित करके निस्तारण किया जाना चाहिए this:

((*this).*(it->init))()

सारांश

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

यह अब तक C ++ में पाया गया सबसे जटिल वाक्यविन्यास है। क्या आप कुछ और भी बुरा जानते हैं? इसे टिप्पणियों में पोस्ट करें!



Source link

Leave a Reply

Your email address will not be published. Required fields are marked *