एकाधिक वंशानुक्रम के साथ सटीक समस्या क्या है?


121

मैं लोगों को हर समय पूछते हुए देख सकता हूं कि क्या एकाधिक विरासत को C # या जावा के अगले संस्करण में शामिल किया जाना चाहिए। सी ++ लोग, जो इस क्षमता के लिए भाग्यशाली हैं, कहते हैं कि यह किसी को रस्सी देने के लिए है जैसे अंततः खुद को लटका देना।

बहु विरासत में क्या बात है? क्या कोई ठोस नमूने हैं?


54
मैं सिर्फ यह उल्लेख करूंगा कि C ++ आपको खुद को लटकाने के लिए पर्याप्त रस्सी देने के लिए महान है।
क्लोच

1
एक से अधिक वंशानुक्रम के विकल्प के लिए जो पते (और, IMHO) एक ही समस्या के कई, लक्षण ( iam.unibe.ch/~scg/Research/Traits ) देखें
बेवन

52
मुझे लगा कि सी ++ आपको अपने पैर में गोली मारने के लिए पर्याप्त रस्सी देता है।
कीथब

6
यह सवाल मानने के लिए लगता है कि सामान्य रूप से एमआई के साथ एक समस्या है, जबकि मैंने बहुत सी भाषाएं पाई हैं जहां एमआई आकस्मिक उपयोग में है। कुछ भाषाओं के MI को संभालने में निश्चित रूप से समस्याएं हैं, लेकिन मुझे इस बात की जानकारी नहीं है कि सामान्य रूप से MI की महत्वपूर्ण समस्याएं हैं।
डेविड थॉर्नले

जवाबों:


86

सबसे स्पष्ट समस्या फ़ंक्शन ओवरराइडिंग के साथ है।

मान लीजिए कि दो वर्ग हैं Aऔर Bदोनों एक विधि को परिभाषित करते हैं doSomething। अब आप किसी तृतीय वर्ग को परिभाषित Cदोनों से है, जो विरासत में मिली Aऔर B, लेकिन आप पर हावी नहीं है doSomethingविधि।

जब संकलक बीज इस कोड ...

C c = new C();
c.doSomething();

... विधि के किस कार्यान्वयन का उपयोग करना चाहिए? किसी और स्पष्टीकरण के बिना, कंपाइलर के लिए अस्पष्टता को हल करना असंभव है।

ओवरराइडिंग के अलावा, मल्टीपल इनहेरिटेंस के साथ अन्य बड़ी समस्या मेमोरी में भौतिक वस्तुओं का लेआउट है।

C ++ और Java और C # जैसी भाषाएँ प्रत्येक प्रकार की ऑब्जेक्ट के लिए एक निश्चित एड्रेस-बेस्ड लेआउट बनाती हैं। कुछ इस तरह:

class A:
    at offset 0 ... "abc" ... 4 byte int field
    at offset 4 ... "xyz" ... 8 byte double field
    at offset 12 ... "speak" ... 4 byte function pointer

class B:
    at offset 0 ... "foo" ... 2 byte short field
    at offset 2 ... 2 bytes of alignment padding
    at offset 4 ... "bar" ... 4 byte array pointer
    at offset 8 ... "baz" ... 4 byte function pointer

जब कंपाइलर मशीन कोड (या बायटेकोड) उत्पन्न करता है, तो यह प्रत्येक विधि या क्षेत्र तक पहुंचने के लिए उन संख्यात्मक ऑफसेट का उपयोग करता है।

एकाधिक वंशानुक्रम इसे बहुत मुश्किल बना देता है।

वर्ग तो Cदोनों से विरासत में मिली Aऔर B, संकलक में डेटा लेआउट तय करना पड़ता है ABआदेश या में BAआदेश।

लेकिन अब कल्पना कीजिए कि आप किसी Bऑब्जेक्ट पर कॉल कर रहे हैं । क्या यह वास्तव में सिर्फ एक है B? या यह वास्तव में एक Cवस्तु है जिसे इसके Bइंटरफ़ेस के माध्यम से बहुरूपिए कहा जा रहा है? ऑब्जेक्ट की वास्तविक पहचान के आधार पर, भौतिक लेआउट अलग होगा, और कॉल-साइट पर आह्वान करने के लिए फ़ंक्शन की ऑफसेट को जानना असंभव है।

इस तरह की प्रणाली को संभालने का तरीका फिक्स्ड-लेआउट दृष्टिकोण को खोदना है, जिससे प्रत्येक वस्तु को उसके लेआउट के लिए पहले से क्वेरर किया जा सके। फ़ंक्शन को लागू करने या अपने खेतों तक पहुंचने लिए क्वियर किया जा सके।

तो ... लंबी कहानी छोटी ... यह कई लेखकों के लिए गर्दन में दर्द है जो कई विरासत का समर्थन करता है। इसलिए जब कोई गुइडो वैन रोसुम अजगर की तरह डिजाइन करता है, या जब एंडर्स हेजलबर्ग सी # डिजाइन करते हैं, तो वे जानते हैं कि कई विरासत का समर्थन करने से कंपाइलर कार्यान्वयन काफी जटिल हो जाता है, और संभवतः उन्हें नहीं लगता कि लाभ लागत के लायक है।


62
एहम, पायथन MI
Nemanja Trifunovic

26
ये बहुत ठोस तर्क नहीं हैं - अधिकांश भाषाओं में निश्चित लेआउट चीज़ बिल्कुल भी मुश्किल नहीं है; C ++ में यह मुश्किल है क्योंकि मेमोरी अपारदर्शी नहीं है और इस तरह आप सूचक अंकगणितीय मान्यताओं के साथ कुछ कठिनाई में भाग सकते हैं। उन भाषाओं में जहाँ कक्षा की परिभाषाएँ स्थिर हैं (जैसे जावा, C # और C ++ में), एकाधिक वंशानुक्रम नाम क्लैश को संकलन समय के लिए निषिद्ध किया जा सकता है (और C # यह किसी भी तरह इंटरफेस के साथ करता है!)।
ईमोन नेरबोन 18

10
ओपी सिर्फ मुद्दों को समझना चाहते थे, और मैंने उन्हें इस मामले पर व्यक्तिगत रूप से संपादकीय किए बिना समझाया। मैंने अभी कहा कि भाषा डिजाइनर और संकलक कार्यान्वयनकर्ता "संभवतः यह नहीं सोचते कि लाभ लागत के लायक है"।
बेंजिस्मिथ

12
" सबसे स्पष्ट समस्या फ़ंक्शन ओवरराइडिंग के साथ है। " इसका फ़ंक्शन ओवरराइडिंग से कोई लेना-देना नहीं है। यह एक साधारण अस्पष्टता समस्या है।
जिज्ञासु

10
इस जवाब में गुइडो और पायथन के बारे में कुछ गलत जानकारी है, क्योंकि पायथन एमआई का समर्थन करता है। "मैंने तय किया कि जब तक मैं वंशानुक्रम का समर्थन करने जा रहा हूँ, तब तक मैं कई विरासतों के सरल-दिमाग वाले संस्करण का समर्थन कर सकता हूँ।" - Guido van Rossum python-history.blogspot.com/2009/02/… - प्लस, अस्पष्टता संकल्प संकलक में काफी आम है (चर ब्लॉक करने के लिए स्थानीय हो सकते हैं, स्थानीय कार्य करने के लिए स्थानीय हो सकते हैं, फ़ंक्शन, ऑब्जेक्ट सदस्यों, वर्ग के सदस्यों को संलग्न करने के लिए स्थानीय, ग्लोबल्स, आदि), मैं नहीं देखता कि कैसे एक अतिरिक्त गुंजाइश एक फर्क पड़ेगा।
माक्र्स

46

जिन समस्याओं का आप लोग उल्लेख करते हैं, वे वास्तव में हल करने के लिए कठिन नहीं हैं। वास्तव में उदाहरण के लिए एफिल ऐसा ही करता है! (और मनमाने विकल्प या जो कुछ भी शुरू किए बिना)

जैसे यदि आप ए और बी से विरासत में लेते हैं, तो दोनों में विधि फू () है, तो निश्चित रूप से आप अपनी कक्षा सी और ए और बी दोनों से विरासत में एक मनमाना विकल्प नहीं चाहते हैं। अगर c.foo () कहा जाता है या अन्यथा आपको सी में एक विधि का नाम बदलना होगा (यह बार बन सकता है) ()

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


26
मैं सहमत हूँ। एमआई से लोग नफरत करते हैं या स्थिर टाइपिंग के साथ ही इसका मुख्य कारण है: ज्यादातर लोगों ने केवल इसका बहुत बुरा कार्यान्वयन किया है - या इसे बहुत बुरी तरह से इस्तेमाल किया है। MI को C ++ से जज करना OOP को PHP से जज करने या पिंटो द्वारा ऑटोमोबाइल्स को जज करने जैसा है।
जोर्ग डब्ल्यू मित्तग

2
@ Curiousguy: C, C ++ की कई "विशेषताओं" की तरह ही, चिंता का एक और सेट पेश करता है। सिर्फ इसलिए कि यह असंदिग्ध है इसके साथ या डिबग के साथ काम करना आसान नहीं है। इस श्रृंखला को हटाने के बाद से यह विषय से दूर चला गया और आपने इसे वैसे भी उड़ा दिया।
ग्वेंटे

4
@ Guvante किसी भी भाषा में MI के साथ एकमात्र समस्या यह है कि वे एक ट्यूटोरियल पढ़ सकते हैं और अचानक एक भाषा जान सकते हैं।
माइल्स राउत

2
मैं तर्क दूंगा कि भाषा की विशेषताएं कोडिंग समय को कम करने के बारे में नहीं हैं। वे किसी भाषा की अभिव्यंजना को बढ़ाने और प्रदर्शन को बढ़ाने के बारे में भी हैं।
माइल्स रूट

4
इसके अलावा, बग केवल एमआई से होता है जब बेवकूफ इसका गलत तरीके से उपयोग करते हैं।
माइल्स राउत

27

हीरे की समस्या :

एक अस्पष्टता जो तब उत्पन्न होती है जब दो वर्ग बी और सी को ए से विरासत में मिलता है, और वर्ग डी को बी और सी दोनों से विरासत में मिलता है। अगर ए में एक विधि है जो बी और सी से ओवरराइड हो गई है , और डी इसे ओवरराइड नहीं करता है, तो कौन सा संस्करण है विधि D विरासत में मिली: B की, या C की?

... इस स्थिति में वर्ग वंशानुक्रम आरेख के आकार के कारण इसे "हीरा समस्या" कहा जाता है। इस मामले में, क्लास ए शीर्ष पर है, बी और सी दोनों इसके नीचे अलग-अलग हैं, और डी हीरे की आकृति बनाने के लिए दोनों को एक साथ नीचे जोड़ते हैं ...


4
जिसका एक समाधान है जिसे वर्चुअल इनहेरिटेंस के रूप में जाना जाता है। यह केवल एक समस्या है अगर आप इसे गलत करते हैं।
इयान गोल्डबी

1
@IanGoldby: वर्चुअल इनहेरिटेंस समस्या के भाग को हल करने के लिए एक तंत्र है, अगर किसी को उन सभी प्रकारों के बीच पहचान-संरक्षण अपस्टेक और डाउनकास्ट की अनुमति देने की आवश्यकता नहीं है, जहां से एक उदाहरण प्राप्त होता है या जिसके लिए यह प्रतिस्थापित होता है । दिया गया एक्स: बी; Y: बी; और जेड: एक्स, वाई; मान लें कि कुछZ Z का एक उदाहरण है। आभासी विरासत के साथ, (B) someZ और (B) (Y) someZ अलग-अलग वस्तुएं हैं; या तो, एक डाउनकास्ट और अपकास्ट के माध्यम से दूसरे को प्राप्त कर सकता है, लेकिन क्या होगा अगर एक के पास है someZऔर उसे Objectऔर फिर उसे डालना चाहता है B? कौन सा Bमिलेगा?
सुपरकैट

2
@supercat शायद, लेकिन उस तरह की समस्याएं काफी हद तक सैद्धांतिक हैं, और किसी भी मामले में संकलक द्वारा संकेत दिया जा सकता है। महत्वपूर्ण बात यह जानना है कि आप किस समस्या को हल करने की कोशिश कर रहे हैं, और फिर सबसे अच्छा उपकरण का उपयोग करें, उन लोगों से हठधर्मिता को अनदेखा करें, जो 'क्यों?' समझने के साथ खुद को चिंतित नहीं करेंगे।
इयान गोल्डबी

@IanGoldby: इस तरह की समस्याएं केवल संकलक द्वारा संकेतित की जा सकती हैं यदि इसमें सभी वर्गों के लिए एक साथ प्रश्न में प्रवेश हो। कुछ रूपरेखाओं में, एक आधार वर्ग में किसी भी परिवर्तन के लिए हमेशा सभी व्युत्पन्न वर्गों की एक पुनर्संरचना की आवश्यकता होगी, लेकिन व्युत्पन्न वर्गों को दोबारा प्राप्त किए बिना आधार वर्गों के नए संस्करणों का उपयोग करने की क्षमता (जिसके लिए किसी के पास स्रोत कोड नहीं हो सकता है) एक उपयोगी विशेषता है। फ्रेमवर्क के लिए जो इसे प्रदान कर सकते हैं। इसके अलावा, समस्याएं सिर्फ सैद्धांतिक नहीं हैं। .NET में कई वर्ग इस तथ्य पर भरोसा करते हैं कि किसी भी संदर्भ प्रकार से एक कास्ट Objectऔर उस प्रकार के लिए वापस ...
Supercat

3
@IanGoldby: काफी साफ है। मेरा कहना था कि जावा और .NET के कार्यान्वयनकर्ता सामान्यीकृत एमआई का समर्थन नहीं करने का निर्णय लेने में सिर्फ "आलसी" नहीं थे; सामान्यीकृत एमआई का समर्थन करने से उनके ढांचे को विभिन्न स्वयंसिद्धों को बनाए रखने से रोका जा सकेगा जिनकी वैधता एमआई की तुलना में कई उपयोगकर्ताओं के लिए अधिक उपयोगी होगी।
सुपरकैट

21

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

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


क्या आप बता सकते हैं कि उन्होंने आपको पूर्व और बाद की स्थितियों को लागू करने की अनुमति क्यों नहीं दी?
यत्रिल्ल

2
@Yttrill क्योंकि इंटरफ़ेस में विधि कार्यान्वयन नहीं हो सकते हैं। तुम कहाँ रख दिया assert?
उत्सुकता

1
@ कुरसी: आप एक उपयुक्त वाक्यविन्यास के साथ एक भाषा का उपयोग करते हैं जो आपको पूर्व और बाद की स्थितियों को सीधे इंटरफ़ेस में रखने की अनुमति देता है: कोई "मुखर" की आवश्यकता नहीं है। फेलिक्स से उदाहरण: fun div (संख्या: int, den: int जब den; = 0): int अपेक्षित परिणाम == 0 का तात्पर्य num == 0;
यत्रिल्ल

@Yttrill ठीक है, लेकिन कुछ भाषाएँ, जैसे जावा, MI या "पूर्व और बाद की स्थितियों को सीधे इंटरफ़ेस में" का समर्थन नहीं करती हैं।
21

इसका उपयोग अक्सर नहीं किया जाता है क्योंकि यह उपलब्ध नहीं है, और हम यह नहीं जानते कि इसका उपयोग कैसे किया जाए। यदि आप कुछ स्कैला कोड पर एक नज़र डालते हैं, तो आप देखेंगे कि चीजें कैसे सामान्य होने लगती हैं और लक्षण के लिए फिर से शुरू की जा सकती हैं (ठीक है, यह एमआई नहीं है, लेकिन मेरी बात साबित करता है)।
संतियागोबसुल्तो

16

मान लीजिए कि आपके पास ए और बी ऑब्जेक्ट हैं जो सी। ए और बी दोनों द्वारा विरासत में मिले हैं, दोनों फू () और सी लागू नहीं करते हैं। मुझे C.foo () कहते हैं। कौन सा कार्यान्वयन चुना जाता है? अन्य मुद्दे हैं, लेकिन इस प्रकार की बात एक बड़ी है।


1
लेकिन यह वास्तव में एक ठोस उदाहरण नहीं है। यदि A और B दोनों का कोई कार्य है, तो इसकी बहुत संभावना है कि C को इसके कार्यान्वयन की आवश्यकता होगी। अन्यथा यह अभी भी ए :: फू () को अपने फू () फ़ंक्शन में बुला सकता है।
पीटर कुन्ह

@ क्वेंटम: क्या होगा अगर यह हालांकि नहीं है? समस्या को एक स्तर की विरासत के साथ देखना आसान है, लेकिन अगर आपके पास बहुत सारे स्तर हैं और आपके पास कुछ यादृच्छिक कार्य हैं जो कहीं-कहीं दो बार होते हैं तो यह बहुत कठिन समस्या बन जाती है।
क्लोच

इसके अलावा, बिंदु यह नहीं है कि आप जिस विधि को निर्दिष्ट करना चाहते हैं उसे ए या बी नहीं कह सकते हैं, मुद्दा यह है कि यदि आप निर्दिष्ट नहीं करते हैं तो किसी को चुनने का कोई अच्छा तरीका नहीं है। मुझे यकीन नहीं है कि C ++ इसे कैसे संभालती है, लेकिन अगर कोई जानता है कि यह उल्लेख कर सकता है?
टोलच ऑक्ट

2
@tloach - यदि C अस्पष्टता का समाधान नहीं करता है, तो संकलक इस त्रुटि का पता लगा सकता है और संकलन-समय की त्रुटि वापस कर सकता है।
Eamon Nerbonne

@ एर्मोन - बहुरूपता के कारण, अगर फू () आभासी है तो संकलक को भी संकलन-समय पर पता नहीं चल सकता है कि यह एक मुद्दा बनने जा रहा है।
tloach

5

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

यह तब और खराब हो जाता है जब आप कई वर्गों से विरासत में मिलते हैं जो एक ही आधार वर्ग से प्राप्त होते हैं। (हीरे की विरासत, यदि आप विरासत का पेड़ खींचते हैं तो आपको हीरे का आकार मिलता है)

इन समस्याओं को दूर करने के लिए एक संकलक के लिए वास्तव में समस्याग्रस्त नहीं हैं। लेकिन कंपाइलर को यहां जो पसंद करना है वह पसंद नहीं बल्कि मनमाना है, यह कोड को बहुत कम सहज बनाता है।

मुझे लगता है कि अच्छा OO डिजाइन करते समय मुझे कई विरासत की आवश्यकता नहीं है। जिन मामलों में मुझे इसकी आवश्यकता है, मुझे आमतौर पर लगता है कि मैं कार्यक्षमता का पुन: उपयोग करने के लिए विरासत का उपयोग कर रहा हूं, जबकि विरासत केवल "एक-एक" संबंधों के लिए उपयुक्त है।

मिक्सिंस जैसी अन्य तकनीकें हैं जो समान समस्याओं को हल करती हैं और उनके पास कई विरासत वाले मुद्दे नहीं होते हैं।


4
संकलित को एक मनमाना विकल्प बनाने की आवश्यकता नहीं है - यह बस त्रुटि कर सकता है। C # में, किस प्रकार का है ([..bool..]? "test": 1)?
ईमोन नेरबोन 18

4
सी ++ में, कंपाइलर कभी भी इस तरह की मनमानी पसंद नहीं करता है: यह एक वर्ग को परिभाषित करने के लिए एक त्रुटि है जहां कंपाइलर को एक मनमाना विकल्प बनाने की आवश्यकता होगी।
जिज्ञासु

5

मुझे नहीं लगता कि हीरे की समस्या एक समस्या है, मैं उस परिष्कार पर विचार करूंगा, और कुछ नहीं।

सबसे बड़ी समस्या, मेरे दृष्टिकोण से, कई उत्तराधिकारियों के साथ आरएडी है - पीड़ित और ऐसे लोग जो डेवलपर्स होने का दावा करते हैं, लेकिन वास्तव में आधे ज्ञान (सर्वोत्तम रूप से) के साथ फंस जाते हैं।

व्यक्तिगत रूप से, मुझे बहुत खुशी होगी अगर मैं अंत में विंडोज फॉर्म में कुछ ऐसा कर सकता हूं (यह सही कोड नहीं है, लेकिन यह आपको विचार देना चाहिए):

public sealed class CustomerEditView : Form, MVCView<Customer>

यह मुख्य मुद्दा है जिसमें मेरे पास कोई बहु विरासत नहीं है। आप इंटरफेस के साथ भी कुछ ऐसा ही कर सकते हैं, लेकिन वहाँ है जिसे मैं "s *** कोड" कहता हूं, यह इस दर्दनाक पुनरावृत्ति सी है *** आपको उदाहरण के लिए, डेटा संदर्भ प्राप्त करने के लिए अपनी प्रत्येक कक्षा में लिखना होगा।

मेरी राय में, आधुनिक भाषा में कोड के किसी भी पुनरावृत्ति के लिए कोई आवश्यकता नहीं, थोड़ी सी भी आवश्यकता नहीं होनी चाहिए।


मैं सहमत हूं, लेकिन मैं केवल यह कहता हूं: गलतियों का पता लगाने के लिए किसी भी भाषा में कुछ अतिरेक की आवश्यकता है। किसी भी तरह आपको फेलिक्स डेवलपर टीम में शामिल होना चाहिए क्योंकि यह एक मुख्य लक्ष्य है। उदाहरण के लिए, सभी घोषणाएं पारस्परिक रूप से पुनरावर्ती हैं, और आप आगे और साथ ही पीछे की ओर देख सकते हैं, इसलिए आपको आगे की घोषणाओं की आवश्यकता नहीं है (गुंजाइश सेट-वार है, जैसे सी गोटो लेबल)।
१६:५० को यत्रिल्ल

मैं इस बात से पूरी तरह सहमत हूँ - मैं यहाँ एक ऐसी ही समस्या में भाग गया । लोग हीरे की समस्या के बारे में बात करते हैं, वे इसे धार्मिक रूप से उद्धृत करते हैं, लेकिन मेरी राय में यह इतनी आसानी से बचा जाता है। (हम सभी को हमारे कार्यक्रमों को लिखने की आवश्यकता नहीं है जैसे कि उन्होंने iostream लाइब्रेरी लिखी है।) एकाधिक उत्तराधिकार का उपयोग तार्किक रूप से किया जाना चाहिए जब आपके पास एक ऐसी वस्तु हो जिसमें दो अलग-अलग आधार वर्गों की कार्यक्षमता की आवश्यकता हो, जिसमें कोई अतिव्यापी कार्य या फ़ंक्शन नाम न हों। दाहिने हाथों में, यह एक उपकरण है।
jedd.ahyoung

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

@ Lunmeat317: यह तथ्य कि कोड को आमतौर पर इस तरह से नहीं लिखा जाना चाहिए कि 'हीरा' किसी समस्या का समाधान करेगा, इसका मतलब यह नहीं है कि एक भाषा / रूपरेखा डिजाइनर सिर्फ इस मुद्दे की अनदेखी कर सकता है। यदि कोई ढांचा यह बताता है कि वस्तु की पहचान को बनाए रखने और उसे कम करने के लिए, किसी वर्ग के बाद के संस्करणों के लिए अनुमति देना चाहता है तो इसके प्रकारों की संख्या में वृद्धि की जाए जिसके लिए इसे बिना किसी परिवर्तन के बदला जा सकता है, और रन-टाइम प्रकार के निर्माण की अनुमति देना चाहता है, मुझे नहीं लगता कि यह उपरोक्त लक्ष्यों को पूरा करते हुए कई वर्ग वंशानुक्रम (इंटरफ़ेस विरासत के विपरीत) की अनुमति दे सकता है।
सुपरकैट

3

कॉमन लिस्प ऑब्जेक्ट सिस्टम (CLOS) C ++ शैली की समस्याओं से बचने के दौरान एमआई का समर्थन करने वाली चीज़ का एक और उदाहरण है: विरासत को एक समझदार डिफ़ॉल्ट दिया गया है , जबकि अभी भी आपको स्पष्ट रूप से यह तय करने की स्वतंत्रता की अनुमति देता है कि कैसे, कहो, एक सुपर के व्यवहार को कॉल करें ।


हां, CLOS आधुनिक काल की गणना के बाद से अब तक के सबसे लंबे समय के बाद से सबसे बेहतर वस्तु प्रणालियों में से एक है :)
rostamn739

2

मल्टीपल इनहेरिटेंस में कुछ भी गलत नहीं है। समस्या एक भाषा में कई विरासत को जोड़ने की है जो शुरू से ही कई विरासत के साथ डिजाइन नहीं की गई थी।

एफिल भाषा बहुत ही कुशल और उत्पादक तरीके से प्रतिबंधों के बिना कई विरासत का समर्थन कर रही है लेकिन भाषा को इस बात के लिए डिज़ाइन किया गया था कि वह इसका समर्थन करें।

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

मुझे लगता है कि एकाधिक विरासत का समर्थन करना या न करना अधिक पसंद का मामला है, प्राथमिकताओं का मामला है। एक अधिक जटिल सुविधा को सही ढंग से कार्यान्वित और संचालन में अधिक समय लगता है और अधिक विवादास्पद हो सकता है। C ++ कार्यान्वयन का कारण हो सकता है कि C # और Java में कई उत्तराधिकार लागू नहीं किए गए ...


1
एमआई के लिए सी ++ समर्थन " बहुत कुशल और उत्पादक " नहीं है?
जिज्ञासु

1
वास्तव में यह कुछ हद तक इस अर्थ में टूट गया है कि यह सी ++ की अन्य विशेषताओं के साथ फिट नहीं है। असाइनमेंट इनहेरिटेंस के साथ ठीक से काम नहीं करता है, अकेले मल्टीपल इनहेरिटेंस (वास्तव में खराब नियमों को देखें)। हीरे को सही ढंग से बनाना इतना कठिन है, मानक समिति ने इसे सही तरीके से करने के बजाय इसे सरल और कुशल बनाए रखने के लिए अपवाद पदानुक्रम को खराब कर दिया। एक पुराने कंपाइलर पर मैं उस समय उपयोग कर रहा था जब मैंने इसे और कुछ एमआई मिक्सिन और बुनियादी अपवादों के कार्यान्वयन ने कोड के एक मेगाबाइट पर खर्च किया और संकलन के लिए 10 मिनट का समय लिया .. बस परिभाषाएं।
यत्रिल्ल

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

1
एमआई के इस मॉडल को कुशलता से लागू करने के लिए एफिल कंपाइलरों को एक वैश्विक कार्यक्रम विश्लेषण करना होगा। बहुरूपिए विधि के लिए कॉल वे या तो डिस्पैचर थ्रक्स या विरल मैट्रिस का उपयोग करते हैं जैसा कि यहां बताया गया है । यह C ++ के अलग संकलन और C # के जावा और क्लास की लोडिंग सुविधा के साथ अच्छी तरह से मिश्रण नहीं करता है।
cyco130

2

जावा और .NET जैसे चौखटे के डिजाइन लक्ष्यों में से एक यह कोड के लिए संभव बनाना है जो कि पूर्व-संकलित पुस्तकालय के एक संस्करण के साथ काम करने के लिए संकलित है, उस पुस्तकालय के बाद के संस्करणों के साथ समान रूप से अच्छी तरह से काम करने के लिए नई सुविधाएँ जोड़ें। जबकि C या C ++ जैसी भाषाओं में सामान्य प्रतिमान सांख्यिकीय रूप से जुड़े निष्पादकों को वितरित करने के लिए होता है जिसमें वे सभी पुस्तकालय होते हैं जिनकी आवश्यकता होती है। .NET और Java में प्रतिमान उन घटकों के संग्रह के रूप में अनुप्रयोग वितरित करने के लिए होते हैं जो रन-टाइम के दौरान "लिंक" होते हैं। ।

COM मॉडल जो पूर्व में था। .NET ने इस सामान्य दृष्टिकोण का उपयोग करने का प्रयास किया, लेकिन इसमें वास्तव में विरासत नहीं थी - इसके बजाय, प्रत्येक वर्ग परिभाषा ने प्रभावी रूप से एक वर्ग और एक ही नाम दोनों के इंटरफ़ेस को परिभाषित किया, जिसमें उसके सभी सार्वजनिक सदस्य शामिल थे। उदाहरण वर्ग प्रकार के थे, जबकि संदर्भ इंटरफ़ेस प्रकार के थे। एक वर्ग को दूसरे से व्युत्पन्न घोषित करना एक वर्ग को दूसरे के इंटरफ़ेस को लागू करने के रूप में घोषित करने के बराबर था, और नए वर्ग को उन सभी सार्वजनिक सदस्यों को फिर से लागू करने की आवश्यकता थी जिनसे एक व्युत्पन्न हुआ था। यदि Y और Z एक्स से निकलते हैं, और फिर W, Y और Z से निकलता है, तो यह मायने नहीं रखेगा कि Y और Z, X के सदस्यों को अलग तरह से लागू करते हैं, क्योंकि Z उनके कार्यान्वयन का उपयोग करने में सक्षम नहीं होगा - इसे इसे परिभाषित करना होगा खुद। W, Y और / या Z के उदाहरणों को इनकैप्सुलेट कर सकता है।

जावा और .NET में कठिनाई यह है कि कोड इनहेरिट सदस्यों की अनुमति दी और करने के लिए उन्हें पहुंच है है परोक्ष माता पिता के सदस्यों को देखें। मान लीजिए कि ऊपर के रूप में WZ से संबंधित एक वर्ग था:

class X { public virtual void Foo() { Console.WriteLine("XFoo"); }
class Y : X {};
class Z : X {};
class W : Y, Z  // Not actually permitted in C#
{
  public static void Test()
  {
    var it = new W();
    it.Foo();
  }
}

ऐसा लगता है कि प्रतीत होता है W.Test()डब्ल्यू कॉल आभासी विधि के क्रियान्वयन का उदाहरण बनाकर चाहिए Fooमें परिभाषित किया X। मान लीजिए, हालांकि, कि Y और Z वास्तव में एक अलग-अलग संकलित मॉड्यूल में थे, और यद्यपि उन्हें ऊपर परिभाषित किया गया था जब X और W संकलित किए गए थे, उन्हें बाद में बदल दिया गया और पुन: संयोजित किया गया:

class Y : X { public override void Foo() { Console.WriteLine("YFoo"); }
class Z : X { public override void Foo() { Console.WriteLine("ZFoo"); }

अब कॉलिंग का W.Test()क्या असर होना चाहिए ? यदि वितरण से पहले कार्यक्रम को सांख्यिकीय रूप से जोड़ा जाना था, तो स्थिर लिंक चरण यह पता लगाने में सक्षम हो सकता है कि जबकि कार्यक्रम में Y और Z से पहले कोई अस्पष्टता नहीं थी, Y और Z के परिवर्तन ने चीजों को अस्पष्ट बना दिया है और लिंक करने वाला मना कर सकता है जब तक या इस तरह की अस्पष्टता का समाधान नहीं हो जाता, तब तक कार्यक्रम का निर्माण करें। दूसरी ओर, यह संभव है कि वह व्यक्ति जिसके पास डब्ल्यू और वाई और जेड दोनों के नए संस्करण हैं, वह कोई है जो केवल कार्यक्रम चलाना चाहता है और इसका कोई स्रोत कोड नहीं है। कब W.Test()चलता है, यह अब स्पष्ट नहीं होगा कि क्याW.Test() चाहिए, लेकिन जब तक उपयोगकर्ता वाई और जेड के नए संस्करण के साथ डब्ल्यू को चलाने की कोशिश करता है, तब तक कोई रास्ता नहीं होगा जिससे सिस्टम का कोई भी हिस्सा पहचान सके कि कोई समस्या थी (जब तक कि डब्ल्यू को नाजायज नहीं माना जाता था Y और Z के परिवर्तन से पहले)।


2

हीरा कोई समस्या नहीं है, जब तक आप C ++ वर्चुअल इनहेरिटेंस जैसी किसी चीज का उपयोग नहीं करते हैं: सामान्य विरासत में प्रत्येक बेस क्लास एक सदस्य क्षेत्र जैसा दिखता है (वास्तव में वे इस तरह से रैम में रखे जाते हैं), आपको कुछ सिंथैटिक शुगर और ए देते हैं अधिक आभासी तरीकों को ओवरराइड करने की अतिरिक्त क्षमता। यह संकलन समय पर कुछ अस्पष्टता थोप सकता है लेकिन आमतौर पर इसे हल करना आसान है।

दूसरी ओर, आभासी विरासत के साथ यह बहुत आसानी से नियंत्रण से बाहर हो जाता है (और फिर गड़बड़ हो जाता है)। एक उदाहरण के रूप में विचार करें "दिल" आरेख:

  A       A
 / \     / \
B   C   D   E
 \ /     \ /
  F       G
    \   /
      H

सी ++ में यह पूरी तरह से असंभव है: जितनी जल्दी हो Fऔर Gएक ही वर्ग में विलय कर दिया जाता है, उनके Aएस भी विलय हो जाते हैं, अवधि। इसका मतलब है कि आप कभी भी C ++ में बेस क्लास अपारदर्शी पर विचार नहीं कर सकते हैं (इस उदाहरण में आपको इसका निर्माण करना Aहै Hताकि आपको पता चले कि यह पदानुक्रम में कहीं मौजूद है)। हालाँकि, अन्य भाषाओं में यह काम कर सकता है; उदाहरण के लिए, Fऔर Gस्पष्ट रूप से ए को "आंतरिक" घोषित कर सकता है, इस प्रकार परिणामी विलय और प्रभावी रूप से खुद को ठोस बनाने के लिए मना करता है।

एक और दिलचस्प उदाहरण ( सी ++ नहीं - विशिष्ट):

  A
 / \
B   B
|   |
C   D
 \ /
  E

यहां, केवल Bवर्चुअल इनहेरिटेंस का उपयोग करता है। तो Eदो Bएस शामिल हैं जो समान हैं A। इस तरह, आप एक प्राप्त कर सकते हैं A*सूचक है कि अंक के लिए Eहै, लेकिन आप नहीं यह एक पर कास्ट कर सकते B*हैं, हालांकि वस्तु सूचक है वास्तव में B के रूप में इस तरह के कलाकारों के अस्पष्ट है, और इस अस्पष्टता (संकलन समय पर पता नहीं लगाया जा सकता है जब तक संकलक देखता है पूरा कार्यक्रम)। यहाँ परीक्षण कोड है:

struct A { virtual ~A() {} /* so that the class is polymorphic */ };
struct B: virtual A {};
struct C: B {};
struct D: B {};
struct E: C, D {};

int main() {
        E data;
        E *e = &data;
        A *a = dynamic_cast<A *>(e); // works, A is unambiguous
//      B *b = dynamic_cast<B *>(e); // doesn't compile
        B *b = dynamic_cast<B *>(a); // NULL: B is ambiguous
        std::cout << "E: " << e << std::endl;
        std::cout << "A: " << a << std::endl;
        std::cout << "B: " << b << std::endl;
// the next casts work
        std::cout << "A::C::B: " << dynamic_cast<B *>(dynamic_cast<C *>(e)) << std::endl;
        std::cout << "A::D::B: " << dynamic_cast<B *>(dynamic_cast<D *>(e)) << std::endl;
        std::cout << "A=>C=>B: " << dynamic_cast<B *>(dynamic_cast<C *>(a)) << std::endl;
        std::cout << "A=>D=>B: " << dynamic_cast<B *>(dynamic_cast<D *>(a)) << std::endl;
        return 0;
}

इसके अलावा, कार्यान्वयन बहुत जटिल हो सकता है (भाषा पर निर्भर करता है; देखें बेन्ज़स्मिथ का उत्तर)।


एमआई के साथ यही असली समस्या है। प्रोग्रामर को एक वर्ग के भीतर विभिन्न प्रस्तावों की आवश्यकता हो सकती है। एक भाषा-व्यापी समाधान जो संभव है उसे सीमित करेगा और प्रोग्रामर को प्रोग्राम को सही ढंग से काम करने के लिए क्लू बनाने के लिए बाध्य करेगा।
शॉनहॉर्की
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.