C++ में तीन का नियम: हर डेवलपर को क्या जानना चाहिए

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

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

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

C++ में तीन का नियम क्या है? उदाहरणों के साथ समझें

\n\n

यदि आपने कभी C++ में कोई ऐसा क्लास लिखा है जो डायनामिक मेमोरी को मैनेज करता है (जैसे, new या malloc का उपयोग करता है), तो आपने निश्चित रूप से तीन के नियम (Rule of Three) का सामना किया होगा। यह भाषा के सबसे मौलिक नियमों में से एक है, जो मेमोरी लीक, डबल डिलीट और अनिर्धारित व्यवहार से बचाता है। आइए इसे वास्तविक उदाहरणों के साथ समझें, ताकि आप केवल नियम को याद न करें, बल्कि यह समझें कि यह क्यों काम करता है।

\n\n

कल्पना करें: आपने एक क्लास लिखा है जो कंस्ट्रक्टर में मेमोरी आवंटित करता है। कंपाइलर आपके लिए कॉपी कंस्ट्रक्टर और असाइनमेंट ऑपरेटर तैयार करता है। लेकिन वह ऐसा सतही (shallow copy) तरीके से करता है। और यहीं से समस्याएँ शुरू होती हैं। तीन का नियम कहता है: यदि आपका क्लास स्पष्ट रूप से डिस्ट्रक्टर, कॉपी कंस्ट्रक्टर या असाइनमेंट ऑपरेटर को परिभाषित करता है, तो संभवतः आपको तीनों को परिभाषित करने की आवश्यकता है। क्यों? क्योंकि यह एक संकेत है कि आपका क्लास किसी संसाधन (मेमोरी, फ़ाइल, नेटवर्क कनेक्शन) का प्रबंधन कर रहा है, और कॉपी करना गहरा (deep) होना चाहिए।

\n\n

समस्या: सतही कॉपी (shallow copy)

\n

आइए एक विशिष्ट उदाहरण देखें। क्लास String, जो डायनामिक मेमोरी में C-स्ट्रिंग संग्रहीत करता है:

\n\n
class String {\nprivate:\n    char* data;\n    size_t size;\npublic:\n    // कंस्ट्रक्टर\n    String(const char* str) {\n        size = strlen(str);\n        data = new char[size + 1];\n        strcpy(data, str);\n    }\n\n    // डिस्ट्रक्टर\n    ~String() {\n        delete[] data;\n    }\n\n    // कॉपी कंस्ट्रक्टर — कंपाइलर द्वारा जनरेटेड (सतही)\n    // असाइनमेंट ऑपरेटर — कंपाइलर द्वारा जनरेटेड (सतही)\n\n    void print() const {\n        std::cout << data << std::endl;\n    }\n};
\n\n

सब कुछ ठीक लगता है, जब तक हम ऑब्जेक्ट को कॉपी करने का प्रयास नहीं करते:

\n\n
int main() {\n    String s1("Hello");\n    String s2 = s1;  // कॉपी कंस्ट्रक्टर का कॉल (जनरेटेड)\n    s1.print();\n    s2.print();\n    // प्रोग्राम double free त्रुटि के साथ क्रैश होगा!\n    return 0;\n}
\n\n

क्या हुआ? कंपाइलर ने एक कॉपी कंस्ट्रक्टर जनरेट किया, जिसने केवल पॉइंटर data को कॉपी किया। अब s1.data और s2.data एक ही मेमोरी ब्लॉक को इंगित करते हैं। जब ऑब्जेक्ट स्कोप से बाहर जाते हैं, तो उनके डिस्ट्रक्टर कॉल होते हैं। पहला डिस्ट्रक्टर मेमोरी को मुक्त करता है, दूसरा इसे फिर से मुक्त करने का प्रयास करता है — double free। प्रोग्राम क्रैश हो जाता है या अप्रत्याशित व्यवहार करता है।

\n\n

समाधान: तीन के नियम का कार्यान्वयन

\n

स्थिति को ठीक करने के लिए, तीनों विशेष सदस्य फ़ंक्शन को लागू करना होगा। इसे ही तीन का नियम कहते हैं। आइए क्लास का सही संस्करण लिखें:

\n\n
class String {\nprivate:\n    char* data;\n    size_t size;\npublic:\n    // कंस्ट्रक्टर\n    String(const char* str) {\n        size = strlen(str);\n        data = new char[size + 1];\n        strcpy(data, str);\n    }\n\n    // डिस्ट्रक्टर\n    ~String() {\n        delete[] data;\n    }\n\n    // कॉपी कंस्ट्रक्टर — गहरी कॉपी\n    String(const String& other) {\n        size = other.size;\n        data = new char[size + 1];\n        strcpy(data, other.data);\n    }\n\n    // असाइनमेंट ऑपरेटर — गहरी कॉपी\n    String& operator=(const String& other) {\n        if (this == &other) return *this; // स्व-असाइनमेंट से सुरक्षा\n\n        delete[] data; // पुराने संसाधनों को मुक्त करें\n\n        size = other.size;\n        data = new char[size + 1];\n        strcpy(data, other.data);\n\n        return *this;\n    }\n\n    void print() const {\n        std::cout << data << std::endl;\n    }\n};
\n\n

अब कॉपी करना सही ढंग से काम करता है: प्रत्येक ऑब्जेक्ट के पास मेमोरी का अपना ब्लॉक होता है। असाइनमेंट भी सही है।

ब्लॉग

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