C++ में वर्चुअल डिस्ट्रक्टर: यह अब भी प्रासंगिक क्यों है (और कब इसे भूल सकते हैं)
\n\nयदि आपने कभी C++ में कोड लिखा है और इनहेरिटेंस का सामना किया है, तो आपने निश्चित रूप से Stack Overflow पर यह प्रश्न देखा होगा जिसके दसियों हज़ार व्यूज़ हैं: «When to use virtual destructors?»। 2025 में यह विषय अपनी तीव्रता नहीं खोया है — virtual छूट जाने के कारण मेमोरी लीक प्रोडक्शन में अभी भी समस्याएँ पैदा कर रही हैं। लेकिन इसका दूसरा पहलू भी है: जहाँ संभव हो, हर जगह बिना सोचे-समझे virtual जोड़ना भी सबसे अच्छी प्रथा नहीं है।
आइए एक बार और हमेशा के लिए समझ लें: वर्चुअल डिस्ट्रक्टर कब अनिवार्य है, कब यह केवल एक अच्छा अभ्यास है, और कब इसकी उपस्थिति खराब आर्किटेक्चर का संकेत है। चलिए शुरू करते हैं।
\n\nसंक्षिप्त उत्तर (उन लोगों के लिए जो जल्दी में हैं)
\n\nवर्चुअल डिस्ट्रक्टर की आवश्यकता तब और केवल तब होती है जब आप बेस क्लास पॉइंटर के माध्यम से डिराइव्ड क्लास के ऑब्जेक्ट को डिलीट करने जा रहे हों। यदि कोड में ऐसा कोई परिदृश्य नहीं है, तो virtual की आवश्यकता नहीं है। लेकिन कुछ बारीकियाँ हैं जिन्हें हम नीचे समझाएँगे।
यह समस्या क्यों है? लीक की शारीरिक रचना
\n\nआइए एक क्लासिक एंटी-पैटर्न देखें जो शुरुआती (और न केवल) के कोड को तोड़ देता है।
\n\n#include <iostream>\n\nclass Base {\npublic:\n ~Base() { \n std::cout << "Base destructor\\n"; \n }\n};\n\nclass Derived : public Base {\nprivate:\n int* big_data = new int[1000]; // संसाधन जिन्हें मुक्त करने की आवश्यकता है\npublic:\n Derived() { std::cout << "Derived created\\n"; }\n ~Derived() {\n delete[] big_data;\n std::cout << "Derived destructor\\n";\n }\n};\n\nint main() {\n Base* ptr = new Derived();\n delete ptr; // उफ़! केवल ~Base() कॉल होगा\n return 0;\n}\n\nआउटपुट:
\nDerived created\nBase destructor\n\nएरे big_data लीक हो गया। Derived का डिस्ट्रक्टर कॉल नहीं हुआ, क्योंकि कंपाइलर, Base* टाइप देखकर, उसी क्लास का डिस्ट्रक्टर कॉल करता है। और वह वर्चुअल नहीं है।
समाधान — Base के डिस्ट्रक्टर से पहले virtual जोड़ें:
class Base {\npublic:\n virtual ~Base() = default; // या खाली बॉडी\n};\n\nअब delete ptr पर पहले ~Derived() निष्पादित होगा, फिर ~Base()। संसाधन मुक्त हो जाते हैं, मेमोरी लीक नहीं होती, स्टैटिक एनालाइज़र संतुष्ट है।
वर्चुअल डिस्ट्रक्टर कब अनिवार्य है (लोहे का नियम)
\n\nकेवल एक बिना शर्त मामला है: यदि क्लास को बेस क्लास पॉइंटर या रेफरेंस के माध्यम से पॉलीमॉर्फिक उपयोग के लिए डिज़ाइन किया गया है, और डिराइव्ड क्लास के ऑब्जेक्ट इस पॉइंटर के माध्यम से डिलीट किए जाएँगे।
\n\nव्यवहार में इसका अर्थ है:
\n- \n
- क्लास में कम से कम एक वर्चुअल फंक्शन (शुद्ध या नहीं) है \n
- आप
Derivedके साथ काम करने के लिएBase*याBase&का उपयोग करने की योजना बना रहे हैं \n - ऑब्जेक्ट हीप (
new) पर बनाए जाते हैं औरdeleteके माध्यम से हटाए जाते हैं \n
उदाहरण — आकृतियों का क्लासिक पदानुक्रम:
\n\nclass Shape {\npublic:\n virtual double area() const = 0;\n virtual ~Shape() = default; // एब्सट्रैक्ट क्लास के लिए अनिवार्य\n};\n\nclass Circle : public Shape {\n double radius;\npublic:\n explicit Circle(double r) : radius(r) {}\n double area() const override { return 3.14159 * radius * radius; }\n};\n\nint main() {\n std::vector<std::unique_ptr<Shape>> shapes;\n shapes.push_back(std::make_unique<Circle>(5.0));\n // unique_ptr स्वयं delete का ध्यान रखेगा, लेकिन Shape का डिस्ट्रक्टर virtual होना चाहिए\n return 0;\n}\n\nध्यान दें: भले ही आप std::unique_ptr का उपयोग कर रहे हों (जो 2025 में अनुशंसित दृष्टिकोण है), फिर भी वर्चुअल डिस्ट्रक्टर आवश्यक है। unique_ptr बेस क्लास पॉइंटर के माध्यम से delete कॉल करता है —