2017-04-24 6 views
12

पर विचार करें:यह हीरा वर्ग विरासत आउटपुट क्यों नहीं है जो मैं उम्मीद करता हूं?

#include <iostream> 

using namespace std; 

class A {// base class 
private: 
    int data; 
public: 
    A(int data = 0) 
    { 
     this->data = data; 
    } 
    void show() 
    { 
     cout << data << endl; 
     return; 
    } 
}; 

class B : virtual public A { 
public: 
    B(int data = 0) : 
     A(data) { 
    } 
}; 

class C : virtual public A { 
public: 
    C(int data = 0) : 
     A(data) { 
    } 
}; 

class D : public B, public C { 
public: 
    D(int dataB = 0, int dataC = 0) : 
     B(dataB), 
     C(dataC) { 
    } 
}; 

int main() { 
    D d(1, 2); 
    d.B::show(); 
    d.C::show(); 
    return 0; 
} 

ऊपर कोड हीरा वर्ग विरासत आरेख है। बेस क्लास ए है। मैं हीरे की समस्या से बचने के लिए वर्चुअल विरासत का उपयोग करता हूं। लेकिन इस कार्यक्रम का आउटपुट 0,0 क्यों नहीं है, 1,2 जैसा कि मुझे उम्मीद है?

B का निर्माता data=1 पारित किया गया है, और इसकी प्रारंभिक सूची में यह के साथ A पर कॉल करता है। C का कन्स्ट्रक्टर इसी प्रकार data=2 पास हो गया है और इसकी प्रारंभिक सूची में Adata के साथ कॉल किया गया है।

हम B और C subobjects show पर उनके मूल्य पूछते हैं। और हमें 0012 जैसा कि मैं उम्मीद करता हूं।

+1

आप 1,2 बहुत आसानी से समायोजित कर सकते हैं-समायोजन उस विरासत को वर्चुअल नहीं बनाते हैं :) फिर आपके पास ए –

+0

की दो प्रतियां होंगी, वैसे भी 'यह->' अजीब है। आधुनिक सी ++ में आप 'ए (int डेटा = 0) लिख सकते हैं: डेटा (डेटा) {} ',' कन्स्ट्रक्टर प्रारंभिक सूची ', जो क्लीनर है, और आपको निरंतर सदस्यों की अनुमति देता है। –

+1

यह समस्या नहीं है, लेकिन क्या आपको वास्तव में अतिरिक्त सामग्री की आवश्यकता है जो 'std :: endl' करता है? '\ n '' एक रेखा समाप्त होता है। –

उत्तर

22

आप वर्चुअल विरासत के साथ इस योजना उपलब्ध हो तो उसे पदानुक्रम में सबसे व्युत्पन्न वर्ग के लिए ऊपर आम आधार (A) 1,2 के निर्माता कॉल करने के लिए (इस मामले D में) है।

चूंकि A के लिए आपके कन्स्ट्रक्टर के पास डिफ़ॉल्ट पैरामीटर data = 0 है, इसे डिफ़ॉल्ट कन्स्ट्रक्टर के रूप में उपयोग किया जा सकता है। और यह हो रहा है, आम A उप-ऑब्जेक्ट डिफ़ॉल्ट रूप से निर्मित हो जाता है, क्योंकि आपने इसे D के सदस्य प्रारंभिक सूची से छोड़ा है।

आप data के लिए डिफ़ॉल्ट मान निकालते हैं, तो आप जोर देने के लिए एक अच्छा संकलक त्रुटि मिलेगी:

A(int data) 
{ 
    this->data = data; 
} 

On g++ it results with:

main.cpp: In constructor 'D::D(int, int)': 
main.cpp:37:16: error: no matching function for call to 'A::A()' 
     C(dataC) { 

याद रखें कि आभासी साथ विरासत केवल एकप्रकार की उप-वस्तु है। और B और C उप-ऑब्जेक्ट्स दोनों इसका संदर्भ लें। अलग-अलग चीजों को मुद्रित करने के लिए show पर आपकी कॉल के लिए असंभव है, क्योंकि वे उसी डेटा तक पहुंचते हैं। यही कारण है कि यह सबसे व्युत्पन्न वर्ग तक है, इसलिए कोई अस्पष्टता नहीं है।

[class.mi/4]

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

[class.base.init/13.1]

एक गैर सौंपने के निर्माता में, निम्न क्रम में प्रारंभ आय:

  • पहले, और केवल सबसे व्युत्पन्न वर्ग के निर्माता के लिए, आभासी बेस क्लास को प्रारंभिक क्रम में क्रमशः बेस क्लास के निर्देशित विश्वकोश ग्राफ के गहराई से पहले बाएं-से-दाएं ट्रैवर्सल पर दिखाई देने के क्रम में प्रारंभ किया जाता है, जहां बाएँ-से-सही "व्युत्पन्न वर्ग आधार विनिर्देशक-सूची में आधार वर्ग की उपस्थिति का आदेश है।

  • ...

तो आप इस बजाय तरह D::D() परिभाषित हैं तो आप विशिष्ट डेटा के साथ A का निर्माण करना चाहते हैं:

D(int dataA = 0) : 
    A(dataA) { 
} 
7

जब आप virtual है विरासत, virtual बेस क्लास सबसे व्युत्पन्न वर्ग के निर्माता द्वारा प्रारंभ किया गया है।

D(int dataB = 0, int dataC = 0) : 
    B(dataB), 
    C(dataC) {} 

के बराबर है:,

D(int dataB = 0, int dataC = 0) : 
    A(), 
    B(dataB), 
    C(dataC) {} 

जो है, आपके मामले में, एक ही रूप में

D(int dataB = 0, int dataC = 0) : 
    A(0), 
    B(dataB), 
    C(dataC) {} 

जब तक आप B का एक उदाहरण का निर्माण

B(int data = 0) : 
    A(data) { 
} 

वही है रों

B(int data = 0) {} 

कोई कोड के साथ प्रारंभ करने में AA के बाद से पहले से ही D के निर्माता में आरंभ नहीं हो जाता।

वही बात C::C(int data) के कार्यान्वयन पर लागू होती है।

जो आपके द्वारा देखे जा रहे आउटपुट को समझाता है।

0

मुझे लगता है कि आपने virtual विरासत और 'हीरा समस्या' की अवधारणा को गलत समझा। virtual विरासत के साथ, आप हीरा विरासत पैटर्न बनाते हैं, लेकिन आपकी पोस्ट से, ऐसा लगता है कि आप इससे बचना चाहते हैं और इसके बजाय दो बेस A हैं, B और C से दूसरा। इसे प्राप्त करने के लिए, B और C में वर्चुअल विरासत से बचें, जब आपका कोड 1 2 लिख देगा।

संयोग से, अगर केवल BA से A लेकिन C पारंपरिक विरासत से virtual विरासत है, तो D दो ठिकानों A, लेकिन वह B से फिर से initialised डिफ़ॉल्ट है (जैसा कि अन्य उत्तर में बताया गया है) होगा।

संबंधित मुद्दे