C # और जावा कार्यान्वयन में, वस्तुओं में आमतौर पर अपनी कक्षा के लिए एक एकल सूचक होता है। यह संभव है क्योंकि वे एकल-विरासत भाषाएँ हैं। तब वर्ग संरचना में एकल-वंशानुक्रम पदानुक्रम के लिए व्यवहार्य होता है। लेकिन कॉलिंग इंटरफ़ेस विधियों में कई विरासत की सभी समस्याएं भी हैं। यह आमतौर पर वर्ग संरचना में सभी कार्यान्वित इंटरफेस के लिए अतिरिक्त vtables डालकर हल किया जाता है। यह C ++ में विशिष्ट वर्चुअल इनहेरिटेंस कार्यान्वयन की तुलना में स्थान बचाता है, लेकिन इंटरफ़ेस विधि को अधिक जटिल बना देता है - जिसे कैशिंग द्वारा आंशिक रूप से मुआवजा दिया जा सकता है।
उदाहरण के लिए OpenJDK JVM में, हर वर्ग के सभी कार्यान्वित इंटरफेस के लिए vtables की एक सरणी शामिल हैं (एक अंतरफलक vtable एक कहा जाता है itable )। जब एक इंटरफ़ेस विधि कहा जाता है, तो इस सरणी को उस इंटरफ़ेस के चलने योग्य के लिए रेखीय रूप से खोजा जाता है, फिर उस पठनीय के माध्यम से विधि को भेजा जा सकता है। कैशिंग का उपयोग किया जाता है ताकि प्रत्येक कॉल साइट विधि प्रेषण का परिणाम याद रखे, इसलिए यह खोज केवल तब दोहराई जानी चाहिए जब ठोस वस्तु प्रकार बदल जाती है। विधि प्रेषण के लिए स्यूडोकोड:
// Dispatch SomeInterface.method
Method const* resolve_method(
Object const* instance, Klass const* interface, uint itable_slot) {
Klass const* klass = instance->klass;
for (Itable const* itable : klass->itables()) {
if (itable->klass() == interface)
return itable[itable_slot];
}
throw ...; // class does not implement required interface
}
(OpenJDK हॉटस्पॉट दुभाषिया या x86 संकलक में वास्तविक कोड की तुलना करें ।)
C # (या अधिक सटीक, CLR) संबंधित दृष्टिकोण का उपयोग करता है। हालाँकि, यहाँ itables में विधियों के संकेत नहीं हैं, लेकिन स्लॉट मैप हैं: वे कक्षा के मुख्य भाग में प्रविष्टियों को इंगित करते हैं। जावा के साथ, सही चलने योग्य की खोज करने के लिए केवल सबसे खराब स्थिति है, और यह उम्मीद की जाती है कि कॉल साइट पर कैशिंग लगभग हमेशा इस खोज से बच सकती है। CLR JIT- संकलित मशीन कोड को अलग-अलग कैशिंग रणनीतियों के साथ पैच करने के लिए वर्चुअल स्टब डिस्पैच नामक तकनीक का उपयोग करता है। स्यूडोकोड:
Method const* resolve_method(
Object const* instance, Klass const* interface, uint interface_slot) {
Klass const* klass = instance->klass;
// Walk all base classes to find slot map
for (Klass const* base = klass; base != nullptr; base = base->base()) {
// I think the CLR actually uses hash tables instead of a linear search
for (SlotMap const* slot_map : base->slot_maps()) {
if (slot_map->klass() == interface) {
uint vtable_slot = slot_map[interface_slot];
return klass->vtable[vtable_slot];
}
}
}
throw ...; // class does not implement required interface
}
OpenJDK-pseudocode में मुख्य अंतर यह है कि OpenJDK में प्रत्येक वर्ग में सीधे या परोक्ष रूप से कार्यान्वित किए गए इंटरफेस की एक सरणी होती है, जबकि CLR केवल उन इंटरफेस के लिए स्लॉट मैप का एक सरणी रखता है जो उस वर्ग में सीधे लागू किए गए थे। इसलिए हमें विरासत के पदानुक्रम को ऊपर की ओर चलने की आवश्यकता है जब तक कि एक स्लॉट मानचित्र नहीं मिलता है। गहरी विरासत पदानुक्रम के लिए यह अंतरिक्ष बचत में परिणाम है। सीएलआर में ये विशेष रूप से प्रासंगिक हैं कि जेनेरिक को किस तरह से लागू किया जाता है: जेनेरिक स्पेशलाइजेशन के लिए, क्लास स्ट्रक्चर की नकल की जाती है और मुख्य वाइबेट में तरीकों को स्पेशलाइजेशन द्वारा प्रतिस्थापित किया जा सकता है। स्लॉट के नक्शे सही व्यवहार्य प्रविष्टियों पर इंगित करना जारी रखते हैं और इसलिए उन्हें किसी वर्ग की सभी सामान्य विशेषज्ञता के बीच साझा किया जा सकता है।
अंतिम नोट के रूप में, इंटरफ़ेस प्रेषण को लागू करने की अधिक संभावनाएं हैं। ऑब्जेक्ट में या कक्षा संरचना में vtable / itable पॉइंटर रखने के बजाय, हम ऑब्जेक्ट को वसा बिंदुओं का उपयोग कर सकते हैं , जो मूल रूप से एक (Object*, VTable*)
जोड़ी है। दोष यह है कि यह पॉइंटर्स के आकार को दोगुना करता है और यह अपस्टेक (एक कंक्रीट प्रकार से एक इंटरफ़ेस प्रकार तक) मुक्त नहीं है। लेकिन यह अधिक लचीला है, कम अप्रत्यक्ष है, और इसका मतलब यह भी है कि इंटरफेस को एक वर्ग से बाहरी रूप से लागू किया जा सकता है। संबंधित दृष्टिकोणों का उपयोग गो इंटरफेस, रस्ट ट्रैक्ट और हास्केल टाइपकालेज़ द्वारा किया जाता है।
संदर्भ और आगे पढ़ने:
- विकिपीडिया: इनलाइन कैशिंग । महंगी पद्धति देखने से बचने के लिए उपयोग किए जा सकने वाले कैशिंग दृष्टिकोणों पर चर्चा करता है। आमतौर पर व्यवहार्य-आधारित प्रेषण के लिए आवश्यक नहीं है, लेकिन उपरोक्त इंटरफ़ेस प्रेषण रणनीतियों जैसे अधिक महंगे प्रेषण तंत्र के लिए बहुत वांछनीय है।
- OpenJDK Wiki (2013): इंटरफ़ेस कॉल । विचार-विमर्श करता है।
- पोबर, न्यूवर्ड (2009): एसएससीएलआई 2.0 इंटर्नल्स। पुस्तक का अध्याय 5 स्लॉट के नक्शे पर बहुत विस्तार से चर्चा करता है। प्रकाशित नहीं किया गया था लेकिन लेखकों द्वारा अपने ब्लॉग पर उपलब्ध कराया गया था । पीडीएफ लिंक के बाद से ले जाया गया है। यह पुस्तक अब CLR की वर्तमान स्थिति को नहीं दर्शाती है।
- CoreCLR (2006): वर्चुअल स्टब डिस्पैच । में: रनटाइम की किताब। महंगे लुक्स से बचने के लिए स्लॉट मैप्स और कैशिंग पर चर्चा करता है।
- कैनेडी, साइमे (2001): नेट कॉमन लैंग्वेज रनटाइम के लिए डिजाइन और जेनेरिक्स का कार्यान्वयन । ( पीडीएफ लिंक )। जेनरिक को लागू करने के लिए विभिन्न दृष्टिकोणों पर चर्चा करता है। जेनेरिक विधि प्रेषण के साथ बातचीत करते हैं क्योंकि विधियाँ विशिष्ट हो सकती हैं इसलिए vtables को फिर से लिखना पड़ सकता है।