15

मैं इस तरह एक हीरे की एकाधिक वंशानुक्रम परिदृश्य है:एकाधिक वंशानुक्रम + आभासी समारोह गड़बड़

A 
/ \ 
B  C 
    \ /
    D 

आम माता-पिता, ए, एक आभासी समारोह fn को परिभाषित करता है()।
क्या बी और सी दोनों के लिए fn() परिभाषित करना संभव है?
यदि यह है, तो अगला प्रश्न यह है कि - डी बी और सी दोनों एफएन() को बिना किसी विघटन के एक्सेस कर सकता है? मुझे लगता है कि इसके लिए कुछ वाक्यविन्यास है ..
और क्या डी को ऐसा करना संभव है कि विशेष रूप से बी और सी कौन हैं? बी और सी को कुछ अन्य वर्गों द्वारा प्रतिस्थापित किया जा सकता है और मैं चाहता हूं कि डी में कोड जेनेरिक हो।

मैं जो करने की कोशिश कर रहा हूं वह है कि डी को किसी भी तरह से अपने पूर्वजों में एफएन() के सभी उदाहरणों का आकलन करना है। क्या यह कुछ अन्य तरीकों से संभव है कि आभासी कार्य?

उत्तर

1

इस से निपटने वाले पहले से ही कई प्रश्न हैं। ऐसा लगता है कि हम पूछने के लिए प्रश्नों से बाहर हैं। हो सकता है कि खोज बॉक्स प्रश्न पूछें बटन से बड़ा होना चाहिए।

+0

मुझे कोई प्रश्न नहीं मिला जो इस विशिष्ट मुद्दे का उत्तर दे। क्या आप? – shoosh

+0

ऐसा इसलिए नहीं है क्योंकि यह एकाधिक विरासत के बारे में है जिसे आप अनुमान लगा सकते हैं कि इसे पहले से ही अन्य पदों में संबोधित किया गया था। उनसे पूछा गया है कि 'मैं जो करने की कोशिश कर रहा हूं वह है कि डी को किसी भी तरह से अपने पूर्वजों में एफएन() के सभी उदाहरणों का आकलन करें। क्या यह किसी अन्य माध्यम से संभव है कि आभासी कार्य? '।हालांकि मुझे लगता है कि यह कुछ हद तक बेवकूफ सवाल था, लेकिन यहां से जुड़े प्रश्नों में से कोई भी ऐसी चीज के बारे में बात नहीं करता है। मुझे लगता है कि वह अपने पूछताछ में बहुत विशिष्ट और अद्वितीय था। -1। –

4

पहला सवाल, हाँ, बी और सी fn() एक आभासी समारोह के रूप में परिभाषित कर सकते हैं। दूसरा, पाठ्यक्रम पहुँच B::fn() और C::fn() का विकास कर सकते हैं गुंजाइश ऑपरेटर तीसरा प्रश्न का उपयोग कर :: द्वारा: डी कम से कम बी और सी को पता होना चाहिए, जब से तुम विरासत सूची में उन्हें परिभाषित करने के लिए किया है। स्वचालित रूप से सभी वर्गों है कि डी से विरासत के सभी fn() फ़ंक्शन की गणना करने में

class A 
{ 
public: 
    virtual ~A() {} 
    virtual void fn() = 0; 
}; 

class B: public A 
{ 
public: 
    virtual ~B() {} 
    virtual void fn(){ std::cout << "B::fn()" << std::endl; } 
}; 

class C: public A 
{ 
public: 
    virtual ~C() {} 
    virtual void fn(){ std::cout << "C::fn()" << std::endl; } 
}; 

template <typename TypeB, typename TypeC> 
class D: public TypeB, public TypeC 
{ 
public: 
    void Do() 
    { 
     static_cast<TypeB*>(this)->fn(); 
     static_cast<TypeC*>(this)->fn(); 
    } 
}; 

typedef D<B, C> DInst; 

DInst d; 
d.Do(); 

इच्छा के बारे में:: आप बी और सी खुला के प्रकार बताने के लिए टेम्पलेट का उपयोग कर सकते हैं मैं अगर है कि बिना संभव नहीं है यकीन नहीं है एमपीएल का सहारा लेना कम से कम आप अपने उदाहरण को उन संस्करणों के साथ बढ़ा सकते हैं जो 3 और अधिक टेम्पलेट पैरामीटर से निपटते हैं, लेकिन मुझे लगता है कि क्लास टेम्पलेट पैरामीटर की संख्या की ऊपरी (आंतरिक कंपाइलर-) सीमा है।

+0

मैं इसे पोस्ट करने जा रहा था। थोड़ी सी गलती है क्योंकि डी को बी और सी दोनों से प्राप्त होना चाहिए और यह उपरोक्त कोड में नहीं है। एक और वाक्यविन्यास (कास्ट से सरल) होगा: TypeB :: fn() और TypeA :: fn() जो अधिक प्राकृतिक लगते हैं। –

+0

मुझे यकीन नहीं है कि कोई TypeB :: fn() कॉल वर्चुअल फ़ंक्शन कॉल के संबंध में सही चीज़ करता है। स्थैतिक कलाकारों के साथ आप सुनिश्चित हैं कि आपके पास बी प्रकार का ऑब्जेक्ट है। मुझे लगता है कि मुझे इसे आजमाने की ज़रूरत है। सुधार के बारे में नोट के लिए धन्यवाद! – vividos

+1

यह संभवतः मुझे जो चाहिए वह सबसे नज़दीकी समाधान है। दुर्भाग्यवश मेरे मामले में विरासत में कक्षाओं की संख्या (बी, सी) भी परिवर्तनीय है। अनुमान लगाएं कि इसे C++ 0x के चरणीय टेम्पलेट तर्कों की प्रतीक्षा करनी होगी। – shoosh

1

आप पूर्वजों में एफएन() की परिभाषाओं की गणना नहीं कर सकते हैं। सी ++ प्रतिबिंब की कमी है। एकमात्र तरीका मैं कल्पना कर सकता हूं कि एक विशाल लूप सभी संभव पूर्वजों के टाइपिड का परीक्षण कर रहा है। और यह कल्पना करने के लिए दर्द होता है।

0

विविडोज ने पहले से ही पोस्ट के मुख्य भाग का उत्तर दिया है। भले ही मैं अधिक बोझिल static_cast <> + dereference ऑपरेटर के बजाय स्कोप ऑपरेटर का उपयोग करूंगा।

काम पर निर्भर करता है, हो सकता है आप विरासत (ए से अधिक संभवतः विरासत) एक कम युग्मन रचना के लिए बी और सी के लिए डी से रिश्ते बदल सकते हैं। यह माना जा रहा है कि आपको डी या सी के रूप में polimorphically इस्तेमाल करने की आवश्यकता नहीं है, और आपको वास्तव में बी और सी को समान आधार उदाहरण साझा करने की आवश्यकता नहीं है।

यदि आप संरचना के लिए चुनते हैं, तो आप बी और सी को अपने कन्स्ट्रक्टर के तर्क के रूप में टाइप ए के संदर्भ/पॉइंटर्स के रूप में प्राप्त कर सकते हैं, जिससे डी पूरी तरह से बी और सी के बारे में अनजान हो जाता है। उस बिंदु पर, आप एक कंटेनर का उपयोग कर सकते हैं कई व्युत्पन्न वस्तुओं को पकड़ने के लिए। एफएन() (यदि आप ऐसा निर्णय लेते हैं) या किसी अन्य विधि का अपना कार्यान्वयन।

18

जब तक आप D में फिर से fn के ऊपर लिख, नहीं ऐसा नहीं संभव है। क्योंकि डी ऑब्जेक्ट में कोई अंतिम ओवरराइडर नहीं है: C और BA::fn ओवरराइड करें। आपके पास कई विकल्प हैं:

  • या तो C::fn या B::fn ड्रॉप करें। फिर, जो अभी भी A::fn ओवरराइड करता है, वह अंतिम ओवरराइडर है।
  • डी में अंतिम ओवरराइडर रखें। फिर, वह A::fnfnC और B में ओवरराइड करता है।

उदाहरण के लिए एक संकलन समय त्रुटि में निम्न परिणाम:

#include <iostream> 

class A { 
public: 
    virtual void fn() { } 
}; 

class B : public virtual A { 
public: 
    virtual void fn() { } 
}; 

class C : public virtual A { 
public: 
    virtual void fn() { } 
}; 

// does not override fn!! 
class D : public B, public C { 
public: 
    virtual void doit() { 
     B::fn(); 
     C::fn(); 
    } 
}; 

int main(int argc, char **argv) { 
    D d; 
    d.doit(); 
    return 0; 
} 

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

एक ऐसा मामला बहुत ही समान है जब आप किसी विशिष्ट फ़ंक्शन को ओवरराइड करना चाहते हैं। कल्पना करें कि आपके पास बी और सी में एक ही नाम के साथ वर्चुअल फ़ंक्शन है (अब एक सामान्य आधार ए के बिना)। और डी में आप प्रत्येक फंक्शन को ओवरराइड करना चाहते हैं लेकिन प्रत्येक को अलग व्यवहार दें। इस पर निर्भर करता है कि क्या आप बी पॉइंटर या सी पॉइंटर के साथ फ़ंक्शन को कॉल करते हैं, आपके पास अलग-अलग व्यवहार हैं। हर्ब सटर द्वारा Multiple Inheritance Part III ऐसा करने का एक अच्छा तरीका बताता है। यह आपको अपने डिजाइन पर निर्णय लेने में मदद कर सकता है।

+0

मुझे लगता है कि शूश क्या करना चाहता है। उन्होंने कहा, "मैं जो करने की कोशिश कर रहा हूं वह है कि डी को किसी भी तरह से अपने पूर्वजों में एफएन() के सभी उदाहरणों का आकलन करें।" वह कक्षा डी – vividos

+3

में एफएन() को ओवरराइड नहीं करना चाहता है, यही कारण है कि मैंने कहा कि यह संभव नहीं है। यदि सी और बी दोनों ए :: एफएन ओवरराइड करते हैं, तो वह डी –

+0

में एफएन ओवरराइड किए बिना डी को परिभाषित नहीं कर सकता है "ड्रॉप" से आपका क्या मतलब था? – yanpas

1

यदि आप वास्तव में वंश को ट्रैक करने और प्रकारों के माध्यम से गणना करने में सक्षम होना चाहते हैं तो आप Loki टाइपलिस्ट देख सकते हैं। मुझे यकीन नहीं है कि आप जो मांग रहे हैं वह वास्तव में काम के गुच्छा के बिना संभव है। सुनिश्चित करें कि आप यहां अति-इंजीनियरिंग नहीं हैं।

एक अलग टिप्पणी पर, आप इस तरीके से एमआई उपयोग करने के लिए जा रहे हैं (जैसे कि, खतरनाक हीरा), तो आप बहुत स्पष्ट जो आभासी सदस्य के बारे में आप चाहते हैं होना चाहिए। मैं D लिखते समय स्पष्ट रूप से निर्णय लेने के बिना C::fn() से B::fn() पर सेमेन्टिक्स चुनना चाहता हूं, जहां आप एक अच्छे मामले के बारे में सोच नहीं सकते। अलग-अलग विधि के आधार पर आप शायद दूसरे (या यहां तक ​​कि दोनों) पर एक चुन लेंगे। एक बार निर्णय लेने के बाद, आवश्यकता यह है कि विरासत में परिवर्तन उम्मीदों या अर्थपूर्ण इंटरफ़ेस को नहीं बदलते हैं।

आप एक नया वर्ग में अदला-बदली के बारे में वास्तव में चिंतित हैं, तो B कहना की जगह है जहाँ EB से उतरना नहीं है, लेकिन एक ही इंटरफ़ेस प्रदान करता है में कहते हैं कि E, तो आप वास्तव में टेम्पलेट दृष्टिकोण हालांकि मैं यकीन नहीं है का उपयोग करना चाहिए यही कारण है कि में एक static_cast<> है ...

struct A { 
    virtual ~A() {} 
    virtual void f() = 0; 
}; 
struct B: A { 
    virtual void f() { std::cout << "B::f()" << std::endl; } 
}; 
struct C: A { 
    virtual void f() { std::cout << "C::f()" << std::endl; } 
}; 

template <typename Base1, typename Base2> 
struct D: Base1, Base2 { 
    void g() { Base1::f(); Base2::f(); } 
}; 

int main() { 
    D<B,C> d1; 
    D<C,B> d2; 
    d1.g(); 
    d2.g(); 
    return 0; 
} 

// Outputs: 
// B::f() 
// C::f() 
// C::f() 
// B::f() 

ठीक काम करता है और एक छोटे से को देखने के लिए आसान लगता है।

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