2010-04-23 26 views
5

मान लीजिए कि मेरे पास Dog है जो Animal कक्षा से विरासत में है। कोड की इन दो पंक्तियों के बीच क्या अंतर है?आधार वर्ग सूचक बनाम विरासत वर्ग सूचक?

Animal *a = new Dog(); 
    Dog *d = new Dog(); 

एक में, सूचक आधार वर्ग के लिए है, और अन्य में, सूचक व्युत्पन्न वर्ग के लिए है। लेकिन यह भेद कब महत्वपूर्ण होगा? बहुरूपता के लिए, कोई भी बिल्कुल वही काम करेगा, है ना?

+1

इस विशिष्ट मामले के लिए यह वही काम करेगा। लेकिन मान लें कि आपके पास एक और कक्षा बिल्ली है जो पशु से विरासत में होती है। आप एक बिल्ली को उस समारोह में नहीं पारित कर सकते हैं जो कुत्ते (आसानी से) की अपेक्षा करता है, लेकिन आप एक बिल्ली को एक पशु को पास कर सकते हैं। पॉलिमॉर्फिज्म एक से अधिक व्युत्पन्न वर्ग – DaClown

उत्तर

11

प्रकार-चेकिंग के सभी प्रयोजनों के लिए, संकलक व्यवहार करता है a जैसे कि वह किसी भी पशु को इंगित कर सकता है, भले ही आप जानते हैं कि यह एक कुत्ते के लिए अंक:

  • आप a एक समारोह को पारित नहीं हो सकता Dog* की उम्मीद है।
  • आप a->fetchStick() नहीं कर सकते हैं, जहां fetchStickDog का सदस्य कार्य है लेकिन Animal नहीं है।
  • Dog *d2 = dynamic_cast<Dog*>(d) शायद आपके कंपाइलर पर सिर्फ एक सूचक प्रति है। Dog *d3 = dynamic_cast<Dog*>(a) शायद नहीं है (मैं यहां अनुमान लगा रहा हूं, मैं किसी भी कंपाइलर पर जांच करने की परेशानी नहीं कर रहा हूं। बिंदु यह है कि संकलक कोड को बदलने पर a और d के बारे में अलग-अलग धारणाएं बनाता है)।
  • आदि

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

गैर-आभासी कार्यों के लिए जो पशु में परिभाषित हैं, और कुत्ते में परिभाषित (अधिभारित) के लिए, a के माध्यम से उस फ़ंक्शन को कॉल करना d के माध्यम से कॉल करने से अलग है।

1

नहीं, वे समान नहीं हैं।

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

-1

यह रन टाइम पर कोई वास्तविक अंतर बना देता है के रूप में दो उदाहरणों में एक ही कर रहे हैं:

पशु सूचक पशु से किसी उपवर्ग देख सकते हैं। एकमात्र अंतर संकलन समय पर है, जहां आप उदाहरण के लिए कॉल कर सकते हैं-> छाल() लेकिन एक-> छाल() नहीं, भले ही वास्तव में एक कुत्ता शामिल हो। कंपाइलर चर को एक जानवर होने के लिए मानता है और केवल यही।

+0

के साथ "केवल" समझ में आता है यदि आप ऑब्जेक्ट के गैर-वर्चुअल फ़ंक्शन को कॉल करने के लिए जाते हैं। – tloach

+0

* "यह रन टाइम पर कोई वास्तविक अंतर नहीं बनाता है ... केवल एक ही अंतर रन टाइम पर है ** –

+0

हाँ ... मेरा मतलब संकलन समय क्षमा करें:] – Shtong

4

इस सवाल का जवाब एक विशाल है: यह निर्भर करता है

ऐसे कई तरीके है जिसमें सूचक के प्रकार के महत्वपूर्ण हो सकता है। सी ++ एक बहुत ही जटिल भाषा है और यह दिखाए जाने वाले तरीकों में से एक विरासत के साथ है।

आइए कई तरीकों से प्रदर्शित करने के लिए एक छोटा सा उदाहरण लें, जिसमें इससे कोई फर्क पड़ता है।

class Animal { 
public: 
    virtual void MakeSound(const char* pNoise) { ... } 
    virtual void MakeSound() { ... } 
}; 

class Dog : public Animal { 
public: 
    virtual void MakeSound() {... } 
}; 

int main() { 
    Animal* a = new Dog(); 
    Dog* d = new Dog(); 
    a->MakeSound("bark"); 
    d->MakeSound("bark"); // Does not compile 
    return 0; 
} 

कारण सी ++ नाम लुकअप का तरीका है। संक्षेप में: C++ को कॉल करने के लिए किसी विधि की तलाश करते समय टाइप पदानुक्रम पहले प्रकार की तलाश करेगा जिसमें मिलान करने वाले नाम की विधि है।इसके बाद उस प्रकार के नाम से उस नाम के साथ विधियों से सही अधिभार की तलाश होगी। चूंकि Dog केवल MakeSound विधि को कोई पैरामीटर नहीं देता है, कोई अधिभार मिलान नहीं करता है और यह संकलित करने में विफल रहता है।

+0

इसे" पशु :: मेकसाउंड का उपयोग करके चिपकाकर दूर किया जा सकता है (कॉन्स चार *) "डॉग क्लास परिभाषा में, मुझे विश्वास है। –

+0

@ डैश-टॉम-बैंग यह निश्चित रूप से कर सकता है लेकिन यह केवल एक उदाहरण है जहां व्यवहार – JaredPar

+0

से भिन्न हो सकता है।) –

0

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

2

पहली पंक्ति आप पर पशु वर्ग के सदस्य ही कॉल करने की अनुमति एक:

Animal *a = new Dog(); 
a->eat(); // assuming all Animal can eat(), here we will call Dog::eat() implementation. 
a->bark(); // COMPILATION ERROR : bark() is not a member of Animal! Even if it's available in Dog, here we manipulate an Animal. 

हालांकि (दूसरों के द्वारा बताया के रूप में), में a के रूप में इस cas अभी भी एक पशु है, तो आप प्रदान नहीं कर सकते a एक अधिक विशिष्ट बच्चे वर्ग कुत्ता है कि के लिए पूछ एक समारोह के एक पैरामीटर के रूप में:

:

void toy(Dog* dog); 

toy(a); // COMPILATION ERROR : we want a Dog! 

दूसरी पंक्ति आप बच्चे को कक्षा के विशिष्ट कार्यों उपयोग करने की अनुमति

Dog *a = new Dog(); 
a->bark(); // works, but only because we're manipulating a Dog 

तो बेस क्लास का उपयोग अपने वर्ग पदानुक्रम के "जेनेरिक" इंटरफ़ेस के रूप में करें (जिससे आप अपने सभी जानवरों को खाने के लिए अनुमति दे सकते हैं() कैसे परेशान करते हैं)।

2

जब आप पॉइंटर का उपयोग करके वर्चुअल फ़ंक्शन को कॉल करते हैं तो भेद महत्वपूर्ण है। मान लें कि पशु और कुत्ते दोनों में do_stuff() नामक फ़ंक्शन हैं।

  1. तो पशु :: do_stuff() आभासी घोषित किया जाता है, do_stuff बुला() एक पशु सूचक पर कुत्ता :: do_stuff फोन करेगा()।

  2. यदि पशु :: do_stuff() को वर्चुअल घोषित नहीं किया गया है, तो एक पशु सूचक पर do_stuff() को कॉल करना Animal :: do_stuff() को कॉल करेगा।

यहाँ प्रदर्शित करने के लिए एक पूर्ण काम कर कार्यक्रम है:

#include <iostream> 

class Animal { 
public: 
     void do_stuff() { std::cout << "Animal::do_stuff\n"; } 
     virtual void virt_stuff() { std::cout << "Animal::virt_stuff\n"; } 
}; 

class Dog : public Animal { 
public: 
     void do_stuff() { std::cout << "Dog::do_stuff\n"; } 
     void virt_stuff() { std::cout << "Dog::virt_stuff\n"; } 
}; 

int main(int argc, char *argv[]) 
{ 
     Animal *a = new Dog(); 
     Dog *b = new Dog(); 

     a->do_stuff(); 
     b->do_stuff(); 
     a->virt_stuff(); 
     b->virt_stuff(); 
} 

आउटपुट:

Animal::do_stuff 
Dog::do_stuff 
Dog::virt_stuff 
Dog::virt_stuff 

यह सिर्फ एक उदाहरण है। अन्य उत्तरों में अन्य महत्वपूर्ण अंतर सूचीबद्ध हैं।

0

आपको हमेशा याद रखना चाहिए कि प्रत्येक वर्ग, डेटा और इंटरफ़ेस में 2 भाग हैं।

आपके कोड ने ढेर पर 2 कुत्ते की वस्तुओं को वास्तव में बनाया है। जिसका मतलब है कि डेटा कुत्ते का है। यह ऑब्जेक्ट आकार के सभी डेटा सदस्यों कुत्ते + पशु + vtable सूचक का योग है।

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

जानवर में पशु का इंटरफ़ेस ग्रहण करने के लिए चित्र को वापस लेना, एक सामान्य चाल() को ले जाना और वह कुत्ता वास्तव में एक कुत्ते :: मूव() {कुत्ते की तरह} के साथ अतिरंजित है।

भले ही आपके पास एनी ए * था और आपने एक-> मूव() को vtable के लिए धन्यवाद दिया था, तो आप वास्तव में() {कुत्ते की तरह) ले जाएंगे। ऐसा इसलिए होता है क्योंकि पशु :: मूव() एक (आभासी) फ़ंक्शन पॉइंटर कुत्ता() के दौरान कुत्ते के :: मूव() को फिर से इंगित करता था।

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