2016-06-19 6 views
5

मेरे पास एक परिणाम है कि मुझे एकाधिक विरासत, virtual आधार वर्गों के लिए विधियों और पॉइंटर्स से उम्मीद नहीं थी।आधार विरासत से वर्चुअल विधियों टकराव और पॉइंटर्स


d.getStr() साथ

, जब d एक derived उदाहरण है, base_2 संस्करण, कहा जाता है के रूप में मैं उम्मीद थी।

p->getStr(), जब p एक derived उदाहरण (या एक derived उदाहरण की ओर इशारा करते base_2 के लिए सूचक) के लिए सूचक है के साथ, base_2 संस्करण, कहा जाता है के रूप में मैं उम्मीद थी।

लेकिन p->getStr(), जब p एक base_1 एक derived उदाहरण की ओर इशारा करते के लिए सूचक है के साथ, base_1 संस्करण कहा जाता है और मैं आश्वस्त था base_2 संस्करण कहा जा सकता है (धन्यवाद using और तथ्य यह है कि getStr()virtual तरीके हैं) ।

निम्नलिखित एक सरल उदाहरण है:

#include <iostream> 

struct base_1 
{ 
    virtual std::string getStr() const 
    { return "string from base 1"; } 
}; 

struct base_2 
{ 
    virtual std::string getStr() const 
    { return "string from base 2"; } 
}; 

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 


int main() 
{ 
    derived d; 

    derived * dp = &d; 
    base_1 * bp1 = &d; 
    base_2 * bp2 = &d; 

    std::cout << "from derived:   " << d.getStr() << std::endl; 
    std::cout << "from derived pointer: " << dp->getStr() << std::endl; 
    std::cout << "from base_1 pointer: " << bp1->getStr() << std::endl; 
    std::cout << "from base_2 pointer: " << bp2->getStr() << std::endl; 
} 

उत्पादन होता है निम्नलिखित

from derived:   string from base 2 
from derived pointer: string from base 2 
from base_1 pointer: string from base 1 
from base_2 pointer: string from base 2 

मुझे पता है कि, base_2 संस्करण की कॉल लागू करने के लिए, मैं derived निम्न विधि में जोड़ सकते हैं

std::string getStr() const 
{ return base_2::getStr(); } 

लेकिन मेरे प्रश्न हैं:

1) क्यों base_1 (एक व्युत्पन्न उदाहरण की ओर इशारा करते) सूचक using निर्देश की अनदेखी और getStr() की base_1 संस्करण फोन करता है?

2) वहाँ getStr() की base_2 संस्करण लागू करने के लिए एक रास्ता है, जब derived उदाहरण getStr() को पुनर्परिभाषित करने के बिना, एक base_1 सूचक द्वारा किया जाता है?

--- संपादित करें ---

जवाब के लिए धन्यवाद।

मैं समझता हूं कि आप क्या कर रहे हैं इसका वर्णन कर रहे हैं लेकिन मेरा संदेह है: क्या भाषा (मानक) इस पहलू का वर्णन करती है? या यह एक अनिर्धारित हिस्सा है?

मेरा मतलब है: अगर मैं using निर्देश निकालने के लिए, मैं, d.getStr() से और dp->getStr() से एक संकलन त्रुटि (error: request for member getStr is ambiguous), मिलता है क्योंकि संकलक पता नहीं है getStr() का कौन सा संस्करण चुना है।

लेकिन getStr()virtual विधियां हैं। तो (मुझे विश्वास था कि) आधार सूचक को उनके व्युत्पन्न संस्करण का उपयोग करना चाहिए। लेकिन हमारे पास कुछ टकराव विधियां हैं।

देखने की भाषा (मानक) की दृष्टि से, एक base_1 (या base_2) सूचक के लिए अधिकृत है (या बाध्य) अन्य अनदेखी टकराने के तरीकों के दो संस्करणों में से एक का चयन करने के?

शायद मैं गलत हूं लेकिन मुझे लगता है कि, इस तरह, virtual विधियों को गैर virtual विधियों के रूप में प्रबंधित किया जाता है।

+1

'उपयोग' केवल दृश्यता में मदद करता है। यह आधार वर्चुअल फ़ंक्शन को व्युत्पन्न 'ओवरलोड' नहीं बनाता है या ऐसा दिखाई देता है। – Arunmu

उत्तर

0

1) ऐसा लगता है कि आपका कोड ठीक वही कर रहा है जो इसे करना है। आप base_1 को इंगित करते हैं, इसलिए आपको base_1 (या इसके किसी भी बेस क्लास) से फ़ंक्शन मिलते हैं। यह ऑब्जेक्ट बेस_1 और बेस_2 दोनों से बना है, उस बिंदु पर अज्ञात है, क्योंकि आप बेस क्लास को इंगित करते हैं, व्युत्पन्न कक्षा नहीं।

2) नहीं, यह बस असंभव है। आपको derived में getStr() को वास्तव में अधिभारित करना होगा।

+0

उत्तरदाता के लिए धन्यवाद लेकिन, बिंदु (1) के बारे में, आप सामान्य तरीकों के साथ आधार/व्युत्पन्न कक्षाओं के व्यवहार का वर्णन कर रहे हैं; 'getStr()' 'वर्चुअल 'विधियां हैं – max66

+0

इससे कोई फर्क नहीं पड़ता; तथ्य यह है कि वर्चुअल फ़ंक्शंस कक्षाओं से "कूद" सकते हैं जो कि तरफ से हैं, केवल क्लासिक-रिलेशन आरेख खींचते ही लंबवत हैं। – JvO

4

आप उम्मीद कर रहे हैं कि जब आप निम्नलिखित तरीके से using कीवर्ड का उपयोग:

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 

यह एक ही है कि के रूप में:

struct derived : public base_1, public base_2 
{ 
    void getStr() 
    { 
     base_2::getStr(); 
    } 
}; 

इस मामले में, व्यवहार आप उम्मीद कर रहे हैं - - p->getStr() का आह्वान करते हुए, जब पी base_1 पर सूचक होता है - वास्तव में, base_2::getStr() का आह्वान करना समाप्त कर देगा। derived ओवरराइड base_1 के getStr(), इसलिए लागू base_1 के getStr(), एक साधारण सूचक के माध्यम से, derived के getStr में परिणाम() लागू हो रही है, जो invokes base_2getStr() विधि।

हालांकि, ऐसा नहीं होता है। using कीवर्ड इस तरीके से एक विधि कॉल अग्रेषित करने के लिए उपनाम नहीं है। using कीवर्ड derived 'डी कक्षा में कोई विधि नहीं बनाता है, इसलिए वर्ग विरासत प्रभावित नहीं होती है, और derived_1 का getStr() उपclas में ओवरराइड नहीं होता है। और यही कारण है कि derived_1 का getStr() का आह्वान करना derived_2 के getStr() पर आक्रमण नहीं करता है।

2

ऐसा इसलिए होता है क्योंकि व्युत्पन्न वर्ग में प्रत्येक बेस क्लास के लिए getStr() के लिए 2 vtable प्रविष्टियां होती हैं, इसलिए यह base_1::getStr() और base_2::getStr() दोनों को सही ढंग से हल कर सकती है। using निर्देश derived::getStr() vtable प्रविष्टि नहीं बनाता है या बेस-क्लास वाले लोगों को प्रतिस्थापित नहीं करता है, यह केवल चुनता है कि कौन सी बेस-क्लास प्रविष्टि का उपयोग किया जाएगा। base_1 पर पॉइंटर के माध्यम से जाने पर, कंपाइलर केवल derived और base_1 से आभासी कार्यों के लिए vtable प्रविष्टियों को "देखता है" तो यह getStr() से base_1::getStr() पर हल करता है। derived में एक स्पष्ट getStr() जोड़ने का आपका समाधान शायद सबसे स्पष्ट है, हालांकि इसे स्पष्टता के लिए आधार वर्गों से मेल खाने के लिए आभासी बनाने की सलाह दी जा सकती है।

+0

उत्तर के लिए धन्यवाद; मेरा संदेह है: व्युत्पन्न वर्ग में 2 vtables सी ++ मानक द्वारा लगाए गए हैं या कार्यान्वयन विस्तार हैं?दूसरे शब्दों में: ऐसा इसलिए होता है क्योंकि यह मानक द्वारा लगाया गया है या अन्य कंपाइलर्स एक अलग व्यवहार उत्पन्न करने के लिए अधिकृत हैं? – max66

+0

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

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