विषय पर बढ़ें

अंतिम अद्यतन:2026-10-13

C++ वर्चुअल फ़ंक्शन

लेखक के बारे में:

व्यक्तिगत सार्वजनिक अकाउंट (WeChat):

C++ वर्चुअल फ़ंक्शन | C++那些事

1. वर्चुअल फ़ंक्शन और रनटाइम पॉलीमॉर्फ़िज्म

संगत कोड: emp.cpp

वर्चुअल फ़ंक्शन का कॉल पॉइंटर या रेफ़रेंस द्वारा इंगित ऑब्जेक्ट के प्रकार पर निर्भर करता है, न कि पॉइंटर या रेफ़रेंस के अपने प्रकार पर।

2. vptr और vtable

देखें vptr और vtable

3. वर्चुअल फ़ंक्शन में डिफ़ॉल्ट पैरामीटर

संगत कोड: default_arg.cpp

डिफ़ॉल्ट पैरामीटर स्टैटिकली बाइंड होते हैं, जबकि वर्चुअल फ़ंक्शन डायनामिकली बाइंड होते हैं। कौन-सा डिफ़ॉल्ट पैरामीटर उपयोग होगा, यह पॉइंटर या रेफ़रेंस के अपने प्रकार पर निर्भर करता है, न कि ऑब्जेक्ट के प्रकार पर।

4. क्या यह संभव है

(1) क्या स्टैटिक फ़ंक्शन को वर्चुअल घोषित किया जा सकता है?

इसके पीछे मुख्य रूप से दो कारण हैं:

स्टैटिक फ़ंक्शन को वर्चुअल घोषित नहीं किया जा सकता, और न ही इसे const और volatile कीवर्ड से क्वालिफ़ाई किया जा सकता है

static मेंबर फ़ंक्शन किसी भी क्लास ऑब्जेक्ट या क्लास इंस्टेंस से संबंधित नहीं होता, इसलिए इस फ़ंक्शन में virtual जोड़ने का कोई अर्थ नहीं है।

वर्चुअल फ़ंक्शन vptr और vtable पर निर्भर करते हैं। vptr एक पॉइंटर है जो क्लास के कंस्ट्रक्टर में बनाया जाता है, और इसे केवल this पॉइंटर के माध्यम से ही एक्सेस किया जा सकता है; स्टैटिक मेंबर फ़ंक्शन में this पॉइंटर नहीं होता, इसलिए वे vptr तक नहीं पहुँच सकते।

कोड अध्ययन हेतु: static_error.cpp

(2) क्या कंस्ट्रक्टर वर्चुअल फ़ंक्शन हो सकता है?

कंस्ट्रक्टर को वर्चुअल घोषित नहीं किया जा सकता। साथ ही, inline और explicit को छोड़कर कंस्ट्रक्टर में किसी अन्य कीवर्ड का उपयोग की अनुमति नहीं है।

कंस्ट्रक्टर वर्चुअल क्यों नहीं हो सकता?

हालाँकि वर्चुअल फ़ंक्शन तालिका vtable कंपाइल चरण में ही बन जाती है, लेकिन vtable की ओर इशारा करने वाला पॉइंटर vptr रनटाइम पर ऑब्जेक्ट के इंस्टेंटिएट होने पर ही उत्पन्न होता है। यदि क्लास में वर्चुअल फ़ंक्शन होते हैं, तो कंपाइलर कंस्ट्रक्टर में vptr बनाने वाला कोड जोड़ता है। यहीं समस्या आती है: यदि कंस्ट्रक्टर वर्चुअल होता, तो उसे vtable तक पहुँचने के लिए vptr की आवश्यकता होती, लेकिन उस समय vptr अभी उत्पन्न नहीं हुआ होता। इसलिए कंस्ट्रक्टर वर्चुअल फ़ंक्शन नहीं हो सकता।

हम वर्चुअल फ़ंक्शन इसलिए उपयोग करते हैं क्योंकि हमें अपूर्ण जानकारी की स्थिति में पॉलीमॉर्फ़िक संचालन की आवश्यकता होती है। जबकि कंस्ट्रक्टर इंस्टेंस को इनिशियलाइज़ करने के लिए होता है, और इंस्टेंस का प्रकार निश्चित होना चाहिए। इसलिए कंस्ट्रक्टर को वर्चुअल घोषित करने की कोई आवश्यकता नहीं है।

कोड अध्ययन हेतु:

(3) क्या डिस्ट्रक्टर वर्चुअल फ़ंक्शन हो सकता है?

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

कोड अध्ययन हेतु:

(4) क्या वर्चुअल फ़ंक्शन प्राइवेट फ़ंक्शन हो सकता है?

  • जब बेस क्लास का पॉइंटर डिराइव्ड क्लास के ऑब्जेक्ट की ओर इशारा करता है, तो डिराइव्ड क्लास ऑब्जेक्ट का फ़ंक्शन कॉल होता है;
  • int main() को Base क्लास का फ्रेंड घोषित किया जाना चाहिए, अन्यथा कंपाइल विफल हो जाता है। कंपाइलर त्रुटि देता है: ptr प्राइवेट फ़ंक्शन तक नहीं पहुँच सकता। बेशक, यदि बेस क्लास को public और डिराइव्ड क्लास को private घोषित कर दिया जाए, तो यह समस्या ही नहीं रहती।

कोड अध्ययन हेतु:

(5) क्या वर्चुअल फ़ंक्शन इनलाइन हो सकता है?

सामान्यतः कंपाइलर क्लास के मेंबर फ़ंक्शंस पर विचार करता है कि उन्हें इनलाइन किया जाए या नहीं। लेकिन बेस क्लास पॉइंटर या रेफ़रेंस के माध्यम से कॉल किया गया वर्चुअल फ़ंक्शन निश्चित रूप से इनलाइन नहीं किया जा सकता। बेशक, वास्तविक ऑब्जेक्ट द्वारा वर्चुअल फ़ंक्शन को कॉल करने पर या स्टैटिक कॉल पर उसे इनलाइन किया जा सकता है, और वर्चुअल डिस्ट्रक्टर का स्टैटिक कॉल अवश्य ही इनलाइन एक्सपैंड होगा।

  • वर्चुअल फ़ंक्शन इनलाइन फ़ंक्शन हो सकता है; inline वर्चुअल फ़ंक्शन को क्वालिफ़ाई कर सकता है, लेकिन जब वर्चुअल फ़ंक्शन पॉलीमॉर्फ़िज्म दर्शाता है तो वह इनलाइन नहीं हो सकता।
  • इनलाइन करना कंपाइलर को कंपाइल समय पर दिया गया सुझाव है, जबकि वर्चुअल फ़ंक्शन का पॉलीमॉर्फ़िज्म रनटाइम का गुण है; कंपाइलर यह नहीं जान सकता कि रनटाइम में कौन-सा कोड कॉल होगा, इसलिए जब वर्चुअल फ़ंक्शन पॉलीमॉर्फ़िक व्यवहार करता है (रनटाइम में) तो वह इनलाइन नहीं हो सकता।
  • inline virtual के इनलाइन होने का एकमात्र अवसर तब है जब कंपाइलर जानता हो कि जिस ऑब्जेक्ट पर कॉल हो रहा है वह किस क्लास का है (जैसे Base::who()); ऐसा केवल तभी होता है जब कंपाइलर के पास ऑब्जेक्ट का पॉइंटर या रेफ़रेंस नहीं बल्कि वास्तविक ऑब्जेक्ट हो।

कोड अध्ययन हेतु:

5. RTTI और dynamic_cast

RTTI (Run-Time Type Identification, रनटाइम टाइप पहचान) के द्वारा प्रोग्राम बेस क्लास के पॉइंटर या रेफ़रेंस का उपयोग करके यह जाँच सकता है कि उन पॉइंटरों या रेफ़रेंसों द्वारा इंगित ऑब्जेक्ट का वास्तविक डिराइव्ड क्लास प्रकार क्या है।

ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग में, कभी-कभी हमें रनटाइम में यह पूछने की आवश्यकता होती है कि कोई ऑब्जेक्ट किसी पॉलीमॉर्फ़िक प्रकार के रूप में उपयोग किया जा सकता है या नहीं। Java के instanceof तथा C# के as और is ऑपरेटरों के अनुरूप, C++ डायनामिक कास्ट के लिए dynamic_cast प्रदान करता है। C-शैली के फ़ोर्स्ड कास्ट और C++ के reinterpret_cast की तुलना में dynamic_cast टाइप-सुरक्षा जाँच प्रदान करता है और क्षमता-प्रश्न (Capability Query) पर आधारित कास्ट है; इसीलिए पॉलीमॉर्फ़िक प्रकारों के बीच रूपांतरण के लिए dynamic_cast का उपयोग करना बेहतर माना जाता है।

कोड अध्ययन हेतु:

6. शुद्ध वर्चुअल फ़ंक्शन और एब्स्ट्रैक्ट क्लास

देखें शुद्ध वर्चुअल फ़ंक्शन और एब्स्ट्रैक्ट क्लास

संबंधित विषय

टिप्पणियाँ