with - types of polymorphism in c++




व्युत्पन्न वर्ग में एक ओवरराइड फ़ंक्शन बेस क्लास के अन्य ओवरलोड को क्यों छिपाता है? (3)

कोड पर विचार करें:

#include <stdio.h>

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){
    Derived obj;
    obj.gogo(7);
}

यह त्रुटि मिली:

>g++ -pedantic -Os test.cpp -o test
test.cpp: In function `int main()':
test.cpp:31: error: no matching function for call to `Derived::gogo(int)'
test.cpp:21: note: candidates are: virtual void Derived::gogo(int*) 
test.cpp:33:2: warning: no newline at end of file
>Exit code: 1

यहां, व्युत्पन्न वर्ग का कार्य बेस क्लास में समान नाम (हस्ताक्षर नहीं) के सभी कार्यों को ग्रहण कर रहा है। किसी भी तरह, सी ++ का यह व्यवहार ठीक नहीं दिखता है। Polymorphic नहीं है।


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

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

उदाहरण के लिए, मान लीजिए कि बेस क्लास B में एक सदस्य फ़ंक्शन foo जो टाइप void * का पैरामीटर लेता है, और foo(NULL) सभी कॉल B::foo(void *) को हल किए जाते हैं। आइए मान लें कि कोई नाम छुपा नहीं है और यह B::foo(void *) B से उतरने वाली कई अलग-अलग कक्षाओं में दिखाई दे रहा है। हालांकि, मान लें कि कक्षा B कुछ [अप्रत्यक्ष, रिमोट] वंशज D में एक फ़ंक्शन foo(int) परिभाषित किया गया है। अब, नाम छिपाने के बिना D में foo(void *) और foo(int) दिखाई देते हैं और ओवरलोड रिज़ॉल्यूशन में भाग लेते हैं। प्रकार D किसी ऑब्जेक्ट के माध्यम से किए गए foo(NULL) को कॉल करने के लिए कौन सा फ़ंक्शन होगा? वे D::foo(int) को हल करेंगे, क्योंकि int किसी भी सूचक प्रकार की तुलना में अभिन्न शून्य (यानी NULL ) के लिए एक बेहतर मिलान है। इसलिए, पूरे पदानुक्रम में foo(NULL) को एक समारोह में हल करने के लिए बुलाया जाता है, जबकि D (और नीचे) में वे अचानक दूसरे को हल करते हैं।

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

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


नाम छिपाने का अर्थ समझ में आता है क्योंकि यह नाम संकल्प में अस्पष्टता को रोकता है।

इस कोड पर विचार करें:

class Base
{
public:
    void func (float x) { ... }
}

class Derived: public Base
{
public:
    void func (double x) { ... }
}

Derived dobj;

यदि Base::func(float) Derived::func(double) द्वारा छुपाया नहीं गया था, तो हम dobj.func(0.f) को कॉल करते समय बेस क्लास फ़ंक्शन को कॉल dobj.func(0.f) , भले ही एक फ्लोट को dobj.func(0.f) जा सके ।

संदर्भ: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/


यह "डिजाइन द्वारा" है। इस प्रकार की विधि के लिए सी ++ ओवरलोड रिज़ॉल्यूशन में निम्न की तरह काम करता है।

  • संदर्भ के प्रकार से शुरू करना और फिर बेस प्रकार पर जाना, पहला प्रकार ढूंढें जिसमें "gogo" नामक विधि है
  • उस प्रकार पर "gogo" नामक विधियों को ध्यान में रखते हुए एक मिलान अधिभार मिलता है

चूंकि व्युत्पन्न में "gogo" नामक मिलान करने वाला फ़ंक्शन नहीं है, इसलिए ओवरलोड रिज़ॉल्यूशन विफल हो जाता है।







override