2011-01-02 14 views
5

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

उदाहरण के लिए

, अगर मेरे पास है:

class ClassA{ 
    public: 
     int x; 
     int y; 
     ClassA(){ 
      cout << "ClassA : " << (unsigned int)this << endl; 
     } 
    }; 

    class ClassC{ 
    public: 
     int cc; 
     int xx; 
     ClassC(){ 
      cout << "ClassC : " << (unsigned int)this << endl; 
     } 
    }; 

    class ClassB : public ClassC, public ClassA{ 
    public: 
     int z; 
     int v; 
     ClassB(){ 
      cout << "ClassB : " << (unsigned int)this << endl; 
     } 
    }; 


    int main(){ 

    ClassB * b = new ClassB(); 

    } 

वर्ग एक और वर्ग सी अलग-अलग पतों जब निर्माता मुद्रित है।

फिर भी, जब मैं उन्हें एक दूसरे के लिए वापस कास्ट करने के लिए प्रयास करते हैं, यह सिर्फ स्वतः काम करता है:

ClassA * the_a = (ClassA*)b; 
cout << "The A, casted : " << (unsigned int)the_a << endl; 

ClassB * the_b = (ClassB*)the_a; 
cout << "The B, casted back : " << (unsigned int)the_b << endl; 

मैं इस प्रकार की जानकारी कोड से संकलक द्वारा प्राप्त किया जा सकता है लगता है, लेकिन यह करने के लिए सुरक्षित है मान लें कि यह सभी कंपाइलरों पर काम करता है?

अतिरिक्त प्रश्न: क्या ऑर्डर को बल देना संभव है जिसमें सबक्लास स्थान जाते हैं? उदाहरण के लिए, यदि मुझे प्रथम श्रेणी में रहने के लिए क्लासए की आवश्यकता है (अनिवार्य रूप से, उसी सूचक स्थान को साझा करें) क्लाससी के रूप में जो इसे उपclass करता है, क्या मुझे इसे सबक्लास के घोषणापत्र में पहले रखना होगा? अद्यतन ठीक है, ऐसा लगता है कि ऑर्डर को मजबूर करना संभव नहीं है। क्या अभी भी संरचना के "रूट" पते, उपclass को आवंटित पते की शुरुआत सुपरक्लास स्तर पर खोजना संभव है? उदाहरण के लिए, कक्षा ए से कक्षा बी का पता प्राप्त करना।

+0

मुझे विश्वास नहीं है कि आप _ "रूट" _ पता प्राप्त कर सकते हैं। हालांकि मैं इसके बारे में एक अलग सवाल पूछूंगा। मानक से AFAICT, यह भी गारंटी नहीं है कि बेस क्लास के सदस्यों या उप-वस्तुओं को कम स्मृति पते में रखा गया है। दूसरे शब्दों में, इस बात की कोई गारंटी नहीं है कि 'the_a' के पास' the_b' या किसी भी प्रकार की तुलना में कम पता है। –

उत्तर

6

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

क्या ऑर्डर को बल देना संभव है जिसमें उपclass स्थान जाते हैं।

नहीं: लेआउट कार्यान्वयन परिभाषित किया गया है, और आपका कोड इस आदेश पर निर्भर नहीं होना चाहिए।

+0

मैं देखता हूं। क्या सुपरक्लस (कहें, कक्षा ए) के लिए पूरी संरचना का मूल पता प्राप्त करने के लिए एक संभावित तरीका है (कक्षा ए का पता अगर वह स्वयं है, या कक्षा बी का पता है तो यह कक्षा बी का हिस्सा है)? मैं एक मेमोरी पूल इम्प्लेमेंटेशन का निर्माण कर रहा था जो रूट पते पर निर्भर था। – kamziro

+0

@kamziro: असल में, मुझे नहीं लगता कि वहां है, लेकिन मुझे कहना है कि मुझे आश्चर्य है कि आपको – icecrime

+0

की आवश्यकता है। पूल में फ्री किए गए पते को वापस करने के लिए आवश्यक सबक्लासिंग द्वारा मेरा मेमोरी पूल कार्यान्वयन, और आधार पते को 4 बाइट्स को सहेजने के लिए नहीं है। चार मापने वाले बाइट्स .. मुझे लगता है कि अगर कोई विकल्प नहीं है तो बहुत बुरा है :) – kamziro

3

हां, आप यह सुनिश्चित कर सकते हैं कि यह सुरक्षित है। सी ++ में एक पॉइंटर टाइपकास्ट को बेस-टू-व्युत्पन्न या इसके विपरीत रूपांतरणों के लिए पॉइंटर को सही ढंग से समायोजित करने की गारंटी दी जाती है।

उसने कहा, आपको सावधान रहना होगा कि सिस्टम को बहुत दूर नहीं दबाया जाए।

ClassB* b = new ClassB; 
ClassC* c = (ClassC*)(void*)b; 

इसका कारण यह है टूटता तो जहां सूचक बी ऑब्जेक्ट के अंदर है के बारे में जानकारी है के लिए एक एक शून्य * के माध्यम से funneled हो, और सी से डाली: उदाहरण के लिए, संकलक इस रूपांतरण सही नहीं मिलेगा खो गया।

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

+0

सुधार के लिए धन्यवाद! यह इतनी बार आती है कि मुझे इसके साथ प्रयोग करने का मौका नहीं मिला है। – templatetypedef

+0

आभासी विरासत के बारे में अच्छी कॉल, जो एक मुश्किल विषय है। मुझे एहसास नहीं हुआ कि आपको 'dynamic_cast' का उपयोग करना था, लेकिन आप करते हैं। सी ++ 03 §5.2.9 अनुच्छेद 5 और 8 देखें, जो बेस से व्युत्पन्न कक्षा में कास्टिंग करते समय स्पष्ट रूप से विरासत को गैर-आभासी होने की आवश्यकता होती है। –

1

हाँ, आभासी अड्डों के लिए एक निष्पक्ष कार्यान्वयन व्युत्पन्न वस्तु में एक ज्ञात स्थान पर वर्चुअल बेस सबोबजेक्ट के लिए एक सूचक स्थान पर रखना है।

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

ऐसी कुछ डेटा संरचना के बिना, क्रॉस कास्ट काम नहीं करेगा। वर्चुअल बेस ए के अंदर वर्चुअल बेस ए से वर्चुअल बेस ए में कास्ट पार कर सकते हैं, भले ही बी सबोबजेक्ट दिखाई नहीं दे रहा है (बशर्ते केवल दो आधारों में साझा वर्चुअल बेस एक्स है यदि मुझे सही याद है)। जब आप इसके बारे में सोचते हैं तो यह सुंदर बालों वाली नेविगेशन है: नेविगेशन सबसे व्युत्पन्न वर्ग के आकार वर्णनकर्ता के माध्यम से जाता है (जो ए और बी दोनों को देख सकता है)।

और भी बालों वाली क्या है कि निर्माण और विनाश के दौरान गतिशील रूप से बदलने के लिए "मानक के कानून द्वारा" संरचना की आवश्यकता होती है, यही कारण है कि जटिल वस्तुओं का निर्माण और विनाश धीमा है। एक बार वे बनने के बाद, विधि कॉल, यहां तक ​​कि क्रॉस कॉल भी काफी तेज हैं।

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