क्या एक विरासत वाले वर्ग के लिए वर्चुअल फ़ंक्शन को एक अलग रिटर्न प्रकार (रिटर्न के रूप में टेम्पलेट का उपयोग नहीं करना) लागू करना संभव है?
जवाबों:
कुछ मामलों में, हाँ, यह एक व्युत्पन्न वर्ग के लिए एक अलग रिटर्न प्रकार का उपयोग करके वर्चुअल फ़ंक्शन को ओवरराइड करने के लिए कानूनी है जब तक कि रिटर्न प्रकार मूल रिटर्न प्रकार के साथ सहसंयोजक होता है। उदाहरण के लिए, निम्नलिखित पर विचार करें:
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*
और ऑपरेशन अच्छी तरह से परिभाषित है।
आम तौर पर, एक फ़ंक्शन का रिटर्न प्रकार कभी भी उसके हस्ताक्षर का हिस्सा नहीं माना जाता है। जब तक वापसी प्रकार सहसंयोजक है तब तक आप किसी भी प्रकार के रिटर्न के साथ एक सदस्य फ़ंक्शन को ओवरराइड कर सकते हैं।
Base*
के साथ long
और Derived*
साथ int
(या दूसरी तरह के आसपास, फर्क नहीं पड़ता)। यह काम नहीं करेगा।
हाँ। जब तक वे सहसंयोजक हैं, तब तक रिटर्न प्रकार भिन्न होने की अनुमति है । 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 ++ इसे एक नया फ़ंक्शन मानता है, इसलिए आप ओवरलोडिंग और छिपाना शुरू कर देते हैं। इस विषय पर अधिक जानकारी के लिए, विकिपीडिया में कोवरियनस और कंट्रावेरियन (कंप्यूटर विज्ञान) देखें ।
आभासी फ़ंक्शन के एक व्युत्पन्न वर्ग कार्यान्वयन में एक कोवरिएंट रिटर्न प्रकार हो सकता है ।