दूसरा जवाब क्यों?
वैसे, SO और लेखों के बाहर के कई पोस्ट कहते हैं, कि हीरे की समस्या Aदो (एक माता-पिता के लिए एक D) के बजाय एक उदाहरण बनाकर हल की जाती है , इस प्रकार अस्पष्टता का समाधान होता है। हालांकि, इसने मुझे प्रक्रिया की व्यापक समझ नहीं दी, मैंने और भी अधिक प्रश्नों के साथ समाप्त किया
- क्या होगा
Bऔर विभिन्न मापदंडों ( ) के साथ पैराट्राइज्ड कंस्ट्रक्टर को कॉल Cकरने के विभिन्न उदाहरण बनाने की कोशिश करता है ? का हिस्सा बनने के लिए किस उदाहरण को चुना जाएगा ?AD::D(int x, int y): C(x), B(y) {}AD
- क्या होगा यदि मैं गैर-आभासी विरासत का उपयोग करता हूं
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वापस इस प्रकार लाने हीरा समस्या
- प्रत्यक्ष बच्चे के बजाय ग्रैंडचिल्ड क्लास में बेस क्लास मापदंडों को परिभाषित करना एक अच्छा अभ्यास नहीं है, इसलिए हीरे की समस्या होने पर इसे सहन किया जाना चाहिए और यह उपाय अपरिहार्य है।