C # और Generations में C + और Templates के बीच C ++ में क्या अंतर हैं? [बन्द है]


203

मैं ज्यादातर जावा का उपयोग करता हूं और जेनेरिक अपेक्षाकृत नए हैं। मैं पढ़ता रहता हूं कि जावा ने गलत निर्णय लिया है या कि .NET में बेहतर कार्यान्वयन आदि हैं।

इसलिए, जेनिक्स में सी ++, सी #, जावा के बीच मुख्य अंतर क्या हैं? प्रत्येक के पेशेवरों / विपक्ष?

जवाबों:


364

मैं अपनी आवाज़ को शोर से जोड़ूंगा और चीजों को स्पष्ट करने पर एक क़दम उठाऊँगा:

सी # जेनरिक आपको कुछ इस तरह की घोषणा करने की अनुमति देता है।

List<Person> foo = new List<Person>();

और फिर संकलक आपको उन चीजों को डालने से रोक देगा Personजो सूची में नहीं हैं ।
पर्दे के पीछे C # संकलक सिर्फ List<Person>.NET dll फ़ाइल में डाल रहा है, लेकिन रनटाइम पर JIT संकलक जाता है और कोड का एक नया सेट बनाता है, जैसे कि आपने केवल लोगों को शामिल करने के लिए एक विशेष सूची वर्ग लिखा था - कुछ ऐसा ListOfPerson

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

इसका नकारात्मक पक्ष यह है कि पुराने C # 1.0 और 1.1 कोड (इससे पहले कि वे जेनरिक जोड़ते हैं) इन नए को नहीं समझते हैं List<something>, इसलिए आपको मैन्युअल रूप से चीजों Listको उनके साथ इंटरोपर्ट करने के लिए सादे पुराने में बदलना होगा। यह एक समस्या का इतना बड़ा नहीं है, क्योंकि C # 2.0 बाइनरी कोड पीछे की ओर संगत नहीं है। ऐसा केवल तभी होगा जब आप कुछ पुराने C # 1.0 / 1.1 कोड को C # 2.0 में अपग्रेड कर रहे हों

जावा जेनरिक आपको कुछ इस तरह की घोषणा करने की अनुमति देता है।

ArrayList<Person> foo = new ArrayList<Person>();

सतह पर यह समान दिखता है, और यह सॉर्ट-ऑफ है। कंपाइलर आपको उन चीजों को डालने से भी रोकेगा Personजो सूची में नहीं हैं ।

फर्क यह है कि पर्दे के पीछे क्या होता है। सी # के विपरीत, जावा नहीं जाता है और एक विशेष निर्माण नहीं करता है ListOfPerson- यह सिर्फ सादे पुराने का उपयोग करता है ArrayListजो हमेशा जावा में रहा है। जब आप चीजों को सरणी से बाहर निकालते हैं, तो सामान्य Person p = (Person)foo.get(1);कास्टिंग-नृत्य अभी भी करना पड़ता है। कंपाइलर आपको की-प्रेस सेव कर रहा है, लेकिन स्पीड हिट / कास्टिंग अभी भी वैसे ही लगी हुई है जैसे कि हमेशा से थी।
जब लोग "टाइप एरासुर" का उल्लेख करते हैं, तो यह वही है जो वे बात कर रहे हैं। संकलक आपके लिए कास्ट्स सम्मिलित करता है, और फिर इस तथ्य को 'मिटा देता है' कि इसका मतलब Personसिर्फ सूची नहीं हैObject

इस दृष्टिकोण का लाभ यह है कि पुराने कोड जो जेनेरिक को नहीं समझते हैं उन्हें देखभाल करने की आवश्यकता नहीं है। यह अभी भी उसी पुराने के साथ काम कर रहा है ArrayListजैसा कि हमेशा होता है। जावा की दुनिया में यह अधिक महत्वपूर्ण है क्योंकि वे जावा 5 का उपयोग करके संकलन कोड का समर्थन करना चाहते थे, और इसे पुराने 1.4 या पिछले जेवीएम पर चलाया गया था, जिसे Microsoft ने जानबूझकर परेशान नहीं करने का फैसला किया था।

नकारात्मक पक्ष वह गति है जिसका मैंने पहले उल्लेख किया था, और इसलिए भी कि कोई ListOfPersonछद्म वर्ग या ऐसा कुछ भी नहीं है जो .class फ़ाइलों में जा रहा हो, कोड जो बाद में इस पर दिखता है (प्रतिबिंब के साथ, या यदि आप इसे किसी अन्य संग्रह से बाहर खींचते हैं। जहां इसे में परिवर्तित किया Objectगया है) किसी भी तरह से यह नहीं बता सकता है कि यह केवल एक सूची है Personऔर केवल किसी अन्य सरणी सूची से युक्त नहीं है।

C ++ टेम्प्लेट आपको कुछ इस तरह की घोषणा करने की अनुमति देता है

std::list<Person>* foo = new std::list<Person>();

यह सी # और जावा जेनरिक की तरह दिखता है, और यह वही करेगा जो आपको लगता है कि इसे करना चाहिए, लेकिन पर्दे के पीछे अलग-अलग चीजें हो रही हैं।

इसमें C # जेनेरिक के साथ सबसे आम है कि यह विशेष pseudo-classesप्रकार की सूचनाओं को फेंकने के बजाय विशेष बनाता है जैसे जावा करता है, लेकिन यह मछली की पूरी तरह से अलग केतली है।

C # और Java दोनों आउटपुट देते हैं जो वर्चुअल मशीनों के लिए डिज़ाइन किया गया है। यदि आप कुछ कोड लिखते हैं, जिसमें एक Personवर्ग है, तो दोनों स्थितियों में एक Personवर्ग के बारे में कुछ जानकारी .dll या .class फ़ाइल में जाएगी, और JVM / CLR इसके साथ सामान करेगा।

C ++ कच्चे x86 बाइनरी कोड का उत्पादन करता है। सब कुछ एक वस्तु नहीं है , और कोई अंतर्निहित आभासी मशीन नहीं है जिसे किसी Personवर्ग के बारे में जानना आवश्यक है । कोई बॉक्सिंग या अनबॉक्सिंग नहीं है, और फ़ंक्शंस कक्षाओं, या वास्तव में कुछ भी करने के लिए नहीं है।

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

C # और Java में, जेनेरिक सिस्टम को यह जानने की जरूरत है कि एक क्लास के लिए कौन से तरीके उपलब्ध हैं, और इसे वर्चुअल मशीन के पास भेजना होगा। इसका यह बताने का एकमात्र तरीका है कि वास्तविक कक्षा को हार्ड-कोडिंग करके या इंटरफेस का उपयोग करके। उदाहरण के लिए:

string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

यह कोड C # या Java में संकलित नहीं होगा, क्योंकि यह नहीं जानता कि प्रकार Tवास्तव में नाम () नामक एक विधि प्रदान करता है। आपको इसे बताना होगा - C # में इस तरह:

interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }

और फिर आपको यह सुनिश्चित करना होगा कि आप जिन चीजों को जोड़ने के लिए पास हों, वे IHasName इंटरफ़ेस को लागू करें और इसी तरह। जावा सिंटैक्स अलग है ( <T extends IHasName>), लेकिन यह समान समस्याओं से ग्रस्त है।

इस समस्या के लिए 'क्लासिक' मामला एक फ़ंक्शन लिखने की कोशिश कर रहा है जो ऐसा करता है

string addNames<T>( T first, T second ) { return first + second; }

आप वास्तव में इस कोड को नहीं लिख सकते क्योंकि इसमें +विधि के साथ एक इंटरफ़ेस घोषित करने के कोई तरीके नहीं हैं । आप विफल हुए।

C ++ इनमें से किसी भी समस्या से ग्रस्त है। कंपाइलर किसी भी वीएम को नीचे टाइपिंग के बारे में परवाह नहीं करता है - यदि आपकी दोनों वस्तुओं में एक .Name () फ़ंक्शन है, तो वह संकलन करेगा। यदि वे नहीं करते हैं, तो यह नहीं होगा। सरल।

इसलिए यह अब आपके पास है :-)


8
C # में संदर्भ प्रकारों के लिए उत्पन्न pseudoclasess समान कार्यान्वयन साझा करें ताकि आपको बिल्कुल ListOfPeople नहीं मिले। की जाँच करें blogs.msdn.com/ericlippert/archive/2009/07/30/...
पिओर Czapla

4
नहीं, आप जेनेरिक का उपयोग करके जावा 5 कोड को संकलित नहीं कर सकते हैं , और क्या यह पुराने 1.4 वीएम पर चलता है (कम से कम सन जेडडीके इसे लागू नहीं करता है। कुछ 3 पार्टी उपकरण करते हैं।) आप जो कर सकते हैं वह पहले से संकलित 1.4 जेएआर से उपयोग कर सकते हैं। 1.5 / 1.6 कोड।
फिनेव

4
मुझे इस कथन पर आपत्ति है कि आप int addNames<T>( T first, T second ) { return first + second; }C # में नहीं लिख सकते । सामान्य प्रकार को इंटरफ़ेस के बजाय एक वर्ग तक सीमित किया जा सकता है, और इसमें +ऑपरेटर के साथ एक वर्ग घोषित करने का एक तरीका है।
मश्मगर

4
@AlexanderMalakhov यह उद्देश्य पर गैर मुहावरेदार है। बिंदु C ++ के मुहावरों के बारे में शिक्षित करने के लिए नहीं था, लेकिन यह स्पष्ट करने के लिए कि प्रत्येक भाषा द्वारा समान-दिखने वाले कोड को कैसे अलग तरीके से संभाला जाता है। यह लक्ष्य अधिक भिन्न कोड प्राप्त करने के लिए कठिन होता
ओरियन एडवर्ड्स

3
@phresnel मैं सिद्धांत रूप में सहमत हूं, लेकिन अगर मैं मुहावरेदार सी ++ में स्निपेट लिखता हूं, तो यह सी # / जावा डेवलपर्स के लिए काफी कम समझ में आता है, और इसलिए (मेरा मानना ​​है) ने अंतर को समझाने में एक बदतर काम किया होगा। आइए इस एक असहमत पर सहमत हों :-)
ओरियन एडवर्ड्स

61

C ++ शायद ही कभी "जेनरिक" शब्दावली का उपयोग करता है। इसके बजाय, "टेम्पलेट" शब्द का उपयोग किया जाता है और अधिक सटीक होता है। टेम्पलेट एक जेनेरिक डिज़ाइन को प्राप्त करने के लिए एक तकनीक का वर्णन करता है ।

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

template <unsigned int N>
struct product {
    static unsigned int const VALUE = N * product<N - 1>::VALUE;
};

template <>
struct product<1> {
    static unsigned int const VALUE = 1;
};

// Usage:
unsigned int const p5 = product<5>::VALUE;

यह कोड C ++ टेम्प्लेट की अन्य विशिष्ट सुविधा का भी उपयोग करता है, अर्थात् टेम्पलेट विशेषज्ञता। कोड एक वर्ग टेम्पलेट को परिभाषित करता है, productजिसमें एक मान तर्क होता है। यह उस टेम्प्लेट के लिए एक विशेषज्ञता को भी परिभाषित करता है जिसका उपयोग जब भी तर्क का मूल्यांकन किया जाता है 1. यह मुझे टेम्प्लेट प्रतियोगिताओं पर एक पुनरावृत्ति को परिभाषित करने की अनुमति देता है। मेरा मानना ​​है कि यह पहली बार आंद्रेई अलेक्जेंड्रेस्कु द्वारा खोजा गया था ।

C ++ के लिए टेम्पलेट विशेषज्ञता महत्वपूर्ण है क्योंकि यह डेटा संरचनाओं में संरचनात्मक अंतर के लिए अनुमति देता है। संपूर्ण के रूप में टेम्प्लेट एक प्रकार के इंटरफ़ेस को एकीकृत करने का एक साधन है। हालाँकि, हालांकि यह वांछनीय है, सभी प्रकार के कार्यान्वयन के अंदर समान रूप से व्यवहार नहीं किया जा सकता है। C ++ टेम्पलेट इसे ध्यान में रखते हैं। यह वही अंतर है जो OOP वर्चुअल तरीकों के ओवरराइडिंग के साथ इंटरफेस और कार्यान्वयन के बीच बनाता है।

C ++ टेम्प्लेट इसके एल्गोरिथम प्रोग्रामिंग प्रतिमान के लिए आवश्यक हैं। उदाहरण के लिए, कंटेनरों के लिए लगभग सभी एल्गोरिदम को उन कार्यों के रूप में परिभाषित किया गया है जो कंटेनर प्रकार को एक टेम्पलेट प्रकार के रूप में स्वीकार करते हैं और उनके साथ समान व्यवहार करते हैं। वास्तव में, यह बिल्कुल सही नहीं है: C ++ कंटेनरों पर काम नहीं करता है, बल्कि उन श्रेणियों पर काम करता है जो दो पुनरावृत्तियों द्वारा परिभाषित की जाती हैं, कंटेनर की शुरुआत और पीछे की ओर इशारा करती हैं। इस प्रकार, संपूर्ण सामग्री पुनरावृत्तियों द्वारा परिचालित होती है: आरंभ <= तत्व <अंत।

कंटेनरों के बजाय पुनरावृत्तियों का उपयोग करना उपयोगी है क्योंकि यह एक कंटेनर के हिस्सों को पूरे के बजाय संचालित करने की अनुमति देता है।

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

template <typename T>
class Store {  }; // (1)

यह किसी भी तत्व प्रकार के लिए काम करता है। लेकिन हम कहते हैं कि हम कुछ विशेष चाल को लागू करके पॉइंटर्स को अन्य प्रकारों की तुलना में अधिक कुशलता से स्टोर कर सकते हैं। हम सभी सूचक प्रकारों के लिए आंशिक रूप से विशेषज्ञता द्वारा ऐसा कर सकते हैं :

template <typename T>
class Store<T*> {  }; // (2)

अब, जब भी हम एक प्रकार के लिए एक कंटेनर टेम्पलेट लेते हैं, तो उपयुक्त परिभाषा का उपयोग किया जाता है:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.

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

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

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

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

35

एंडर्स हेजलबर्ग ने स्वयं यहां " जेनरिक इन सी #, जावा और सी ++ " के अंतरों का वर्णन किया ।


मैं वास्तव में उस साक्षात्कार को पसंद करता हूं। यह गैर-सी # लोगों के लिए यह स्पष्ट करता है कि मुझ जैसे सी # जेनरिक के साथ क्या हो रहा है।
जोहान्स शाउब -

18

अंतर क्या हैं, इस बारे में पहले से ही बहुत सारे अच्छे उत्तर हैं, इसलिए मुझे थोड़ा अलग दृष्टिकोण देना चाहिए और क्यों जोड़ना चाहिए ।

जैसा कि पहले ही समझाया गया था, मुख्य अंतर प्रकार का क्षरण है , अर्थात यह तथ्य कि जावा कंपाइलर जेनेरिक प्रकारों को मिटा देता है और वे उत्पन्न बायटेकोड में समाप्त नहीं होते हैं। हालांकि, सवाल यह है कि कोई ऐसा क्यों करेगा? इसका कोई मतलब नहीं है! या करता है?

खैर, विकल्प क्या है? यदि आप भाषा में जेनरिक लागू नहीं करते हैं, तो आप उन्हें कहाँ लागू करते हैं? और जवाब है: वर्चुअल मशीन में। जो पीछे की संगतता को तोड़ता है।

दूसरी ओर टाइप इरेज़र, आपको जेनेरिक क्लाइंट को गैर-जेनेरिक लाइब्रेरीज़ के साथ मिलाने की अनुमति देता है। दूसरे शब्दों में: जावा 5 पर संकलित कोड को अभी भी जावा 1.4 में तैनात किया जा सकता है।

हालाँकि, Microsoft ने जेनरिक के लिए पीछे की संगतता को तोड़ने का फैसला किया। यही कारण है कि .NET जेनरिक जावा जेनेरिक की तुलना में "बेहतर" हैं।

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

अभी तक एक और तरीका रखें: जावा में, जेनेरिक भाषा का एक हिस्सा है (जिसका अर्थ है कि वे केवल जावा पर लागू होते हैं , अन्य भाषाओं में नहीं), .NET में वे वर्चुअल मशीन (जिसका अर्थ है कि वे सभी पर लागू होते हैं) भाषाओं हैं, नहीं) बस C # और Visual Basic.NET)।

LINQ, लैम्ब्डा एक्सप्रेशंस, लोकल वैरिएबल टाइप इंट्रेंस, अनाम टाइप्स और एक्सप्रेशन ट्री जैसे .NET फीचर्स से इसकी तुलना करें: ये सभी लैंग्वेज फीचर्स हैं। इसीलिए VB.NET और C # के बीच सूक्ष्म अंतर हैं: यदि वे सुविधाएँ VM का हिस्सा थीं, तो वे सभी भाषाओं में समान होंगी । लेकिन CLR नहीं बदला है: यह अभी भी .NET 3.5 SP1 में वैसा ही है जैसा कि .NET 2.0 में था। आप .NET 3.5 कंपाइलर के साथ LINQ का उपयोग करने वाले C # प्रोग्राम को संकलित कर सकते हैं और अभी भी इसे .NET 2.0 पर चला सकते हैं, बशर्ते कि आप किसी भी .NET 3.5 लाइब्रेरी का उपयोग न करें। यह जेनेरिक और .NET 1.1 के साथ काम नहीं करेगा , लेकिन यह जावा और जावा 1.4 के साथ काम करेगा


3
LINQ मुख्य रूप से एक पुस्तकालय सुविधा है (हालाँकि C # और VB ने इसके साथ वाक्य रचना चीनी भी जोड़ा है)। कोई भी भाषा जो 2.0 सीएलआर को लक्षित करती है, केवल System.Core असेंबली को लोड करके LINQ का पूर्ण उपयोग प्राप्त कर सकती है।
रिचर्ड बर्ग

हाँ, क्षमा करें, मुझे और अधिक स्पष्ट wrt होना चाहिए था। LINQ। मैं क्वेरी सिंटैक्स की बात कर रहा था, न कि मानदंड मानक क्वेरी ऑपरेटरों की, LINQ एक्सटेंशन विधियों या IQueryable इंटरफ़ेस की। जाहिर है, आप किसी भी .NET भाषा से उन का उपयोग कर सकते हैं।
जोर्ग डब्ल्यू मित्तग

1
मैं जावा के लिए एक और विकल्प सोच रहा हूं। यहां तक ​​कि ओरेकल पिछड़ी संगतता को तोड़ना नहीं चाहता है, फिर भी वे टाइप की गई जानकारी को मिटाने से बचने के लिए कुछ कंपाइलर ट्रिक बना सकते हैं। उदाहरण के लिए, ArrayList<T>एक (छिपे हुए) स्थिर Class<T>क्षेत्र के साथ आंतरिक रूप से नामित नए प्रकार के रूप में उत्सर्जित किया जा सकता है । जब तक जेनेरिक लिबास के नए संस्करण को 1.5+ बाइट कोड के साथ तैनात किया गया है, तब तक यह 1.4- JVM पर चलने में सक्षम होगा।
पृथ्वी इंजन

14

मेरे पिछले पोस्ट करने के लिए अनुवर्ती।

टेम्प्लेट मुख्य कारणों में से एक है कि C ++ इंटेलीजेंस में इतना कम क्यों विफल हो जाता है, भले ही आईडीई का उपयोग नहीं किया गया हो। टेम्पलेट विशेषज्ञता के कारण, IDE वास्तव में निश्चित नहीं हो सकता है कि कोई सदस्य मौजूद है या नहीं। विचार करें:

template <typename T>
struct X {
    void foo() { }
};

template <>
struct X<int> { };

typedef int my_int_type;

X<my_int_type> a;
a.|

अब, कर्सर संकेतित स्थिति में है और यह आईडीई के लिए उस बिंदु पर कहना मुश्किल है, अगर और क्या है, सदस्यों के aपास। अन्य भाषाओं के लिए पार्सिंग सीधी होगी लेकिन C ++ के लिए, पहले से काफी मूल्यांकन की आवश्यकता है।

ये और ख़राब हो जाता है। क्या होगा अगर my_int_typeएक वर्ग टेम्पलेट के अंदर भी परिभाषित किया गया? अब इसका प्रकार दूसरे प्रकार के तर्क पर निर्भर करेगा। और यहाँ, यहां तक ​​कि संकलक भी विफल हो जाते हैं।

template <typename T>
struct Y {
    typedef T my_type;
};

X<Y<int>::my_type> b;

थोड़ा सोचने के बाद, एक प्रोग्रामर यह निष्कर्ष निकालता है कि यह कोड उपरोक्त के समान है: Y<int>::my_typeकरने के लिए हल करता है int, इसलिए bउसी प्रकार होना चाहिए a, है ना?

गलत। जिस बिंदु पर संकलक इस कथन को हल करने की कोशिश करता है, वह वास्तव में Y<int>::my_typeअभी तक नहीं जानता है! इसलिए, यह नहीं जानता कि यह एक प्रकार है। यह कुछ और हो सकता है, उदाहरण के लिए एक सदस्य फ़ंक्शन या फ़ील्ड। यह अस्पष्टता को जन्म दे सकता है (हालांकि वर्तमान मामले में नहीं), इसलिए कंपाइलर विफल हो जाता है। हमें यह स्पष्ट रूप से बताना होगा कि हम एक प्रकार के नाम का उल्लेख करते हैं:

X<typename Y<int>::my_type> b;

अब, कोड संकलित करता है। इस स्थिति से कैसे अस्पष्टता उत्पन्न होती है, यह देखने के लिए निम्नलिखित कोड पर विचार करें:

Y<int>::my_type(123);

यह कोड स्टेटमेंट पूरी तरह से मान्य है और फ़ंक्शन कॉल को निष्पादित करने के लिए C ++ को बताता है Y<int>::my_type। हालांकि, यदि my_typeकोई फ़ंक्शन नहीं है, बल्कि एक प्रकार है, तो यह कथन अभी भी मान्य होगा और एक विशेष कलाकारों (फ़ंक्शन-स्टाइल कास्ट) का प्रदर्शन करेगा जो अक्सर एक रचनाकार मंगलाचरण होता है। कंपाइलर यह नहीं बता सकता है कि हमारा क्या मतलब है इसलिए हमें यहां से निकलना होगा।


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

6

जावा और C # दोनों ने अपनी पहली भाषा रिलीज़ के बाद जेनरिक की शुरुआत की। हालांकि, इस बात पर मतभेद हैं कि जब जेनरिक को पेश किया गया तो कोर लाइब्रेरी कैसे बदल गई। सी # की जेनरिक सिर्फ कंपाइलर मैजिक नहीं है और इसलिए मौजूदा लाइब्रेरी क्लासेस को पीछे की अनुकूलता को तोड़े बिना इसे बनाना संभव नहीं था ।

उदाहरण के लिए, जावा में मौजूदा कलेक्शंस फ्रेमवर्क पूरी तरह से जेनेरिक था । जावा के पास संग्रह कक्षाओं का एक सामान्य और विरासत गैर-सामान्य संस्करण दोनों नहीं है। कुछ मायनों में यह बहुत साफ है - अगर आपको C # में एक संग्रह का उपयोग करने की आवश्यकता है, तो गैर-सामान्य संस्करण के साथ जाने के लिए वास्तव में बहुत कम कारण है, लेकिन उन विरासत वर्गों को जगह मिलती है, जो परिदृश्य को अव्यवस्थित करते हैं।

एक और उल्लेखनीय अंतर जावा और C # में Enum कक्षाएं हैं। जावा के एनम की यह कुछ हद तक यातनापूर्ण परिभाषा है:

//  java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

(देखें एंजेलिका लैंगर की स्पष्ट व्याख्या कि ऐसा क्यों है। अनिवार्य रूप से, इसका अर्थ है कि जावा स्ट्रिंग से अपने Enum मान तक सुरक्षित पहुँच दे सकता है:

//  Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");

इसकी तुलना C # के संस्करण से करें:

//  Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");

चूंकि Enum भाषा में पेश किए जाने से पहले C # में पहले से मौजूद था, मौजूदा कोड को तोड़े बिना परिभाषा नहीं बदल सकती थी। इसलिए, संग्रह की तरह, यह इस विरासत की स्थिति में मुख्य पुस्तकालयों में बना हुआ है।


यहां तक ​​कि C # के जेनरिक सिर्फ कंपाइलर मैजिक नहीं हैं, कंपाइलर मौजूदा लाइब्रेरी को जेनरेट करने के लिए आगे मैजिक कर सकते हैं। कोई कारण नहीं है कि वे नाम बदलने की आवश्यकता है ArrayListके लिए List<T>और यह एक नया नाम स्थान में डाल दिया। तथ्य यह है, अगर स्रोत कोड में एक वर्ग दिखाई देता है क्योंकि ArrayList<T>यह आईएल कोड में एक अलग संकलित उत्पन्न वर्ग नाम बन जाएगा, इसलिए कोई नाम संघर्ष नहीं हो सकता है।
पृथ्वी इंजन

4

11 महीने देर से, लेकिन मुझे लगता है कि यह सवाल कुछ जावा वाइल्डकार्ड सामान के लिए तैयार है।

यह जावा की एक वाक्यात्मक विशेषता है। मान लीजिए कि आपके पास एक तरीका है:

public <T> void Foo(Collection<T> thing)

और मान लें कि आपको विधि बॉडी में टाइप T को संदर्भित करने की आवश्यकता नहीं है। आप एक नाम T घोषित कर रहे हैं और उसके बाद केवल एक बार इसका उपयोग कर रहे हैं, इसलिए आपको इसके लिए एक नाम क्यों सोचना चाहिए? इसके बजाय, आप लिख सकते हैं:

public void Foo(Collection<?> thing)

प्रश्न-चिह्न संकलक को यह दिखावा करने के लिए कहता है कि आपने एक सामान्य नामित प्रकार का पैरामीटर घोषित किया है जिसे केवल उस स्थान पर एक बार दिखाई देने की आवश्यकता है।

वाइल्डकार्ड्स के साथ ऐसा कुछ भी नहीं है जिसे आप एक नामित प्रकार के पैरामीटर के साथ भी नहीं कर सकते (जो कि ये चीजें हमेशा C ++ और C # में की जाती हैं)।


2
एक और 11 महीने देर से ... जावा वाइल्डकार्ड्स के साथ ऐसी चीजें हैं जो आप नामित प्रकारों के साथ नहीं कर सकते। आप इसे जावा में कर सकते हैं: class Foo<T extends List<?>>और उपयोग करें Foo<StringList>लेकिन C # में आपको उस अतिरिक्त प्रकार के पैरामीटर को जोड़ना होगा: class Foo<T, T2> where T : IList<T2>और क्लंकी का उपयोग करें Foo<StringList, String>
आर। मार्टिनो फर्नांडिस

2

विकिपीडिया में जावा / सी # जेनरिक और जावा जेनेरिक / सी ++ टेम्प्लेट दोनों की तुलना में शानदार लेखन है । जेनेरिक्स पर मुख्य लेख थोड़ा भरा हुआ लगता है, लेकिन यह उस में कुछ अच्छी जानकारी है।


1

सबसे बड़ी शिकायत टाइप इरेज़र है। उस में, क्रम के समय जेनरिक लागू नहीं किया जाता है। यहाँ इस विषय पर कुछ सन डॉक्स का लिंक दिया गया है

जेनरेशन टाइप इरेज़र द्वारा कार्यान्वित किए जाते हैं: सामान्य प्रकार की जानकारी केवल संकलन समय पर मौजूद होती है, जिसके बाद इसे कंपाइलर द्वारा मिटा दिया जाता है।


1

C ++ टेम्प्लेट वास्तव में उनके C # और Java समकक्षों की तुलना में बहुत अधिक शक्तिशाली होते हैं क्योंकि उनका मूल्यांकन संकलन समय और समर्थन विशेषज्ञता पर किया जाता है। यह टेम्प्लेट मेटा-प्रोग्रामिंग के लिए अनुमति देता है और C ++ कंपाइलर को ट्यूरिंग मशीन के बराबर बनाता है (यानी संकलन प्रक्रिया के दौरान आप किसी भी चीज की गणना कर सकते हैं जो ट्यूरिंग मशीन के साथ अभिकलन है)।


1

जावा में, जेनरिक केवल कंपाइलर स्तर होते हैं, इसलिए आपको यह मिलता है:

a = new ArrayList<String>()
a.getClass() => ArrayList

ध्यान दें कि 'a' का प्रकार एक अरै लिस्ट है, स्ट्रिंग्स की लिस्ट नहीं। तो केले की एक सूची का प्रकार बंदरों की सूची के बराबर () होगा।

इतनी बात करने के लिए।


1

ऐसा लगता है, अन्य बहुत ही दिलचस्प प्रस्तावों के बीच, जेनेरिक को परिष्कृत करने और पीछे की संगतता को तोड़ने के बारे में एक है:

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

जावा 7 प्रस्तावों के बारे में एलेक्स मिलर के लेख में


0

NB: मेरे पास टिप्पणी करने के लिए पर्याप्त बिंदु नहीं है, इसलिए इसे उचित उत्तर के लिए टिप्पणी के रूप में स्थानांतरित करने के लिए स्वतंत्र महसूस करें।

लोकप्रिय विश्वास के विपरीत, जिसे मैं कभी नहीं समझता कि यह कहां से आया है, .net ने पिछड़ी संगतता को तोड़ने के बिना सच्चे जेनरिक को लागू किया, और उन्होंने इसके लिए स्पष्ट प्रयास किया। आपको अपने गैर-जेनेरिक .net 1.0 कोड को जेनेटिक्स में बदलने की जरूरत नहीं है। नेट 2.0 में इस्तेमाल किया जा सकता है। जेनेरिक और गैर-जेनेरिक दोनों सूचियाँ अभी भी .net ढाँचे 2.0 में 4.0 तक उपलब्ध हैं, वास्तव में कुछ और नहीं बल्कि पिछड़ी अनुकूलता के कारण। इसलिए पुराने कोड जो अभी भी गैर-जेनेरिक ArrayList का उपयोग करते थे, अब भी काम करेंगे, और पहले की तरह ही ArrayList वर्ग का उपयोग करेंगे। पिछड़े कोड की संगतता हमेशा 1.0 से अब तक बनी हुई है ... इसलिए यहां तक ​​कि .net 4.0 में, आपको अभी भी 1.0 बीसीएल से किसी भी गैर-जेनेरिक क्लास का उपयोग करने का विकल्प चुनना है यदि आप ऐसा करना चाहते हैं।

इसलिए मुझे नहीं लगता कि जावा को सच्ची जेनरिक का समर्थन करने के लिए पिछड़ेपन को तोड़ना होगा।


यह उस तरह की पिछड़ी अनुकूलता नहीं है जिस पर लोग बात करते हैं। रनटाइम के लिए विचार पश्चगामी अनुकूलता है : .NET 2.0 में जेनेरिक का उपयोग करते हुए लिखा गया कोड .NET फ्रेमवर्क / CLR के पुराने संस्करणों पर नहीं चलाया जा सकता है। इसी तरह, यदि जावा को "सही" जेनरिक का परिचय देना था, तो नया जावा कोड पुराने जेवीएम पर नहीं चल पाएगा (क्योंकि इसके लिए बाइटकोड में परिवर्तन की आवश्यकता होती है)।
तजमान

यह .net, जेनरिक नहीं है। हमेशा विशिष्ट CLR संस्करण को लक्षित करने के लिए पुनर्संयोजन की आवश्यकता होती है। वहाँ कोड संगतता है, कोड संगतता है। और साथ ही, मैं विशेष रूप से पुराने कोड को बदलने की आवश्यकता के बारे में उत्तर दे रहा था जो कि पुरानी सूची का उपयोग नई जेनरिक सूची का उपयोग करने के लिए कर रहा था, जो बिल्कुल भी सच नहीं है।
शीप

1
मुझे लगता है कि लोग आगे की कॉम्पिटिटिवनेस के बारे में बात कर रहे हैं । I .net .net 1.1 पर चलने के लिए 2.0 कोड। जो कि टूट जाएगा क्योंकि 1.1 रनटाइम 2.0 "छद्म वर्ग" के बारे में कुछ नहीं जानता है। क्या तब यह नहीं होना चाहिए कि "जावा सामान्य सच को लागू नहीं करता है क्योंकि वे आगे संगतता बनाए रखना चाहते हैं"? (पिछड़े के बजाय)
शीप

संगतता मुद्दे सूक्ष्म हैं। मुझे नहीं लगता कि समस्या यह थी कि जावा में "वास्तविक" जेनरिक को जोड़ने से जावा के पुराने संस्करणों का उपयोग करने वाले किसी भी कार्यक्रम को प्रभावित किया जाएगा, बल्कि उस कोड को जो "नए बेहतर" जेनरिक का उपयोग करता था, पुराने कोड के साथ ऐसी वस्तुओं का आदान-प्रदान करने में एक कठिन समय होगा। नए प्रकारों के बारे में कुछ नहीं पता था। मान लीजिए, उदाहरण के लिए, एक कार्यक्रम में एक है ArrayList<Foo>कि यह एक पुरानी विधि को पारित करना चाहता है जिसे ArrayListउदाहरणों के साथ आबाद करना है Foo। यदि कोई ArrayList<foo>नहीं है ArrayList, तो वह काम कैसे करता है?
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.