वर्चुअल इनहेरिटेंस

आभासी विरासत एक सी ++ तकनीक है जो यह सुनिश्चित करती है कि आधार वर्ग की केवल एक प्रति सुनिश्चित करती है{{'}ग्रैंडचिल्ड व्युत्पन्न कक्षाओं द्वारा } सदस्य चर वंशानुक्रम (कंप्यूटर विज्ञान) हैं। आभासी विरासत के बिना, यदि दो वर्ग   और   एक वर्ग से विरासत में मिला , और एक वर्ग   दोनों से विरासत में मिलता है   और  , तब   की दो प्रतियाँ होंगी  's सदस्य चर: एक के माध्यम से  , और एक के माध्यम से. ये स्कोप रिज़ॉल्यूशन ऑपरेटर का उपयोग करके स्वतंत्र रूप से एक्सेस किए जा सकेंगे।

इसके बजाय, यदि कक्षाएं  और   वस्तुतः वर्ग से विरासत में मिलता है , फिर कक्षा की वस्तुएं   कक्षा से सदस्य चर का केवल एक सेट होगा.

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

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

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

इस स्थिति को कभी-कभी डायमंड इनहेरिटेंस (डायमंड प्रॉब्लम देखें) कहा जाता है क्योंकि इनहेरिटेंस डायग्राम डायमंड के आकार में होता है। वर्चुअल इनहेरिटेंस इस समस्या को हल करने में मदद कर सकता है।

समाधान
हम अपनी कक्षाओं को निम्नानुसार पुनः घोषित कर सकते हैं: ई> का हिस्सा  अब वही है   उदाहरण के रूप में द्वारा उपयोग किया जाता है , कहने का तात्पर्य यह है कि ए   केवल एक है, साझा किया,   इसके प्रतिनिधित्व में उदाहरण और इसलिए एक कॉल   असंदिग्ध है। इसके अतिरिक्त, से एक सीधा कलाकार   को   भी असंदिग्ध है, अब जबकि केवल एक ही मौजूद है   उदाहरण जो   में परिवर्तित किया जा सकता है।

का एक उदाहरण साझा करने की क्षमता  माता-पिता के बीच   और   के बीच मेमोरी ऑफ़सेट रिकॉर्ड करके सक्षम किया गया है   या   सदस्य और आधार के लोग   व्युत्पन्न वर्ग के भीतर। हालाँकि यह ऑफ़सेट सामान्य स्थिति में केवल रनटाइम पर ही जाना जा सकता है   बनना चाहिए. दो व्यवहार्य संकेत हैं, एक प्रति वंशानुक्रम पदानुक्रम जो वस्तुतः विरासत में मिलता है. इस उदाहरण में, एक के लिए  और एक के लिए. वस्तु का आकार इसलिए दो बिंदुओं से बढ़ गया है, लेकिन अब केवल एक ही है  और कोई अस्पष्टता नहीं। प्रकार की सभी वस्तुएं   एक ही vpointers का प्रयोग करेंगे, लेकिन प्रत्येक   ऑब्जेक्ट में अपना अनूठा होगा   वस्तु। यदि कोई अन्य वर्ग से प्राप्त होता है , जैसे कि  , फिर vpointer में   का हिस्सा   आम तौर पर vpointer से अलग होगा   का हिस्सा   हालांकि वे वही हो सकते हैं यदि   वर्ग के समान आकार का हो.

अनेक पूर्वजों का अतिरिक्त उदाहरण
यह उदाहरण एक ऐसे मामले को दिखाता है जहां बेस क्लास  एक कंस्ट्रक्टर चर है   और एक अतिरिक्त पूर्वज   पोते वर्ग से लिया गया है.  ए / \ बी सी \ / डी | इ  यहाँ,  दोनों में बनाया जाना चाहिए   और. इसके अलावा, चर का निरीक्षण  दिखाता है कि कैसे वर्ग   इसके व्युत्पन्न वर्ग का एक सीधा आधार वर्ग बन जाता है, जो किसी मध्यवर्ती व्युत्पन्न वर्ग के आधार वर्ग के विपरीत होता है   और अंतिम व्युत्पन्न वर्ग। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से खोजा जा सकता है यहां।
 * 1) शामिल <स्ट्रिंग>
 * 2) शामिल

एक कक्षा                    { निजी: एसटीडी :: स्ट्रिंग _msg; जनता: ए (एसटीडी :: स्ट्रिंग एक्स): _msg (एक्स) {} शून्य परीक्षण {std :: cout << A से हैलो: <<_msg << \n; } };

// बी, सी वस्तुतः ए को विरासत में मिला है क्लास बी: वर्चुअल पब्लिक ए {पब्लिक: बी (एसटीडी :: स्ट्रिंग एक्स): ए (बी) {}}; क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स): ए (सी) {}}; // संकलन त्रुटि जब: ए (सी) हटा दी जाती है (चूंकि ए के निर्माता को नहीं कहा जाता है) // क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स) {}}; // क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स) {ए (सी); }}; // समान संकलन त्रुटि

// चूँकि B, C को वस्तुतः A विरासत में मिला है, प्रत्येक बच्चे में A का निर्माण किया जाना चाहिए कक्षा डी: सार्वजनिक बी, सी {सार्वजनिक: डी (एसटीडी :: स्ट्रिंग एक्स): ए (डी_ए), बी (डी_बी), सी (डी_सी) {}}; कक्षा ई: सार्वजनिक डी {सार्वजनिक: ई (एसटीडी :: स्ट्रिंग एक्स): ए (ई_ए), डी (ई_डी) {}};

// ए के निर्माण के बिना संकलन त्रुटि // कक्षा डी: सार्वजनिक बी, सी {सार्वजनिक: डी (एसटीडी :: स्ट्रिंग एक्स): बी (एक्स), सी (एक्स) {}};

// ए के निर्माण के बिना संकलन त्रुटि // कक्षा ई: सार्वजनिक डी {सार्वजनिक: ई (एसटीडी :: स्ट्रिंग एक्स): डी (एक्स) {}};

इंट मेन (इंट एआरजीसी, चार ** एआरजीवी) { डी डी (डी); डी. टेस्ट ; // हैलो ए से: d_a

ई ई (ई); ई.टेस्ट ; // ए से हैलो: e_a } 

शुद्ध आभासी तरीके
मान लीजिए कि आधार वर्ग में एक शुद्ध आभासी विधि परिभाषित है। यदि एक व्युत्पन्न वर्ग वस्तुतः आधार वर्ग को प्राप्त करता है, तो उस व्युत्पन्न वर्ग में शुद्ध आभासी विधि को परिभाषित करने की आवश्यकता नहीं होती है। हालाँकि, यदि व्युत्पन्न वर्ग आधार वर्ग को वस्तुतः इनहेरिट नहीं करता है, तो सभी आभासी विधियों को परिभाषित किया जाना चाहिए। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से एक्सप्लोर किया जा सकता है यहां।
 * 1) शामिल <स्ट्रिंग>
 * 2) शामिल

एक कक्षा                    { संरक्षित: एसटीडी :: स्ट्रिंग _msg; जनता: ए (एसटीडी :: स्ट्रिंग एक्स): _msg (एक्स) {} शून्य परीक्षण {std :: cout << A से हैलो: <<_msg << \n; } आभासी शून्य शुद्ध_वर्चुअल_टेस्ट = 0; };

// चूंकि बी, सी वर्चुअल रूप से विरासत में मिला है, शुद्ध वर्चुअल विधि Pure_virtual_test को परिभाषित करने की आवश्यकता नहीं है क्लास बी: वर्चुअल पब्लिक ए {पब्लिक: बी (एसटीडी :: स्ट्रिंग एक्स): ए (बी) {}}; क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स): ए (सी) {}};

// चूंकि बी, सी ए को विरासत में मिला है, प्रत्येक बच्चे में ए का निर्माण किया जाना चाहिए // हालांकि, चूंकि डी वास्तव में बी, सी का उत्तराधिकारी नहीं है, ए में शुद्ध आभासी विधि * को परिभाषित किया जाना चाहिए * कक्षा डी: सार्वजनिक बी, सी { जनता: डी (एसटीडी :: स्ट्रिंग एक्स): ए (डी_ए), बी (डी_बी), सी (डी_सी) {} शून्य Pure_virtual_test ओवरराइड {std::cout<< शुद्ध वर्चुअल हैलो फ्रॉम: <<_msg << \n; } };

// माता-पिता द्वारा इसे परिभाषित करने के बाद शुद्ध आभासी पद्धति को फिर से परिभाषित करना आवश्यक नहीं है कक्षा ई: सार्वजनिक डी { जनता: ई (एसटीडी :: स्ट्रिंग एक्स): ए (ई_ए), डी (ई_डी) {} };

इंट मेन (इंट एआरजीसी, चार ** एआरजीवी) { डी डी (डी); डी. टेस्ट ; // हैलो ए से: d_a d.pure_virtual_test ; // शुद्ध वर्चुअल हैलो फ्रॉम: d_a

ई ई (ई); ई.टेस्ट ; // ए से हैलो: e_a e.pure_virtual_test ; // शुद्ध वर्चुअल हैलो फ्रॉम: e_a } 

संदर्भ
Multipelt arv Çoklu kalıtım