C++ में वर्चुअल डिस्ट्रक्टर की आवश्यकता कब होती है: 2025 का विश्लेषण

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

C++ में वर्चुअल डिस्ट्रक्टर: यह अब भी प्रासंगिक क्यों है (और कब इसे भूल सकते हैं)

\n\n

यदि आपने कभी C++ में कोड लिखा है और इनहेरिटेंस का सामना किया है, तो आपने निश्चित रूप से Stack Overflow पर यह प्रश्न देखा होगा जिसके दसियों हज़ार व्यूज़ हैं: «When to use virtual destructors?»। 2025 में यह विषय अपनी तीव्रता नहीं खोया है — virtual छूट जाने के कारण मेमोरी लीक प्रोडक्शन में अभी भी समस्याएँ पैदा कर रही हैं। लेकिन इसका दूसरा पहलू भी है: जहाँ संभव हो, हर जगह बिना सोचे-समझे virtual जोड़ना भी सबसे अच्छी प्रथा नहीं है।

\n\n

आइए एक बार और हमेशा के लिए समझ लें: वर्चुअल डिस्ट्रक्टर कब अनिवार्य है, कब यह केवल एक अच्छा अभ्यास है, और कब इसकी उपस्थिति खराब आर्किटेक्चर का संकेत है। चलिए शुरू करते हैं।

\n\n

संक्षिप्त उत्तर (उन लोगों के लिए जो जल्दी में हैं)

\n\n

वर्चुअल डिस्ट्रक्टर की आवश्यकता तब और केवल तब होती है जब आप बेस क्लास पॉइंटर के माध्यम से डिराइव्ड क्लास के ऑब्जेक्ट को डिलीट करने जा रहे हों। यदि कोड में ऐसा कोई परिदृश्य नहीं है, तो virtual की आवश्यकता नहीं है। लेकिन कुछ बारीकियाँ हैं जिन्हें हम नीचे समझाएँगे।

\n\n

यह समस्या क्यों है? लीक की शारीरिक रचना

\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

आउटपुट:

\n
Derived created\nBase destructor
\n\n

एरे big_data लीक हो गया। Derived का डिस्ट्रक्टर कॉल नहीं हुआ, क्योंकि कंपाइलर, Base* टाइप देखकर, उसी क्लास का डिस्ट्रक्टर कॉल करता है। और वह वर्चुअल नहीं है।

\n\n

समाधान — Base के डिस्ट्रक्टर से पहले virtual जोड़ें:

\n\n
class Base {\npublic:\n    virtual ~Base() = default;  // या खाली बॉडी\n};
\n\n

अब delete ptr पर पहले ~Derived() निष्पादित होगा, फिर ~Base()। संसाधन मुक्त हो जाते हैं, मेमोरी लीक नहीं होती, स्टैटिक एनालाइज़र संतुष्ट है।

\n\n

वर्चुअल डिस्ट्रक्टर कब अनिवार्य है (लोहे का नियम)

\n\n

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

\n\n

व्यवहार में इसका अर्थ है:

\n
    \n
  • क्लास में कम से कम एक वर्चुअल फंक्शन (शुद्ध या नहीं) है
  • \n
  • आप Derived के साथ काम करने के लिए Base* या Base& का उपयोग करने की योजना बना रहे हैं
  • \n
  • ऑब्जेक्ट हीप (new) पर बनाए जाते हैं और delete के माध्यम से हटाए जाते हैं
  • \n
\n\n

उदाहरण — आकृतियों का क्लासिक पदानुक्रम:

\n\n
class 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 कॉल करता है —

ब्लॉग

पुस्तक अनुशंसाएँ