परिचय: आपका कोड जादू जैसा क्यों नहीं लगता?
\n\nकल्पना करें कि आप C++ में एक प्रोग्राम लिख रहे हैं और आपको दो कॉम्प्लेक्स संख्याओं की तुलना करनी है। आप लिखते हैं if (a > b) — और कंपाइलर एरर देता है। "क्यों?" — आप हैरान होते हैं। इसलिए क्योंकि कंपाइलर नहीं जानता कि आपके खुद के डेटा टाइप के लिए "बड़ा" का क्या मतलब है।
C++ में ऑपरेटर सिर्फ कीबोर्ड के निशान नहीं हैं। ये एक शक्तिशाली टूल हैं जो आपके कोड को गणित या Python की तरह स्वाभाविक बनाते हैं। ऑपरेटर ओवरलोडिंग (operator overloading) एक तरीका है जिससे आप कंपाइलर को सिखाते हैं कि आपकी क्लासेस के साथ बिल्ट-इन टाइप्स की तरह ही काम करे। आज हम बुनियादी नियमों और idioms को समझेंगे जो आपके कोड को उबाऊ फंक्शन्स के सेट से C++ के अंदर एक सुंदर "प्रोग्रामिंग भाषा" में बदल देंगे।
\n\nयह सामग्री Stack Overflow के एक वास्तविक प्रश्न पर आधारित है जिसे हज़ारों बार देखा गया। हमने इसे शुरुआती लोगों के लिए अनुकूलित किया है ताकि आप सिर्फ उदाहरण कॉपी न करें, बल्कि समझें कि क्यों कुछ इस तरह करना चाहिए।
\n\nनियम #1: "पहिया मत बनाओ" — ओवरलोडिंग कब उचित है
\n\nपहली बात: ऑपरेटर ओवरलोडिंग कोई पार्टी नहीं है जहाँ सभी को बुलाना है। ऑपरेटरों को तभी ओवरलोड करें जब इससे कोड स्पष्ट हो। अगर आप वेक्टर क्लास बना रहे हैं — तो वेक्टर जोड़ने के लिए + ओवरलोड करना पूरी तरह तर्कसंगत है। लेकिन डेटाबेस क्लास के लिए + ऑपरेटर अजीब और भ्रामक लगेगा।
सुनहरा नियम: ऑपरेटर की सिमैंटिक्स सहज रूप से समझ में आनी चाहिए। आपकी क्लास का उपयोगकर्ता (या एक महीने बाद आप खुद) पहली नज़र में समझ जाना चाहिए कि a + b क्या करता है।
कब ओवरलोड न करें
\n- \n
- अगर ऑपरेटर का आपके टाइप के लिए कोई स्पष्ट अर्थ नहीं है (जैसे, "व्यक्ति" क्लास के लिए
operator+)। \n - अगर आप एक सामान्य फंक्शन से काम चला सकते हैं जिसका नाम बताने वाला हो (
add(),merge())। \n - अगर आप मानक सिमैंटिक्स को बनाए रखने की योजना नहीं बनाते (
+ऑपरेटर को अपने आर्गुमेंट्स नहीं बदलने चाहिए)। \n
नियम #2: असाइनमेंट ऑपरेटर — idioms का राजा
\n\nसबसे आम और सबसे मुश्किल ऑपरेटर है असाइनमेंट ऑपरेटर (operator=)। इसके बिना आपकी क्लास काम करेगी, लेकिन तब तक जब तक आप ऑब्जेक्ट्स को कॉपी करना शुरू नहीं करते। और फिर मज़ा शुरू होता है।
operator= के लिए मुख्य idiom है copy-and-swap (कॉपी करना और बदलना)। यह इतना अच्छा क्यों है? क्योंकि यह एक साथ तीन समस्याओं को हल करता है:
- \n
- सेल्फ-असाइनमेंट से सुरक्षा (
a = a)। \n - एक्सेप्शन सेफ्टी (अगर कॉपी करने में एक्सेप्शन आता है, तो मूल ऑब्जेक्ट अछूता रहता है)। \n
- कोड की सादगी और पठनीयता। \n
व्यवहार में यह इस तरह दिखता है:
\n\nclass MyString {\nprivate:\n char* data;\n size_t size;\n\npublic:\n // कॉपी कंस्ट्रक्टर\n MyString(const MyString& other) : size(other.size) {\n data = new char[size + 1];\n std::copy(other.data, other.data + size + 1, data);\n }\n\n // डिस्ट्रक्टर\n ~MyString() { delete[] data; }\n\n // copy-and-swap के माध्यम से असाइनमेंट ऑपरेटर\n MyString& operator=(MyString other) { // 1. वैल्यू द्वारा लें\n std::swap(data, other.data); // 2. डेटा बदलें\n std::swap(size, other.size);\n return *this; // 3. रेफरेंस लौटाएँ\n } // 4. पुराना ऑब्जेक्ट other के डिस्ट्रक्टर में नष्ट हो जाता है\n};\n\nध्यान दें: other को वैल्यू द्वारा पास किया गया है, रेफरेंस से नहीं। यह मुख्य बिंदु है: कॉपी कंस्ट्रक्टर एक अस्थायी कॉपी बनाता है, और हम बस उसके साथ डेटा बदल लेते हैं। पुराना डेटा अपने आप मुक्त हो जाता है जब अस्थायी ऑब्जेक्ट नष्ट होता है।