C++ में Virtual Functions: इनके बिना काम क्यों नहीं चलता?
\n\nसोचिए कि आप एक फिल्म डायरेक्टर हैं जो जानवरों पर फिल्म बना रहे हैं। आपकी स्क्रिप्ट है: "सभी जानवर आवाज़ निकालते हैं"। आप कुत्ते का सीन शूट करते हैं - वह भौंकता है। बिल्ली का सीन - वह म्याऊं करती है। सब लॉजिकल है। अब सोचिए कि आपका असिस्टेंट (कंपाइलर) आपके लिए खिलौना जानवरों का एक बॉक्स लाता है और कहता है: "शूट करो!"। आप बटन दबाते हैं, और खिलौना... चुप रहता है। क्यों? क्योंकि आप virtual functions के बारे में भूल गए।
\n\nC++ प्रोग्रामिंग में यह समस्या अक्सर आती है। आप एक "Animal" क्लास बनाते हैं, उसमें sound() मेथड होता है। फिर आप "Dog" और "Cat" क्लास बनाते हैं जो "Animal" से इनहेरिट करते हैं। सब कुछ ठीक लगता है, जब तक आप इन ऑब्जेक्ट्स को बेस क्लास के पॉइंटर के ज़रिए एक्सेस नहीं करते। तब पता चलता है कि C++ डिफ़ॉल्ट रूप से बेस क्लास का मेथड कॉल करता है, न कि डिराइव्ड क्लास में ओवरराइड किए गए मेथड को। Virtual functions इस समस्या को हमेशा के लिए हल कर देते हैं।
इस आर्टिकल में हम विस्तार से जानेंगे कि virtual functions क्या हैं, इनकी ज़रूरत क्यों है, ये अंदरूनी तौर पर कैसे काम करते हैं, और आधुनिक C++ में इनके बिना काम क्यों नहीं चल सकता।
\n\nसरल शब्दों में Virtual Function क्या है?
\n\nVirtual function एक क्लास का मेथड है जिसे आप virtual कीवर्ड से मार्क करते हैं। ऐसा फंक्शन डिराइव्ड क्लासेज़ में ओवरराइड किया जा सकता है, और जब इसे बेस क्लास के पॉइंटर या रेफरेंस के ज़रिए कॉल किया जाता है, तो डिराइव्ड क्लास का वर्जन ही कॉल होगा।
मुश्किल लग रहा है? चलिए एक जीवन उदाहरण से समझते हैं। रोबोट के रिमोट कंट्रोल की कल्पना करें। आपके पास एक बेस क्लास "Robot" है जिसमें doWork() मेथड है। एक रोबोट वैक्यूम क्लीनर है और दूसरा रोबोट कूरियर। जब आप "काम करो" बटन दबाते हैं, तो संबंधित एल्गोरिदम चलने चाहिए: वैक्यूम सफाई शुरू करे, कूरियर पार्सल डिलीवर करे। Virtual functions के बिना, आप बटन दबाते और रोबोट वहीं खड़ा रहता (बेस क्लास का मेथड कॉल होता)।
C++ में virtual functions polymorphism की नींव हैं। Polymorphism आपको अलग-अलग टाइप के ऑब्जेक्ट्स के साथ एक समान तरीके से काम करने की अनुमति देता है, अगर वे एक ही पूर्वज से इनहेरिट करते हैं।
\n\nVirtual Functions के बिना समस्या: एक स्पष्ट उदाहरण
\n\nआइए देखें कि virtual का उपयोग न करने पर क्या होता है।
#include <iostream>\n#include <vector>\n\nclass Animal {\npublic:\n void sound() {\n std::cout << "Some generic animal sound" << std::endl;\n }\n};\n\nclass Dog : public Animal {\npublic:\n void sound() {\n std::cout << "Woof! Woof!" << std::endl;\n }\n};\n\nclass Cat : public Animal {\npublic:\n void sound() {\n std::cout << "Meow!" << std::endl;\n }\n};\n\nint main() {\n std::vector<Animal*> animals;\n animals.push_back(new Dog());\n animals.push_back(new Cat());\n\n for (Animal* a : animals) {\n a->sound(); // क्या प्रिंट होगा?\n }\n\n // मेमोरी साफ करना\n for (Animal* a : animals) delete a;\n\n return 0;\n}\n\n\nइस कोड को चलाएँ। आप देखेंगे:
\nSome generic animal sound\nSome generic animal sound\n\n\nक्यों? क्योंकि कंपाइलर पॉइंटर के टाइप (Animal*) को देखता है, न कि असली ऑब्जेक्ट को। वह बेस क्लास का मेथड कॉल करता है। इसे early binding कहते हैं - कॉल करने का फैसला कंपाइलेशन के समय हो जाता है।
हमें चाहिए late binding - ताकि फैसला रनटाइम पर ऑब्जेक्ट के टाइप के आधार पर हो। यही virtual functions देते हैं।
\n\nसमाधान: virtual जोड़ना
\n\nचलिए उदाहरण को सही करते हैं, virtual कीवर्ड जोड़कर।
class Animal {\npublic:\n virtual void sound() { // virtual जोड़ा\n std::cout << "Some generic animal sound" <<