C में वर्चुअल फ़ंक्शंस

हाल ही में मुझसे एक सवाल पूछा गया था: मैं सी में आभासी कार्यों के तंत्र को कैसे लागू करूंगा?

सबसे पहले, मुझे नहीं पता था कि यह कैसे करना है: आखिरकार, सी एक वस्तु-उन्मुख प्रोग्रामिंग भाषा नहीं है, और विरासत जैसी कोई चीज नहीं है। लेकिन चूंकि मेरे पास पहले से ही सी के साथ थोड़ा अनुभव था, और मुझे पता था कि आभासी कार्यों ने कैसे काम किया है, मैंने सोचा कि संरचनाओं (संरचना) का उपयोग करके आभासी कार्यों के व्यवहार की नकल करने का एक तरीका होना चाहिए।

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

आइए अब C ++ में आभासी कार्यों का उपयोग करने का एक सरल उदाहरण देखें

class ClassA { public: ClassA() {data = 10;} virtual void set() { std::cout << "ClassA is increasing" << std::endl; data++; } int get() { set(); return data; } protected: int data; }; class ClassB : public ClassA { public: void set() { std::cout << "ClassB is decreasing" << std::endl; data--; } }; 

उपरोक्त उदाहरण में, हमारे पास एक क्लास क्लास है जिसमें दो विधियाँ हैं: get() और set()get() विधि को एक आभासी फ़ंक्शन के रूप में चिह्नित किया गया है; ClassB कक्षा में, इसका कार्यान्वयन बदल रहा है। data पूर्णांक को protected कीवर्ड के साथ चिह्नित किया जाता protected ताकि ClassB वंशज वर्ग की पहुंच हो।

 int main() { ClassA classA; ClassB classB; std::cout << "ClassA value: " << classA.get() << std::endl; std::cout << "ClassB value: " << classB.get() << std::endl; return 0; } 

निष्कर्ष:

 ClassA is increasing ClassA value: 11 ClassB is decreasing ClassB value: 9 

अब आइए सी में वर्चुअल फ़ंक्शंस की अवधारणा को कैसे लागू किया जाए, इस बारे में सोचें कि वर्चुअल फ़ंक्शंस को पॉइंटर्स के रूप में दर्शाया जाता है और वाइबल में संग्रहीत किया जाता है, और वाइबल एक स्थिर सरणी है, हमें एक ऐसी संरचना बनानी होगी जो क्लासए क्लास को खुद ही बना ले, क्लासए के लिए वर्चुअल फ़ंक्शन टेबल, और ClassA विधियों का कार्यान्वयन।

 /*    */ struct ClassA; /*  ,    . */ typedef struct { void (*ClassA)(struct ClassA*); /* "" */ void (*set)(struct ClassA*); /*  set */ int (*get)(struct ClassA*); /*  get */ } ClassA_functiontable; typedef struct ClassA { int data; ClassA_functiontable *vtable; /*    ClassA */ } ClassA; /*   ClassA */ void ClassA_constructor(ClassA *this); void ClassA_set(ClassA *this); int ClassA_get(ClassA *this); /*     ClassA */ ClassA_functiontable ClassA_vtable = {ClassA_constructor, ClassA_set, ClassA_get }; /*   */ void ClassA_constructor(ClassA *this) { this->vtable = &ClassA_vtable; this->data = 10; } void ClassA_set(ClassA *this) { printf("ClassA is increasing\n"); this->data++; } int ClassA_get(ClassA *this) { this->vtable->set(this); return this->data; } 

सी में, यह कोई संकेतक नहीं है जो कॉलर को इंगित करता है। मैंने C ++ में this पॉइंटर के उपयोग को अनुकरण करने के लिए this तरह के पैरामीटर का नाम दिया (इसके अलावा, यह उस तरह दिखता है जैसे C ++ में ऑब्जेक्ट विधि कॉल वास्तव में काम करता है)।

जैसा कि हम ऊपर दिए गए कोड से देखते हैं, ClassA_get() कॉल set() फ़ंक्शन को सूचक से सूचक के माध्यम से कहता है। अब हम व्युत्पन्न वर्ग के कार्यान्वयन को देखें:

 /*    */ struct ClassB; /*  ,     ,      */ typedef struct { void (*ClassB)(struct ClassB*); void (*set)(struct ClassB*); void (*get)(struct ClassA*); } ClassB_functiontable; typedef struct ClassB { ClassA inherited_class; } ClassB; void ClassB_constructor(ClassB *this); void ClassB_set(ClassB *this); int ClassB_get(ClassB *this); ClassB_functiontable ClassB_vtable = {ClassB_constructor, ClassB_set, ClassB_get}; void ClassB_constructor(ClassB *this) { /*     */ ClassA_constructor((ClassA*)this); /*          */ this->inherited_class.vtable = (ClassA_functiontable*)&ClassB_vtable; } void ClassB_set(ClassB *this) { printf("ClassB decreasing\n"); this->inherited_class.data--; } int ClassB_get(ClassB *this) { this->inherited_class.vtable->set((ClassA*)this); return this->inherited_class.data; } 

जैसा कि आप कोड से देख सकते हैं, हम set() फ़ंक्शन को गेट से get() क्लासबी के कार्यान्वयन से बुलाते हैं, व्यवहार्य का उपयोग करते हैं, जो वांछित set() फ़ंक्शन को इंगित करता है, और "इनहेरिट" क्लास क्लास के माध्यम से एक ही पूर्णांक data तक भी पहुंचता है।

यह main() फ़ंक्शन जैसा दिखता है:

 int main() { ClassA classA; ClassB classB; ClassA_constructor(&classA); ClassB_constructor(&classB); printf("ClassA value: %d\n", classA.vtable->get(&classA)); /*   get()  - */ printf("ClassB value: %d\n", classB.inherited_class.vtable->get((struct ClassA*)&classB)); } 

निष्कर्ष:

 ClassA is increasing ClassA value: 11 ClassB decreasing ClassB value: 9 

बेशक, यह ट्रिक C ++ की तरह या किसी अन्य ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग लैंग्वेज में स्वाभाविक नहीं लगती है, और जब मैंने C में प्रोग्राम लिखा तो मुझे कभी भी ऐसा कुछ लागू नहीं करना पड़ा, लेकिन फिर भी यह आंतरिक संरचना को बेहतर ढंग से समझने में मदद कर सकता है। आभासी कार्य।

अनुवादक से: C ++ में आभासी कार्यों के आंतरिक कार्यान्वयन के बारे में एक विस्तृत लेख

Source: https://habr.com/ru/post/In138684/


All Articles