दूसरा जवाब क्यों?
वैसे, SO और लेखों के बाहर के कई पोस्ट कहते हैं, कि हीरे की समस्या A
दो (एक माता-पिता के लिए एक D
) के बजाय एक उदाहरण बनाकर हल की जाती है , इस प्रकार अस्पष्टता का समाधान होता है। हालांकि, इसने मुझे प्रक्रिया की व्यापक समझ नहीं दी, मैंने और भी अधिक प्रश्नों के साथ समाप्त किया
- क्या होगा
B
और विभिन्न मापदंडों ( ) के साथ पैराट्राइज्ड कंस्ट्रक्टर को कॉल C
करने के विभिन्न उदाहरण बनाने की कोशिश करता है ? का हिस्सा बनने के लिए किस उदाहरण को चुना जाएगा ?A
D::D(int x, int y): C(x), B(y) {}
A
D
- क्या होगा यदि मैं गैर-आभासी विरासत का उपयोग करता हूं
B
, लेकिन आभासी एक के लिए C
? क्या यह एकल उदाहरण बनाने के लिए पर्याप्त A
है D
?
- क्या मुझे हमेशा से अब तक निवारक उपाय के रूप में डिफ़ॉल्ट रूप से आभासी विरासत का उपयोग करना चाहिए क्योंकि यह मामूली प्रदर्शन लागत और अन्य कमियों के साथ संभव हीरे की समस्या को हल करता है?
कोड नमूनों की कोशिश किए बिना व्यवहार की भविष्यवाणी करने में सक्षम नहीं होने का मतलब अवधारणा को समझना नहीं है। नीचे मुझे आभासी विरासत के चारों ओर सिर लपेटने में मदद मिली है।
डबल एक
सबसे पहले, इस कोड के साथ आभासी विरासत के बिना शुरू करने देता है:
#include<iostream>
using namespace std;
class A {
public:
A() { cout << "A::A() "; }
A(int x) : m_x(x) { cout << "A::A(" << x << ") "; }
int getX() const { return m_x; }
private:
int m_x = 42;
};
class B : public A {
public:
B(int x):A(x) { cout << "B::B(" << x << ") "; }
};
class C : public A {
public:
C(int x):A(x) { cout << "C::C(" << x << ") "; }
};
class D : public C, public B {
public:
D(int x, int y): C(x), B(y) {
cout << "D::D(" << x << ", " << y << ") "; }
};
int main() {
cout << "Create b(2): " << endl;
B b(2); cout << endl << endl;
cout << "Create c(3): " << endl;
C c(3); cout << endl << endl;
cout << "Create d(2,3): " << endl;
D d(2, 3); cout << endl << endl;
// error: request for member 'getX' is ambiguous
//cout << "d.getX() = " << d.getX() << endl;
// error: 'A' is an ambiguous base of 'D'
//cout << "d.A::getX() = " << d.A::getX() << endl;
cout << "d.B::getX() = " << d.B::getX() << endl;
cout << "d.C::getX() = " << d.C::getX() << endl;
}
आउटपुट के माध्यम से चलें। निष्पादित B b(2);
बनाता है A(2)
के रूप में की उम्मीद के लिए, एक ही C c(3);
:
Create b(2):
A::A(2) B::B(2)
Create c(3):
A::A(3) C::C(3)
D d(2, 3);
दोनों की जरूरत है B
और C
उनमें से प्रत्येक अपना स्वयं का निर्माण कर रहा है A
, इसलिए हमारे पास इसमें दोगुना A
है d
:
Create d(2,3):
A::A(2) C::C(2) A::A(3) B::B(3) D::D(2, 3)
यही कारण है कि d.getX()
संकलन त्रुटि का कारण है क्योंकि संकलक यह नहीं चुन सकता है कि A
उसे किस विधि के लिए कॉल करना चाहिए। फिर भी चुने गए मूल वर्ग के लिए सीधे तौर पर कॉल करना संभव है:
d.B::getX() = 3
d.C::getX() = 2
Virtuality
अब वर्चुअल इनहेरिटेंस जोड़ने देता है। निम्नलिखित परिवर्तनों के साथ समान कोड नमूने का उपयोग करना:
class B : virtual public A
...
class C : virtual public A
...
cout << "d.getX() = " << d.getX() << endl; //uncommented
cout << "d.A::getX() = " << d.A::getX() << endl; //uncommented
...
निर्माण के लिए कूदें d
:
Create d(2,3):
A::A() C::C(2) B::B(3) D::D(2, 3)
आप देख सकते हैं, A
डिफॉल्ट कंस्ट्रक्टर के साथ बनाया गया है B
और कंस्ट्रक्टरों से पारित मापदंडों की अनदेखी कर रहा है C
। जैसा कि अस्पष्टता चली गई है, सभी getX()
समान मूल्य वापस करने के लिए कॉल करते हैं:
d.getX() = 42
d.A::getX() = 42
d.B::getX() = 42
d.C::getX() = 42
लेकिन क्या होगा अगर हम पैराट्राइज्ड कंस्ट्रक्टर को कॉल करना चाहते हैं A
? इसे स्पष्ट रूप से इसे कंस्ट्रक्टर के द्वारा कॉल करके किया जा सकता है D
:
D(int x, int y, int z): A(x), C(y), B(z)
आम तौर पर, वर्ग स्पष्ट रूप से केवल प्रत्यक्ष माता-पिता के निर्माणकर्ताओं का उपयोग कर सकता है, लेकिन आभासी विरासत के मामले के लिए एक बहिष्करण है। इस नियम की खोज ने मेरे लिए "क्लिक" किया और वर्चुअल इंटरफेस को समझने में बहुत मदद की:
कोड का class B: virtual A
अर्थ है, कि किसी भी वर्ग को विरासत में मिली B
ज़िम्मेदारी अब A
खुद ही बनाने की ज़िम्मेदारी है, क्योंकि B
यह स्वचालित रूप से करने वाला नहीं है।
इस कथन को ध्यान में रखते हुए मेरे पास मौजूद सभी प्रश्नों का उत्तर देना आसान है:
D
निर्माण के दौरान न तो B
और न ही C
मापदंडों के लिए जिम्मेदार है A
, यह D
केवल पूरी तरह से है ।
C
के निर्माण के प्रतिनिधि जाएगा A
करने के लिए D
, लेकिन B
के अपने स्वयं के उदाहरण पैदा करेगा A
वापस इस प्रकार लाने हीरा समस्या
- प्रत्यक्ष बच्चे के बजाय ग्रैंडचिल्ड क्लास में बेस क्लास मापदंडों को परिभाषित करना एक अच्छा अभ्यास नहीं है, इसलिए हीरे की समस्या होने पर इसे सहन किया जाना चाहिए और यह उपाय अपरिहार्य है।