वर्चुअल इनहेरिटेंस: Difference between revisions

From Vigyanwiki
(Created page with "{{For|inheritance of virtual functions|virtual function}} thumb|[[ हीरा विरासत का डायग्राम, एक...")
 
No edit summary
Tag: Manual revert
 
(7 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{For|inheritance of virtual functions|virtual function}}
[[File:Diamond inheritance.svg|thumb|[[ हीरा विरासत | हीरा इनहेरिटेंस]] का डायग्राम, एक ऐसी समस्या जिसे वर्चुअल इनहेरिटेंस हल करने की कोशिश कर रहा है]]वर्चुअल इनहेरिटेंस एक [[सी ++]] तकनीक है जो यह सुनिश्चित करती है कि [[आधार वर्ग]] की केवल एक प्रति सुनिश्चित करती है{{'}ग्रैंडचिल्ड व्युत्पन्न कक्षाओं द्वारा } सदस्य चर [[वंशानुक्रम (कंप्यूटर विज्ञान)]] हैं। वर्चुअल इनहेरिटेंस के बिना, यदि दो वर्ग <code>B</code> और <code>C</code> एक वर्ग से इनहेरिटेंस में मिला <code>A</code>, और एक वर्ग <code>D</code> दोनों से इनहेरिटेंस में मिलता है <code>B</code> और <code>C</code>, तब <code>D</code> की दो प्रतियाँ होंगी <code>A</code>{{'}}s सदस्य चर: एक के माध्यम से <code>B</code>, और एक के माध्यम से <code>C</code>. ये [[स्कोप रिज़ॉल्यूशन ऑपरेटर]] का उपयोग करके स्वतंत्र रूप से एक्सेस किए जा सकेंगे।
[[File:Diamond inheritance.svg|thumb|[[ हीरा विरासत ]] का डायग्राम, एक ऐसी समस्या जिसे वर्चुअल इनहेरिटेंस हल करने की कोशिश कर रहा है]]वर्चुअल इनहेरिटेंस एक [[सी ++]] तकनीक है जो [[आधार वर्ग]] की केवल एक प्रति सुनिश्चित करती है{{'}ग्रैंडचिल्ड व्युत्पन्न कक्षाओं द्वारा } सदस्य चर [[वंशानुक्रम (कंप्यूटर विज्ञान)]] हैं। आभासी विरासत के बिना, यदि दो वर्ग <code>B</code> और <code>C</code> एक वर्ग से विरासत में मिला <code>A</code>, और एक वर्ग <code>D</code> दोनों से विरासत में मिलता है <code>B</code> और <code>C</code>, तब <code>D</code> की दो प्रतियाँ होंगी <code>A</code>{{'}}s सदस्य चर: एक के माध्यम से <code>B</code>, और एक के माध्यम से <code>C</code>. ये [[स्कोप रिज़ॉल्यूशन ऑपरेटर]] का उपयोग करके स्वतंत्र रूप से एक्सेस किए जा सकेंगे।


इसके बजाय, यदि कक्षाएं <code>B</code> और <code>C</code> वस्तुतः वर्ग से विरासत में मिलता है <code>A</code>, फिर कक्षा की वस्तुएं <code>D</code> कक्षा से सदस्य चर का केवल एक सेट होगा <code>A</code>.
इसके बजाय, यदि कक्षाएं <code>B</code> और <code>C</code> वस्तुतः वर्ग से इनहेरिटेंस में मिलता है <code>A</code>, फिर कक्षा की वस्तुएं <code>D</code> कक्षा से सदस्य चर का केवल एक सेट होगा <code>A</code>.


यह सुविधा [[एकाधिक वंशानुक्रम]] के लिए सबसे उपयोगी है, क्योंकि यह व्युत्पन्न वर्ग और इससे प्राप्त होने वाले सभी वर्गों के लिए वर्चुअल आधार को एक सामान्य सबोबिज बनाता है। इसका उपयोग हीरे की समस्या से बचने के लिए अस्पष्टता को स्पष्ट करने के लिए किया जा सकता है कि किस पूर्वज वर्ग का उपयोग किया जाए, जैसा कि व्युत्पन्न वर्ग के दृष्टिकोण से (<code>D</code> ऊपर के उदाहरण में) आभासी आधार (<code>A</code>) कार्य करता है जैसे कि यह प्रत्यक्ष आधार वर्ग था <code>D</code>, आधार के माध्यम से अप्रत्यक्ष रूप से व्युत्पन्न वर्ग नहीं (<code>B</code> या <code>C</code>).<ref>{{cite web
यह सुविधा [[एकाधिक वंशानुक्रम]] के लिए सबसे उपयोगी है, क्योंकि यह व्युत्पन्न वर्ग और इससे प्राप्त होने वाले सभी वर्गों के लिए वर्चुअल आधार को एक सामान्य सबोबिज बनाता है। इसका उपयोग हीरे की समस्या से बचने के लिए अस्पष्टता को स्पष्ट करने के लिए किया जा सकता है कि किस पूर्वज वर्ग का उपयोग किया जाए, जैसा कि व्युत्पन्न वर्ग के दृष्टिकोण से (<code>D</code> ऊपर के उदाहरण में) वर्चुअल आधार (<code>A</code>) कार्य करता है जैसे कि यह प्रत्यक्ष आधार वर्ग था <code>D</code>, आधार के माध्यम से अप्रत्यक्ष रूप से व्युत्पन्न वर्ग नहीं (<code>B</code> या <code>C</code>).<ref>{{cite web
| access-date = 2010-03-08
| access-date = 2010-03-08
| website = Cprogramming.com
| website = Cprogramming.com
Line 61: Line 60:
कॉल करने के लिए <code>Eat</code>, वही असंबद्धता, या स्पष्ट योग्यता आवश्यक है: <code>static_cast<Mammal&>(bat).Eat()</code> या <code>static_cast<WingedAnimal&>(bat).Eat()</code> या वैकल्पिक रूप से <code>bat.Mammal::Eat()</code> और <code>bat.WingedAnimal::Eat()</code>. स्पष्ट योग्यता न केवल पॉइंटर्स और ऑब्जेक्ट्स दोनों के लिए एक आसान, समान सिंटैक्स का उपयोग करती है, बल्कि स्थिर प्रेषण की भी अनुमति देती है, इसलिए यह यकीनन बेहतर तरीका होगा।
कॉल करने के लिए <code>Eat</code>, वही असंबद्धता, या स्पष्ट योग्यता आवश्यक है: <code>static_cast<Mammal&>(bat).Eat()</code> या <code>static_cast<WingedAnimal&>(bat).Eat()</code> या वैकल्पिक रूप से <code>bat.Mammal::Eat()</code> और <code>bat.WingedAnimal::Eat()</code>. स्पष्ट योग्यता न केवल पॉइंटर्स और ऑब्जेक्ट्स दोनों के लिए एक आसान, समान सिंटैक्स का उपयोग करती है, बल्कि स्थिर प्रेषण की भी अनुमति देती है, इसलिए यह यकीनन बेहतर तरीका होगा।


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


इस स्थिति को कभी-कभी डायमंड इनहेरिटेंस (डायमंड प्रॉब्लम देखें) कहा जाता है क्योंकि इनहेरिटेंस डायग्राम डायमंड के आकार में होता है। वर्चुअल इनहेरिटेंस इस समस्या को हल करने में मदद कर सकता है।
इस स्थिति को कभी-कभी डायमंड इनहेरिटेंस (डायमंड प्रॉब्लम देखें) कहा जाता है क्योंकि इनहेरिटेंस डायग्राम डायमंड के आकार में होता है। वर्चुअल इनहेरिटेंस इस समस्या को हल करने में मदद कर सकता है।
Line 88: Line 87:
  <code>Animal</code> ई> का हिस्सा <code>Bat::WingedAnimal</code> अब वही है <code>Animal</code> उदाहरण के रूप में द्वारा उपयोग किया जाता है <code>Bat::Mammal</code>, कहने का तात्पर्य यह है कि ए <code>Bat</code> केवल एक है, साझा किया, <code>Animal</code> इसके प्रतिनिधित्व में उदाहरण और इसलिए एक कॉल <code>Bat::Eat</code> असंदिग्ध है। इसके अतिरिक्त, से एक सीधा कलाकार <code>Bat</code> को <code>Animal</code> भी असंदिग्ध है, अब जबकि केवल एक ही मौजूद है <code>Animal</code> उदाहरण जो <code>Bat</code> में परिवर्तित किया जा सकता है।
  <code>Animal</code> ई> का हिस्सा <code>Bat::WingedAnimal</code> अब वही है <code>Animal</code> उदाहरण के रूप में द्वारा उपयोग किया जाता है <code>Bat::Mammal</code>, कहने का तात्पर्य यह है कि ए <code>Bat</code> केवल एक है, साझा किया, <code>Animal</code> इसके प्रतिनिधित्व में उदाहरण और इसलिए एक कॉल <code>Bat::Eat</code> असंदिग्ध है। इसके अतिरिक्त, से एक सीधा कलाकार <code>Bat</code> को <code>Animal</code> भी असंदिग्ध है, अब जबकि केवल एक ही मौजूद है <code>Animal</code> उदाहरण जो <code>Bat</code> में परिवर्तित किया जा सकता है।


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


=== अनेक पूर्वजों का अतिरिक्त उदाहरण ===
=== अनेक पूर्वजों का अतिरिक्त उदाहरण ===
यह उदाहरण एक ऐसे मामले को दिखाता है जहां बेस क्लास <code>A</code> एक कंस्ट्रक्टर चर है <code>msg</code> और एक अतिरिक्त पूर्वज <code>E</code> पोते वर्ग से लिया गया है <code>D</code>.
यह उदाहरण एक ऐसे मामले को दिखाता है जहां बेस क्लास <code>A</code> एक कंस्ट्रक्टर चर है <code>msg</code> और एक अतिरिक्त पूर्वज <code>E</code> पोते वर्ग से लिया गया है <code>D</code>.
<पूर्व>
<pre>
  
  
  / \
  / \
बी सी
B  C 
  \ /
  \ /
   डी
   D
   |
   |
  
   E
</पूर्व>
</pre>
यहाँ, <code>A</code> दोनों में बनाया जाना चाहिए <code>D</code> और <code>E</code>. इसके अलावा, चर का निरीक्षण <code>msg</code> दिखाता है कि कैसे वर्ग <code>A</code> इसके व्युत्पन्न वर्ग का एक सीधा आधार वर्ग बन जाता है, जो किसी मध्यवर्ती व्युत्पन्न वर्ग के आधार वर्ग के विपरीत होता है <code>A</code> और अंतिम व्युत्पन्न वर्ग। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से खोजा जा सकता है [https://godbolt.org/z/WGfa9bYG7 यहां]।<syntaxhighlight lang= c++ >
यहाँ, <code>A</code> दोनों में बनाया जाना चाहिए <code>D</code> और <code>E</code>. इसके अलावा, चर का निरीक्षण <code>msg</code> दिखाता है कि कैसे वर्ग <code>A</code> इसके व्युत्पन्न वर्ग का एक सीधा आधार वर्ग बन जाता है, जो किसी मध्यवर्ती व्युत्पन्न वर्ग के आधार वर्ग के विपरीत होता है <code>A</code> और अंतिम व्युत्पन्न वर्ग। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से खोजा जा सकता है [https://godbolt.org/z/WGfa9bYG7 यहां]।<syntaxhighlight lang="c++">
#शामिल <स्ट्रिंग>
#include <string>
#शामिल <iostream>
#include <iostream>


एक कक्षा                     {
class A                     {  
     निजी:
     private:  
         एसटीडी :: स्ट्रिंग _msg;
         std::string _msg;  
     जनता:
     public:
         (एसटीडी :: स्ट्रिंग एक्स): _msg (एक्स) {}
         A(std::string x): _msg(x) {}  
         शून्य परीक्षण () {std :: cout << A से हैलो: <<_msg << \n; }
         void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; }
};
};  


// बी, सी वस्तुतः ए को विरासत में मिला है
// B,C inherit A virtually
क्लास बी: वर्चुअल पब्लिक ए {पब्लिक: बी (एसटीडी :: स्ट्रिंग एक्स): (बी) {}};
class B: virtual public A  { public: B(std::string x):A("b"){} };
क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स): (सी) {}};
class C: virtual public A  { public: C(std::string x):A("c"){} };
// संकलन त्रुटि जब: (सी) हटा दी जाती है (चूंकि ए के निर्माता को नहीं कहा जाता है)
// Compile error when :A("c") is removed (since A's constructor is not called)
// क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स) {}};
//class C: virtual public A  { public: C(std::string x){} };
// क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स) {(सी); }}; // समान संकलन त्रुटि
//class C: virtual public A  { public: C(std::string x){ A("c"); } }; // Same compile error


// चूँकि B, C को वस्तुतः A विरासत में मिला है, प्रत्येक बच्चे में A का निर्माण किया जाना चाहिए
// Since B, C inherit A virtually, A must be constructed in each child
कक्षा डी: सार्वजनिक बी, सी {सार्वजनिक: डी (एसटीडी :: स्ट्रिंग एक्स): (डी_ए), बी (डी_बी), सी (डी_सी) {}};
class D: public        B,C { public: D(std::string x):A("d_a"),B("d_b"),C("d_c"){} };  
कक्षा ई: सार्वजनिक डी {सार्वजनिक: (एसटीडी :: स्ट्रिंग एक्स): (ई_ए), डी (ई_डी) {}};
class E: public        D  { public: E(std::string x):A("e_a"),D("e_d"){} };  


// ए के निर्माण के बिना संकलन त्रुटि
// Compile error without constructing A
// कक्षा डी: सार्वजनिक बी, सी {सार्वजनिक: डी (एसटीडी :: स्ट्रिंग एक्स): बी (एक्स), सी (एक्स) {}};
//class D: public        B,C { public: D(std::string x):B(x),C(x){} };


// ए के निर्माण के बिना संकलन त्रुटि
// Compile error without constructing A
// कक्षा ई: सार्वजनिक डी {सार्वजनिक: (एसटीडी :: स्ट्रिंग एक्स): डी (एक्स) {}};
//class E: public        D  { public: E(std::string x):D(x){} };




इंट मेन (इंट एआरजीसी, चार ** एआरजीवी) {
int main(int argc, char ** argv){
     डी डी (डी);
     D d("d");  
     डी. टेस्ट (); // हैलो ए से: d_a
     d.test(); // hello from A: d_a


     ई ई ();
     E e("e");  
     .टेस्ट (); // ए से हैलो: e_a
     e.test(); // hello from A: e_a
}
}
</वाक्यविन्यास हाइलाइट>
</syntaxhighlight>


==== शुद्ध आभासी तरीके ====
==== शुद्ध वर्चुअल तरीके ====


मान लीजिए कि आधार वर्ग में एक शुद्ध आभासी विधि परिभाषित है। यदि एक व्युत्पन्न वर्ग वस्तुतः आधार वर्ग को प्राप्त करता है, तो उस व्युत्पन्न वर्ग में शुद्ध आभासी विधि को परिभाषित करने की आवश्यकता नहीं होती है। हालाँकि, यदि व्युत्पन्न वर्ग आधार वर्ग को वस्तुतः इनहेरिट नहीं करता है, तो सभी आभासी विधियों को परिभाषित किया जाना चाहिए। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से एक्सप्लोर किया जा सकता है [https://godbolt.org/z/3nrejWfEW यहां]।<syntaxhighlight lang= c++ >
मान लीजिए कि आधार वर्ग में एक शुद्ध वर्चुअल विधि परिभाषित है। यदि एक व्युत्पन्न वर्ग वस्तुतः आधार वर्ग को प्राप्त करता है, तो उस व्युत्पन्न वर्ग में शुद्ध वर्चुअल विधि को परिभाषित करने की आवश्यकता नहीं होती है। हालाँकि, यदि व्युत्पन्न वर्ग आधार वर्ग को वस्तुतः इनहेरिट नहीं करता है, तो सभी वर्चुअल विधियों को परिभाषित किया जाना चाहिए। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से एक्सप्लोर किया जा सकता है [https://godbolt.org/z/3nrejWfEW यहां]।<syntaxhighlight lang="c++">
#शामिल <स्ट्रिंग>
#include <string>
#शामिल <iostream>
#include <iostream>


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


// चूंकि बी, सी वर्चुअल रूप से विरासत में मिला है, शुद्ध वर्चुअल विधि Pure_virtual_test को परिभाषित करने की आवश्यकता नहीं है
// since B,C inherit A virtually, the pure virtual method pure_virtual_test doesn't need to be defined
क्लास बी: वर्चुअल पब्लिक ए {पब्लिक: बी (एसटीडी :: स्ट्रिंग एक्स): (बी) {}};
class B: virtual public A  { public: B(std::string x):A("b"){} };  
क्लास सी: वर्चुअल पब्लिक ए {पब्लिक: सी (एसटीडी :: स्ट्रिंग एक्स): (सी) {}};
class C: virtual public A  { public: C(std::string x):A("c"){} };  


// चूंकि बी, सी ए को विरासत में मिला है, प्रत्येक बच्चे में ए का निर्माण किया जाना चाहिए
// since B,C inherit A virtually, A must be constructed in each child
// हालांकि, चूंकि डी वास्तव में बी, सी का उत्तराधिकारी नहीं है, ए में शुद्ध आभासी विधि * को परिभाषित किया जाना चाहिए *
// however, since D does not inherit B,C virtually, the pure virtual method in A *must be defined*
कक्षा डी: सार्वजनिक बी, सी {
class D: public B,C {  
     जनता:
     public:  
         डी (एसटीडी :: स्ट्रिंग एक्स): (डी_ए), बी (डी_बी), सी (डी_सी) {}
         D(std::string x):A("d_a"),B("d_b"),C("d_c"){}
         शून्य Pure_virtual_test() ओवरराइड {std::cout<< शुद्ध वर्चुअल हैलो फ्रॉम: <<_msg << \n; }
         void pure_virtual_test() override { std::cout<<"pure virtual hello from: "<<_msg <<"\n"; }
};
};  


// माता-पिता द्वारा इसे परिभाषित करने के बाद शुद्ध आभासी पद्धति को फिर से परिभाषित करना आवश्यक नहीं है
// it is not necessary to redefine the pure virtual method after the parent defines it
कक्षा ई: सार्वजनिक डी {
class E: public D {  
     जनता:
     public:  
     (एसटीडी :: स्ट्रिंग एक्स): (ई_ए), डी (ई_डी) {}
     E(std::string x):A("e_a"),D("e_d"){}
};
};




इंट मेन (इंट एआरजीसी, चार ** एआरजीवी) {
int main(int argc, char ** argv){
     डी डी (डी);
     D d("d");
     डी. टेस्ट (); // हैलो ए से: d_a
     d.test(); // hello from A: d_a
     d.pure_virtual_test (); // शुद्ध वर्चुअल हैलो फ्रॉम: d_a
     d.pure_virtual_test(); // pure virtual hello from: d_a


     ई ई ();
     E e("e");  
     .टेस्ट (); // ए से हैलो: e_a
     e.test(); // hello from A: e_a
     e.pure_virtual_test (); // शुद्ध वर्चुअल हैलो फ्रॉम: e_a
     e.pure_virtual_test(); // pure virtual hello from: e_a
}
}
</वाक्यविन्यास हाइलाइट>
</syntaxhighlight>


==संदर्भ==
==संदर्भ==
{{reflist}}
{{reflist}}
{{C++ programming language}}
[[Category: वर्ग (कंप्यूटर प्रोग्रामिंग)]] [[Category: सी ++]]
[[sv:Multipelt arv]]
[[sv:Multipelt arv]]
[[tr:Çoklu kalıtım]]
[[tr:Çoklu kalıtım]]


 
[[Category:Articles with hatnote templates targeting a nonexistent page]]
 
[[Category:Collapse templates]]
[[Category: Machine Translated Page]]
[[Category:Created On 02/03/2023]]
[[Category:Created On 02/03/2023]]
[[Category:Machine Translated Page]]
[[Category:Navigational boxes| ]]
[[Category:Navigational boxes without horizontal lists]]
[[Category:Pages with script errors]]
[[Category:Sidebars with styles needing conversion]]
[[Category:Template documentation pages|Documentation/doc]]
[[Category:Templates Vigyan Ready]]

Latest revision as of 07:08, 19 March 2023

हीरा इनहेरिटेंस का डायग्राम, एक ऐसी समस्या जिसे वर्चुअल इनहेरिटेंस हल करने की कोशिश कर रहा है

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

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

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

निम्नलिखित वर्ग पदानुक्रम पर विचार करें।

struct Animal {
    virtual ~Animal() = default;
    virtual void Eat() {}
};

struct Mammal: Animal {
    virtual void Breathe() {}
};

struct WingedAnimal: Animal {
    virtual void Flap() {}
};

// A bat is a winged mammal
struct Bat: Mammal, WingedAnimal {};

Bat bat;

जैसा कि ऊपर घोषित किया गया है, को कॉल करें bat.Eat अस्पष्ट है क्योंकि दो हैं Animal (अप्रत्यक्ष) आधार वर्ग में Bat, कोई भी Bat वस्तु के दो भिन्न होते हैं Animal बेस क्लास सबऑब्जेक्ट्स। तो एक संदर्भ को सीधे बाँधने का प्रयास Animal ए का विषय Bat वस्तु विफल हो जाएगी, क्योंकि बाध्यकारी स्वाभाविक अस्पष्ट है:

Bat b;
Animal& a = b;  // error: which Animal subobject should a Bat cast into, 
                // a Mammal::Animal or a WingedAnimal::Animal?

असंबद्ध करने के लिए, किसी को स्पष्ट रूप से परिवर्तित करना होगा bat या तो बेस क्लास सबऑब्जेक्ट के लिए:

Bat b;
Animal& mammal = static_cast<Mammal&>(b); 
Animal& winged = static_cast<WingedAnimal&>(b);

कॉल करने के लिए Eat, वही असंबद्धता, या स्पष्ट योग्यता आवश्यक है: static_cast<Mammal&>(bat).Eat() या static_cast<WingedAnimal&>(bat).Eat() या वैकल्पिक रूप से bat.Mammal::Eat() और bat.WingedAnimal::Eat(). स्पष्ट योग्यता न केवल पॉइंटर्स और ऑब्जेक्ट्स दोनों के लिए एक आसान, समान सिंटैक्स का उपयोग करती है, बल्कि स्थिर प्रेषण की भी अनुमति देती है, इसलिए यह यकीनन बेहतर तरीका होगा।

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

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

समाधान

हम अपनी कक्षाओं को निम्नानुसार पुनः घोषित कर सकते हैं:

struct Animal {
    virtual ~Animal() = default;
    virtual void Eat() {}
};

// Two classes virtually inheriting Animal:
struct Mammal: virtual Animal {
    virtual void Breathe() {}
};

struct WingedAnimal: virtual Animal {
    virtual void Flap() {}
};

// A bat is still a winged mammal
struct Bat: Mammal, WingedAnimal {};
Animal ई> का हिस्सा Bat::WingedAnimal अब वही है Animal उदाहरण के रूप में द्वारा उपयोग किया जाता है Bat::Mammal, कहने का तात्पर्य यह है कि ए Bat केवल एक है, साझा किया, Animal इसके प्रतिनिधित्व में उदाहरण और इसलिए एक कॉल Bat::Eat असंदिग्ध है। इसके अतिरिक्त, से एक सीधा कलाकार Bat को Animal भी असंदिग्ध है, अब जबकि केवल एक ही मौजूद है Animal उदाहरण जो Bat में परिवर्तित किया जा सकता है।

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

अनेक पूर्वजों का अतिरिक्त उदाहरण

यह उदाहरण एक ऐसे मामले को दिखाता है जहां बेस क्लास A एक कंस्ट्रक्टर चर है msg और एक अतिरिक्त पूर्वज E पोते वर्ग से लिया गया है D.

  A  
 / \  
B   C  
 \ /  
  D 
  |
  E

यहाँ, A दोनों में बनाया जाना चाहिए D और E. इसके अलावा, चर का निरीक्षण msg दिखाता है कि कैसे वर्ग A इसके व्युत्पन्न वर्ग का एक सीधा आधार वर्ग बन जाता है, जो किसी मध्यवर्ती व्युत्पन्न वर्ग के आधार वर्ग के विपरीत होता है A और अंतिम व्युत्पन्न वर्ग। नीचे दिए गए कोड को अंतःक्रियात्मक रूप से खोजा जा सकता है यहां

#include <string>
#include <iostream>

class A                     { 
    private: 
        std::string _msg; 
    public:
        A(std::string x): _msg(x) {} 
        void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; }
}; 

// B,C inherit A virtually
class B: virtual public A   { public: B(std::string x):A("b"){}  };
class C: virtual public A   { public: C(std::string x):A("c"){}  };
// Compile error when :A("c") is removed (since A's constructor is not called)
//class C: virtual public A   { public: C(std::string x){}  };
//class C: virtual public A   { public: C(std::string x){ A("c"); }  }; // Same compile error

// Since B, C inherit A virtually, A must be constructed in each child
class D: public         B,C { public: D(std::string x):A("d_a"),B("d_b"),C("d_c"){}  }; 
class E: public         D   { public: E(std::string x):A("e_a"),D("e_d"){}  }; 

// Compile error without constructing A
//class D: public         B,C { public: D(std::string x):B(x),C(x){}  };

// Compile error without constructing A
//class E: public         D   { public: E(std::string x):D(x){}  };


int main(int argc, char ** argv){
    D d("d"); 
    d.test(); // hello from A: d_a

    E e("e"); 
    e.test(); // hello from A: e_a
}

शुद्ध वर्चुअल तरीके

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

#include <string>
#include <iostream>

class A                     { 
    protected: 
        std::string _msg; 
    public:
        A(std::string x): _msg(x) {} 
        void test(){ std::cout<<"hello from A: "<<_msg <<"\n"; } 
        virtual void pure_virtual_test() = 0;
}; 

// since B,C inherit A virtually, the pure virtual method pure_virtual_test doesn't need to be defined
class B: virtual public A   { public: B(std::string x):A("b"){}  }; 
class C: virtual public A   { public: C(std::string x):A("c"){}  }; 

// since B,C inherit A virtually, A must be constructed in each child
// however, since D does not inherit B,C virtually, the pure virtual method in A *must be defined*
class D: public B,C { 
    public: 
        D(std::string x):A("d_a"),B("d_b"),C("d_c"){}
        void pure_virtual_test() override { std::cout<<"pure virtual hello from: "<<_msg <<"\n"; }
}; 

// it is not necessary to redefine the pure virtual method after the parent defines it
class E: public D { 
    public: 
    E(std::string x):A("e_a"),D("e_d"){}  
};


int main(int argc, char ** argv){
    D d("d");
    d.test(); // hello from A: d_a
    d.pure_virtual_test(); // pure virtual hello from: d_a

    E e("e"); 
    e.test(); // hello from A: e_a
    e.pure_virtual_test(); // pure virtual hello from: e_a
}

संदर्भ

  1. Milea, Andrei. "Solving the Diamond Problem with Virtual Inheritance". Cprogramming.com. Retrieved 2010-03-08. One of the problems that arises due to multiple inheritance is the diamond problem. A classical illustration of this is given by Bjarne Stroustrup (the creator of C++) in the following example:
  2. McArdell, Ralph (2004-02-14). "C++/What is virtual inheritance?". All Experts. Archived from the original on 2010-01-10. Retrieved 2010-03-08. This is something you find may be required if you are using multiple inheritance. In that case it is possible for a class to be derived from other classes which have the same base class. In such cases, without virtual inheritance, your objects will contain more than one subobject of the base type the base classes share. Whether this is what is the required effect depends on the circumstances. If it is not then you can use virtual inheritance by specifying virtual base classes for those base types for which a whole object should only contain one such base class subobject.