C ++ वर्चुअल फंक्शन रिटर्न टाइप


81

क्या एक विरासत वाले वर्ग के लिए वर्चुअल फ़ंक्शन को एक अलग रिटर्न प्रकार (रिटर्न के रूप में टेम्पलेट का उपयोग नहीं करना) लागू करना संभव है?

जवाबों:


86

कुछ मामलों में, हाँ, यह एक व्युत्पन्न वर्ग के लिए एक अलग रिटर्न प्रकार का उपयोग करके वर्चुअल फ़ंक्शन को ओवरराइड करने के लिए कानूनी है जब तक कि रिटर्न प्रकार मूल रिटर्न प्रकार के साथ सहसंयोजक होता है। उदाहरण के लिए, निम्नलिखित पर विचार करें:

class Base {
public:
    virtual ~Base() {}

    virtual Base* clone() const = 0;
};

class Derived: public Base {
public:
    virtual Derived* clone() const {
        return new Derived(*this);
    }
};

यहां, Baseएक शुद्ध आभासी फ़ंक्शन को परिभाषित करता है जिसे कहा जाता cloneहै एक रिटर्न Base *। व्युत्पन्न कार्यान्वयन में, यह वर्चुअल फ़ंक्शन रिटर्न प्रकार का उपयोग करके ओवरराइड किया जाता है Derived *। यद्यपि रिटर्न प्रकार आधार में समान नहीं है, यह पूरी तरह से सुरक्षित है क्योंकि किसी भी समय आप लिखेंगे

Base* ptr = /* ... */
Base* clone = ptr->clone();

कॉल करने के लिए कॉल clone()हमेशा किसी Baseऑब्जेक्ट को पॉइंटर लौटाता है Derived*, भले ही वह ए वापस आए , यह पॉइंटर अनुमानित रूप से एक के लिए परिवर्तनीय है Base*और ऑपरेशन अच्छी तरह से परिभाषित है।

आम तौर पर, एक फ़ंक्शन का रिटर्न प्रकार कभी भी उसके हस्ताक्षर का हिस्सा नहीं माना जाता है। जब तक वापसी प्रकार सहसंयोजक है तब तक आप किसी भी प्रकार के रिटर्न के साथ एक सदस्य फ़ंक्शन को ओवरराइड कर सकते हैं।


9
यह "आप किसी भी रिटर्न प्रकार के साथ एक सदस्य फ़ंक्शन को ओवरराइड कर सकते हैं" सही नहीं है। आप तब तक ओवरराइड कर सकते हैं जब तक कि रिटर्न प्रकार समान या सहसंयोजक (जिसे आपने समझाया), अवधि। यहां कोई अधिक सामान्य मामला नहीं है।
bronekk

1
@ bronekk- आपके द्वारा उद्धृत किए गए शेष वाक्य में कहा गया है कि नए रिटर्न प्रकार को मूल प्रकार कहीं भी उपयोग करने योग्य होना चाहिए; यह है, नए प्रकार मूल के साथ सहसंयोजक है।
टेम्प्लेटेटिडेफ

बाकी की सजा सही नहीं है; जगह की कल्पना Base*के साथ longऔर Derived*साथ int(या दूसरी तरह के आसपास, फर्क नहीं पड़ता)। यह काम नहीं करेगा।
ब्रोनकॉक

@ bronekk- आह हाँ, मैंने ऐसा नहीं सोचा था! यह बात बताने के लिए धन्यवाद।
templatetypedef

1
यह प्रकार प्रयोग करने योग्य है कहीं भी मूल प्रकार होगा और सहसंयोजक दो अलग-अलग संदर्भ हैं। सहसंयोजक का अर्थ है कि फ़ंक्शन को लागू करने वाले प्रकारों और लौटे प्रकारों के बीच संबंध उसी तरह से भिन्न होते हैं । Contravariant (हालांकि C ++ में उपयोगी नहीं) विपरीत संदर्भ है। कुछ भाषाएं डायनेमिक डिस्पैच में कंट्रावेरिएंट तर्कों के लिए अनुमति देती हैं (यदि आधार एक प्रकार के टी की वस्तु लेता है, तो व्युत्पन्न प्रकार एक टी 'ले सकता है जहां टी' टी का एक आधार है - आप एक पदानुक्रम के नीचे जाते हैं जिसे आप दूसरे तक ले जाते हैं ) है।
डेविड रॉड्रिग्ज - drieaseas

54

हाँ। जब तक वे सहसंयोजक हैं, तब तक रिटर्न प्रकार भिन्न होने की अनुमति है । C ++ मानक इसे इस तरह बताता है (++10.3 / 5):

ओवरराइडिंग फ़ंक्शन का रिटर्न प्रकार या तो ओवरराइड फ़ंक्शन के रिटर्न प्रकार के समान होगा या फ़ंक्शंस की कक्षाओं के साथ सहसंयोजक होगा । यदि कोई फ़ंक्शन फ़ंक्शन को D::fओवरराइड करता है B::f, तो निम्न मानदंड को संतुष्ट करने पर फ़ंक्शन का प्रकार लौकिक होता है:

  • दोनों कक्षाओं के लिए संकेत हैं या कक्षा 98 के संदर्भ हैं )
  • वापसी प्रकार में B::fवर्ग एक ही वर्ग है जो वापसी के प्रकार में वर्ग है D::fया, वापसी के प्रकार में कक्षा का एक प्रत्यक्ष या अप्रत्यक्ष आधार वर्ग है D::fऔर यह सुलभ हैD
  • दोनों बिंदुओं या संदर्भों में एक ही cv- योग्यता होती है और वापसी प्रकार में वर्ग प्रकार D::fके समान cv- योग्यता होती है या वापसी प्रकार में वर्ग प्रकार की तुलना में कम cv- योग्यता होती है B::f

फुटनोट 98 बताते हैं कि "कक्षाओं के लिए बहु-स्तरीय संकेत या वर्गों के लिए बहु-स्तरीय संकेत के संदर्भ की अनुमति नहीं है।"

संक्षेप में, यदि Dइसका उपप्रकार है B, तो फ़ंक्शन के प्रकार में वापसी प्रकार के फ़ंक्शन का Dउपप्रकार होना चाहिए B। सबसे आम उदाहरण है जब वापसी के प्रकार स्वयं पर आधारित होते हैं Dऔर B, लेकिन वे नहीं होते हैं। इस पर विचार करें, जहां हम दो अलग-अलग प्रकार के पदानुक्रम हैं:

struct Base { /* ... */ };
struct Derived: public Base { /* ... */ };

struct B {
  virtual Base* func() { return new Base; }
  virtual ~B() { }
};
struct D: public B {
  Derived* func() { return new Derived; }
};

int main() {
  B* b = new D;
  Base* base = b->func();
  delete base;
  delete b;
}

इसका कारण यह है क्योंकि कोई भी कॉल पॉइंटर की funcउम्मीद कर रहा है Base। कोई भी Baseपॉइंटर करेगा। इसलिए, अगर D::funcहमेशा एक Derivedपॉइंटर वापस करने का वादा किया जाता है, तो यह हमेशा पूर्वज वर्ग द्वारा निर्धारित अनुबंध को पूरा करेगा क्योंकि किसी भी Derivedपॉइंटर को स्पष्ट रूप से एक Baseपॉइंटर में परिवर्तित किया जा सकता है । इस प्रकार, कॉलर को हमेशा वही मिलेगा जो वे अपेक्षा करते हैं।


रिटर्न प्रकार को अलग-अलग करने की अनुमति देने के अलावा, कुछ भाषाएं ओवरराइडिंग फ़ंक्शन के पैरामीटर प्रकारों को भी भिन्न करने की अनुमति देती हैं। जब वे ऐसा करते हैं, तो उन्हें आमतौर पर विरोधाभासी होने की आवश्यकता होती है । यही है, अगर B::fएक स्वीकार करता है Derived*, तो D::fएक को स्वीकार करने की अनुमति दी जाएगी Base*। वंशज को वे जो स्वीकार करते हैं उसमें ढीले होने की अनुमति दी जाती है , और जो वे लौटते हैं उसमें कठोर होते हैं। C ++ पैरामीटर-प्रकार के कंट्रोवर्सी की अनुमति नहीं देता है। यदि आप पैरामीटर प्रकार बदलते हैं, तो C ++ इसे एक नया फ़ंक्शन मानता है, इसलिए आप ओवरलोडिंग और छिपाना शुरू कर देते हैं। इस विषय पर अधिक जानकारी के लिए, विकिपीडिया में कोवरियनस और कंट्रावेरियन (कंप्यूटर विज्ञान) देखें


2
क्या यह एक वास्तविक विशेषता है या रिज़ॉल्यूशन प्रकार के साइड इफेक्ट का उपयोग रिज़ॉल्यूशन में नहीं किया जा रहा है?
मार्टिन यॉर्क

1
@ मर्टिन, निश्चित रूप से एक सुविधा। मुझे पूरा यकीन है कि ओवरलोड रिज़ॉल्यूशन का इससे कोई लेना-देना नहीं था। यदि आप किसी फ़ंक्शन को ओवरराइड कर रहे हैं तो रिटर्न प्रकार का उपयोग किया जाता है।
रोब कैनेडी

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.