runtime polymorphism c
सी ++ में रनटाइम पॉलिमोर्फिज्म का एक विस्तृत अध्ययन।
विंडोज़ पर .jar फ़ाइल कैसे चलाएं 10
रनटाइम पॉलीमॉर्फिज्म को डायनेमिक पॉलीमोर्फिज्म या लेट बाइंडिंग के रूप में भी जाना जाता है। रनटाइम बहुरूपता में, फ़ंक्शन कॉल को रन टाइम पर हल किया जाता है।
इसके विपरीत, समय या स्थिर बहुरूपता को संकलित करने के लिए, कंपाइलर रन टाइम पर ऑब्जेक्ट को घटाता है और फिर निर्णय लेता है कि कौन सी फ़ंक्शन कॉल ऑब्जेक्ट को बांधने के लिए है। C ++ में, विधि ओवरराइडिंग का उपयोग करके रनटाइम बहुरूपता को लागू किया जाता है।
इस ट्यूटोरियल में, हम विस्तार से रनटाइम पॉलीमॉर्फिज्म के बारे में विस्तार से जानकारी देंगे।
=> यहां सभी C ++ ट्यूटोरियल की जांच करें।
आप क्या सीखेंगे:
- ओवरराइडिंग
- वर्चुअल फंक्शन
- वर्चुअल टेबल और _vptr का कार्य करना
- शुद्ध आभासी कार्य और सार वर्ग
- वर्चुअल डिस्ट्रक्टर्स
- निष्कर्ष
- अनुशंसित पाठ
ओवरराइडिंग
फंक्शन ओवरराइडिंग वह तंत्र है जिसके उपयोग से बेस क्लास में परिभाषित फ़ंक्शन को एक बार फिर से व्युत्पन्न वर्ग में परिभाषित किया जाता है। इस मामले में, हम कहते हैं कि फ़ंक्शन व्युत्पन्न वर्ग में ओवरराइड है।
हमें याद रखना चाहिए कि ओवरराइडिंग फ़ंक्शन एक वर्ग के भीतर नहीं किया जा सकता है। फ़ंक्शन केवल व्युत्पन्न वर्ग में ओवरराइड किया जाता है। इसलिए इनहेरिटेंस फ़ंक्शन ओवरराइडिंग के लिए मौजूद होना चाहिए।
दूसरी बात यह है कि एक बेस क्लास से जो फंक्शन हम ओवरराइड कर रहे हैं, उसमें एक ही सिग्नेचर या प्रोटोटाइप होना चाहिए यानी इसमें एक ही नाम, एक ही रिटर्न टाइप और एक ही लॉजिक लिस्ट होनी चाहिए।
आइए एक उदाहरण देखते हैं जो विधि के ओवरराइडिंग को प्रदर्शित करता है।
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'< आउटपुट:
वर्ग :: आधार
कक्षा :: व्युत्पन्न
उपरोक्त कार्यक्रम में, हमारे पास एक आधार वर्ग और एक व्युत्पन्न वर्ग है। बेस क्लास में, हमारे पास एक फंक्शन show_val है जो व्युत्पन्न क्लास में ओवरराइड होता है। मुख्य फ़ंक्शन में, हम बेस और डेरेव्ड क्लास के प्रत्येक ऑब्जेक्ट बनाते हैं और प्रत्येक ऑब्जेक्ट के साथ शो_वल फ़ंक्शन को कॉल करते हैं। यह वांछित उत्पादन का उत्पादन करता है।
प्रत्येक वर्ग की वस्तुओं का उपयोग करने वाले कार्यों का उपरोक्त बंधन स्थिर बंधन का एक उदाहरण है।
अब देखते हैं कि क्या होता है जब हम बेस क्लास पॉइंटर का उपयोग करते हैं और व्युत्पन्न क्लास ऑब्जेक्ट को उसकी सामग्री के रूप में असाइन करते हैं।
उदाहरण कार्यक्रम नीचे दिखाया गया है:
#include using namespace std; class Base { public: void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() //overridden function { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //Early Binding }
आउटपुट:
वर्ग :: आधार
अब हम देखते हैं, कि आउटपुट 'क्लास :: बेस' है। तो इस बात पर ध्यान दिए बिना कि बेस पॉइंटर किस प्रकार की वस्तु है, प्रोग्राम उस क्लास के फंक्शन की सामग्री को आउटपुट करता है जिसका बेस पॉइंटर किस प्रकार का है। इस मामले में, स्थैतिक लिंकिंग भी किया जाता है।
आधार सूचक आउटपुट, सही सामग्री और उचित लिंकिंग बनाने के लिए, हम कार्यों के गतिशील बंधन के लिए जाते हैं। यह वर्चुअल फ़ंक्शन तंत्र का उपयोग करके प्राप्त किया जाता है जिसे अगले भाग में समझाया गया है।
वर्चुअल फंक्शन
ओवरराइड फ़ंक्शन के लिए फ़ंक्शन बॉडी को गतिशील रूप से बाध्य किया जाना चाहिए, हम 'वर्चुअल' कीवर्ड का उपयोग करके बेस क्लास फ़ंक्शन को आभासी बनाते हैं। यह वर्चुअल फंक्शन एक ऐसा फंक्शन है जिसे व्युत्पन्न वर्ग में ओवरराइड किया जाता है और कंपाइलर इस फ़ंक्शन के लिए देर से या डायनामिक बाइंडिंग करता है।
अब हम वर्चुअल कीवर्ड को शामिल करने के लिए उपरोक्त प्रोग्राम को संशोधित करते हैं:
#include using namespace std;. class Base { public: virtual void show_val() { cout << 'Class::Base'; } }; class Derived:public Base { public: void show_val() { cout <<'Class::Derived'; } }; int main() { Base* b; //Base class pointer Derived d; //Derived class object b = &d; b->show_val(); //late Binding }
आउटपुट:
कक्षा :: व्युत्पन्न
तो बेस की उपरोक्त वर्ग परिभाषा में, हमने show_val फ़ंक्शन को 'वर्चुअल' के रूप में बनाया। जैसा कि बेस क्लास फ़ंक्शन को वर्चुअल किया जाता है, जब हम बेस क्लास पॉइंटर और कॉल शो_वल फ़ंक्शन के लिए व्युत्पन्न क्लास ऑब्जेक्ट असाइन करते हैं, तो रनटाइम के दौरान बाइंडिंग होती है।
इस प्रकार, चूंकि बेस क्लास पॉइंटर में व्युत्पन्न वर्ग ऑब्जेक्ट होता है, इसलिए व्युत्पन्न वर्ग में शो_वल फ़ंक्शन बॉडी शो शोवल और इसलिए आउटपुट के लिए बाध्य होती है।
C ++ में, व्युत्पन्न वर्ग में ओवरराइड फ़ंक्शन भी निजी हो सकता है। कंपाइलर केवल समय पर ऑब्जेक्ट के प्रकार की जांच करता है और फ़ंक्शन को रन टाइम पर बांधता है, इसलिए यह फ़ंक्शन सार्वजनिक या निजी होने पर भी कोई फर्क नहीं पड़ता है।
ध्यान दें कि यदि किसी फ़ंक्शन को आधार वर्ग में आभासी घोषित किया जाता है, तो यह सभी व्युत्पन्न वर्गों में आभासी होगा।
लेकिन अब तक, हमने चर्चा नहीं की है कि वास्तव में आभासी फ़ंक्शन सही फ़ंक्शन को बाध्य करने या अन्य शब्दों में पहचानने में एक भूमिका कैसे निभाते हैं, वास्तव में देर से बाध्यकारी कैसे होता है।
वर्चुअल फंक्शन, कॉन्सेप्ट का उपयोग करके रनटाइम पर फ़ंक्शन बॉडी के लिए बाध्य है वर्चुअल टेबल (VTABLE) और एक छुपा सूचक कहा जाता है _vptr।
ये दोनों अवधारणाएं आंतरिक कार्यान्वयन हैं और सीधे कार्यक्रम द्वारा उपयोग नहीं की जा सकती हैं।
वर्चुअल टेबल और _vptr का कार्य करना
सबसे पहले, हम समझते हैं कि वर्चुअल टेबल (VTABLE) क्या है।
संकलित समय पर संकलक आभासी कार्यों वाले वर्ग के साथ-साथ उन कक्षाओं के लिए एक-एक वीटीएबल सेट करता है जो आभासी कार्यों वाले वर्गों से प्राप्त होते हैं।
एक वीटीएबल में ऐसी प्रविष्टियाँ होती हैं जो वर्चुअल फ़ंक्शंस के लिए फंक्शन पॉइंटर्स होती हैं जिन्हें कक्षा की वस्तुओं द्वारा बुलाया जा सकता है। प्रत्येक वर्चुअल फ़ंक्शन के लिए एक फ़ंक्शन पॉइंटर प्रविष्टि है।
शुद्ध आभासी कार्यों के मामले में, यह प्रविष्टि NULL है। (यही कारण है कि हम अमूर्त वर्ग को त्वरित नहीं कर सकते हैं)।
अगली इकाई, _vptr जिसे वॉयटेबल पॉइंटर कहा जाता है, एक हिडन पॉइंटर है जिसे कंपाइलर बेस क्लास में जोड़ता है। यह _vptr वर्ग की व्यवहार्यता की ओर इशारा करता है। इस बेस क्लास से निकली सभी कक्षाएं _vptr को विरासत में मिलती हैं।
वर्चुअल फ़ंक्शंस वाले वर्ग की प्रत्येक वस्तु आंतरिक रूप से इस _vptr को संग्रहीत करती है और उपयोगकर्ता के लिए पारदर्शी होती है। ऑब्जेक्ट का उपयोग करके वर्चुअल फ़ंक्शन के लिए हर कॉल तब इस _vptr का उपयोग करके हल किया जाता है।
परीक्षण के मामले और परीक्षण परिदृश्य के बीच अंतर
आइए हम व्यवहार्य और _vtr के कामकाज को प्रदर्शित करने के लिए एक उदाहरण लेते हैं।
#include using namespace std; class Base_virtual { public: virtual void function1_virtual() {cout<<'Base :: function1_virtual()
';}; virtual void function2_virtual() {cout<<'Base :: function2_virtual()
';}; virtual ~Base_virtual(){}; }; class Derived1_virtual: public Base_virtual { public: ~Derived1_virtual(){}; virtual void function1_virtual() { coutfunction2_virtual(); delete (b); return (0); }
आउटपुट:
Derived1_virtual :: function1_virtual ()
आधार :: function2_virtual ()
उपरोक्त कार्यक्रम में, हमारे पास दो वर्चुअल फ़ंक्शन और एक वर्चुअल डिस्ट्रक्टर के साथ बेस क्लास है। हमने बेस क्लास से भी एक क्लास ली है और उसमें; हमने केवल एक वर्चुअल फ़ंक्शन को ओवरराइड किया है। मुख्य फ़ंक्शन में, व्युत्पन्न वर्ग पॉइंटर को बेस पॉइंटर को सौंपा गया है।
फिर हम बेस क्लास पॉइंटर का उपयोग करके दोनों वर्चुअल फ़ंक्शन को कॉल करते हैं। हम देखते हैं कि ओवरराइड फ़ंक्शन को कहा जाता है जब इसे कहा जाता है और बेस फ़ंक्शन नहीं। जबकि दूसरे मामले में, चूंकि फ़ंक्शन को ओवरराइड नहीं किया जाता है, बेस क्लास फ़ंक्शन को कहा जाता है।
अब देखते हैं कि उपरोक्त प्रोग्राम को vtable और _vptr का उपयोग करके आंतरिक रूप से कैसे दर्शाया जाता है।
पहले के स्पष्टीकरण के अनुसार, चूंकि वर्चुअल फ़ंक्शंस के साथ दो कक्षाएं हैं, हमारे पास दो vtables होंगे - प्रत्येक वर्ग के लिए एक। साथ ही, _vptr बेस क्लास के लिए मौजूद रहेगा।

ऊपर दर्शाया गया है कि उपरोक्त कार्यक्रम के लिए विएबेट लेआउट कैसा होगा, इसका चित्रात्मक प्रतिनिधित्व है। आधार वर्ग के लिए व्यवहार्य सीधा है। व्युत्पन्न वर्ग के मामले में, केवल function1_virtual ओवरराइड किया जाता है।
इसलिए हम देखते हैं कि व्युत्पन्न वर्ग की व्यवहार्यता में, फंक्शन पॉइंटर 1 के लिए फंक्शन पॉइंटर, व्युत्पन्न वर्ग में ओवरराइड फंक्शन की ओर इशारा करता है। बेस क्लास में एक फंक्शन के लिए दूसरी ओर फंक्शन पॉइंटर।
इस प्रकार उपरोक्त कार्यक्रम में जब बेस पॉइंटर को एक व्युत्पन्न वर्ग ऑब्जेक्ट दिया जाता है, बेस पॉइंटर व्युत्पन्न वर्ग के _vptr को इंगित करता है।
इसलिए जब कॉल b-> function1_virtual () किया जाता है, तो व्युत्पन्न वर्ग से function1_virtual कहा जाता है और जब फ़ंक्शन कॉल b-> function2_virtual () बनाया जाता है, क्योंकि यह फ़ंक्शन पॉइंटर बेस क्लास फ़ंक्शन, बेस क्लास फ़ंक्शन को इंगित करता है। कहा जाता है।
शुद्ध आभासी कार्य और सार वर्ग
हमने अपने पिछले भाग में C ++ में आभासी कार्यों के बारे में विवरण देखा है। C ++ में, हम एक “परिभाषित” भी कर सकते हैं शुद्ध आभासी कार्य “जो आमतौर पर शून्य के बराबर होता है।
शुद्ध वर्चुअल फ़ंक्शन को नीचे दिखाया गया है।
virtual return_type function_name(arg list) = 0;
जिस वर्ग में कम से कम एक शुद्ध आभासी फ़ंक्शन होता है जिसे 'कहा जाता है' अमूर्त वर्ग ”। हम कभी भी एब्सट्रैक्ट क्लास को नहीं रोक सकते हैं यानी हम एब्सट्रैक्ट क्लास का ऑब्जेक्ट नहीं बना सकते हैं।
ऐसा इसलिए है क्योंकि हम जानते हैं कि VTABLE (वर्चुअल टेबल) में हर वर्चुअल फंक्शन के लिए एक एंट्री होती है। लेकिन एक शुद्ध आभासी फ़ंक्शन के मामले में, यह प्रविष्टि किसी भी पते के बिना होती है और इस तरह इसे अधूरा प्रदान करती है। इसलिए कंपाइलर अपूर्ण VTABLE प्रविष्टि के साथ कक्षा के लिए एक ऑब्जेक्ट बनाने की अनुमति नहीं देता है।
यही वह कारण है जिसके लिए हम एक अमूर्त वर्ग को तात्कालिक नहीं कर सकते हैं।
नीचे का उदाहरण शुद्ध आभासी फ़ंक्शन के साथ-साथ सार वर्ग को प्रदर्शित करेगा।
#include using namespace std; class Base_abstract { public: virtual void print() = 0; // Pure Virtual Function }; class Derived_class:public Base_abstract { public: void print() { cout <<'Overriding pure virtual function in derived class
'; } }; int main() { // Base obj; //Compile Time Error Base_abstract *b; Derived_class d; b = &d; b->print(); }
आउटपुट:
व्युत्पन्न वर्ग में शुद्ध आभासी फ़ंक्शन को ओवरराइड करना
उपरोक्त कार्यक्रम में, हमारे पास बेस_ब्रेट के रूप में परिभाषित एक वर्ग है जिसमें एक शुद्ध आभासी फ़ंक्शन होता है जो इसे एक सार वर्ग बनाता है। तब हम Base_abstract से एक वर्ग 'Derived_class' प्राप्त करते हैं और इसमें शुद्ध आभासी फ़ंक्शन प्रिंट को ओवरराइड करते हैं।
मुख्य कार्य में, पहली पंक्ति पर टिप्पणी नहीं की जाती है। ऐसा इसलिए है क्योंकि यदि हम इसे अनसुना करते हैं, तो कंपाइलर एक त्रुटि देगा क्योंकि हम एक अमूर्त वर्ग के लिए ऑब्जेक्ट नहीं बना सकते हैं।
लेकिन दूसरी लाइन कोड काम करती है। हम सफलतापूर्वक बेस क्लास पॉइंटर बना सकते हैं और फिर हम व्युत्पन्न क्लास ऑब्जेक्ट को इसमें असाइन करते हैं। अगला, हम एक प्रिंट फ़ंक्शन कहते हैं जो व्युत्पन्न वर्ग में ओवरराइड किए गए प्रिंट फ़ंक्शन की सामग्री को आउटपुट करता है।
आइए हम संक्षेप में सार वर्ग की कुछ विशेषताओं को सूचीबद्ध करते हैं:
- हम एक अमूर्त वर्ग को तात्कालिक नहीं कर सकते।
- एक सार वर्ग में कम से कम एक शुद्ध आभासी फ़ंक्शन होता है।
- यद्यपि हम अमूर्त वर्ग को तात्कालिक नहीं कर सकते हैं, हम हमेशा इस वर्ग को संकेत या संदर्भ बना सकते हैं।
- एक अमूर्त वर्ग में कुछ कार्यान्वयन हो सकते हैं जैसे कि शुद्ध आभासी कार्यों के साथ गुण और तरीके।
- जब हम अमूर्त वर्ग से एक वर्ग प्राप्त करते हैं, तो व्युत्पन्न वर्ग को सार वर्ग में सभी शुद्ध आभासी कार्यों को ओवरराइड करना चाहिए। यदि यह ऐसा करने में विफल रहा, तो व्युत्पन्न वर्ग भी एक सार वर्ग होगा।
वर्चुअल डिस्ट्रक्टर्स
वर्ग के विध्वंसक को आभासी घोषित किया जा सकता है। जब भी हम upcast करते हैं यानी एक बेस क्लास पॉइंटर को व्युत्पन्न वर्ग ऑब्जेक्ट असाइन करते हैं, तो सामान्य विध्वंसक अस्वीकार्य परिणाम उत्पन्न कर सकते हैं।
उदाहरण के लिए,साधारण विध्वंसक के निम्नलिखित उत्थान पर विचार करें।
#include using namespace std; class Base { public: ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
आउटपुट:
बेस क्लास :: विध्वंसक
उपरोक्त कार्यक्रम में, हमारे पास बेस क्लास से विरासत में प्राप्त व्युत्पन्न वर्ग है। मुख्य में, हम व्युत्पन्न वर्ग के एक ऑब्जेक्ट को बेस क्लास पॉइंटर में असाइन करते हैं।
आदर्श रूप से, 'डिलीट बी' कहे जाने वाले विध्वंसक को व्युत्पन्न वर्ग के रूप में होना चाहिए था, लेकिन हम आउटपुट से देख सकते हैं कि बेस क्लास के विध्वंसक को बेस क्लास पॉइंटर पॉइंट कहा जाता है।
इसके कारण, व्युत्पन्न वर्ग विध्वंसक नहीं कहा जाता है और व्युत्पन्न वर्ग वस्तु बरकरार रहती है जिससे स्मृति रिसाव होता है। इसका हल बेस क्लास कंस्ट्रक्टर को वर्चुअल बनाना है ताकि ऑब्जेक्ट पॉइंटर को सही डिस्ट्रक्टर को पॉइंट किया जा सके और ऑब्जेक्ट्स का उचित विनाश किया जा सके।
आभासी विध्वंसक का उपयोग नीचे दिए गए उदाहरण में दिखाया गया है।
#include using namespace std; class Base { public: virtual ~Base() { cout << 'Base Class:: Destructor
'; } }; class Derived:public Base { public: ~Derived() { cout<< 'Derived class:: Destructor
'; } }; int main() { Base* b = new Derived; // Upcasting delete b; }
आउटपुट:
व्युत्पन्न वर्ग :: विनाशक
बेस क्लास :: विध्वंसक
यह पिछले प्रोग्राम जैसा ही प्रोग्राम है, सिवाय इसके कि हमने बेस क्लास डिस्ट्रक्टर के सामने एक वर्चुअल कीवर्ड जोड़ा है। बेस क्लास डिस्ट्रक्टर को आभासी बनाकर, हमने वांछित आउटपुट हासिल कर लिया है।
हम देख सकते हैं कि जब हम बेस क्लास पॉइंटर को व्युत्पन्न क्लास ऑब्जेक्ट असाइन करते हैं और फिर बेस क्लास पॉइंटर को हटाते हैं, तो डिस्ट्रक्टर्स को ऑब्जेक्ट क्रिएशन के रिवर्स ऑर्डर में बुलाया जाता है। इसका मतलब यह है कि पहले व्युत्पन्न वर्ग को नष्ट करने वाले को बुलाया जाता है और वस्तु को नष्ट कर दिया जाता है और फिर आधार वर्ग की वस्तु को नष्ट कर दिया जाता है।
ध्यान दें: C ++ में, कंस्ट्रक्टर कभी भी वर्चुअल नहीं हो सकते, क्योंकि कंस्ट्रक्टर ऑब्जेक्ट को बनाने और शुरू करने में शामिल होते हैं। इसलिए हमें सभी कंस्ट्रक्टरों को पूरी तरह से निष्पादित करने की आवश्यकता है।
निष्कर्ष
ओवरटाइमिंग विधि का उपयोग करके रनटाइम पॉलीमॉर्फिज़्म लागू किया जाता है। यह ठीक काम करता है जब हम तरीकों को उनके संबंधित वस्तुओं के साथ कहते हैं। लेकिन जब हमारे पास बेस क्लास पॉइंटर होता है और हम बेस क्लास पॉइंटर का उपयोग करके ओवरराइड विधियों को व्युत्पन्न क्लास ऑब्जेक्ट्स की ओर इशारा करते हैं, तो स्टेटिक लिंकिंग के कारण अप्रत्याशित परिणाम आते हैं।
इसे दूर करने के लिए, हम आभासी कार्यों की अवधारणा का उपयोग करते हैं। Vtables और _vptr के आंतरिक प्रतिनिधित्व के साथ, वर्चुअल फ़ंक्शन हमें वांछित कार्यों को सही ढंग से कॉल करने में मदद करते हैं। इस ट्यूटोरियल में, हमने C ++ में प्रयुक्त रनटाइम पॉलीमोर्फिज़्म के बारे में विस्तार से देखा है।
वेब अनुप्रयोगों के लिए स्वचालन परीक्षण उपकरण
इसके साथ, हम C ++ में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग पर अपने ट्यूटोरियल को समाप्त करते हैं। हमें उम्मीद है कि यह ट्यूटोरियल C ++ में ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग अवधारणाओं की बेहतर और गहन समझ हासिल करने में मददगार होगा।
=> स्क्रैच से C ++ जानने के लिए यहाँ जाएँ।
अनुशंसित पाठ
- C ++ में बहुरूपता
- C ++ में वंशानुक्रम
- मित्र कार्य C ++ में
- C ++ में क्लास और ऑब्जेक्ट
- वेब पेज पर ड्रॉपडाउन तत्वों को संभालने के लिए सेलेनियम सिलेक्ट क्लास का उपयोग - सेलेनियम ट्यूटोरियल # 13
- हाथों पर उदाहरण के साथ पायथन मेन फंक्शन ट्यूटोरियल
- जावा वर्चुअल मशीन: जावा एप्लीकेशन चलाने में JVM कैसे मदद करता है
- LoadRunner VuGen स्क्रिप्ट फ़ाइलों और रनटाइम सेटिंग्स को कैसे सेट करें