"एक कंस्ट्रक्टर से दूसरे में": C++ डेवलपर का शाश्वत प्रश्न
\n\nएक स्थिति की कल्पना करें: आप एक ऐसा क्लास लिख रहे हैं जिसमें कई कंस्ट्रक्टर हैं। एक तीन पैरामीटर लेता है, दूसरा दो, तीसरा एक। और सभी में एक ही इनिशियलाइज़ेशन लॉजिक दोहराया जाता है। आपका मन कुछ इस तरह लिखने को करता है MyClass() : MyClass(42, "default") {} — और डुप्लिकेशन के बारे में भूल जाएं। लेकिन C++ कंपाइलर (एक निश्चित बिंदु तक) इसे संदेह की दृष्टि से देखता है। क्या एक कंस्ट्रक्टर से दूसरे कंस्ट्रक्टर को कॉल करना संभव भी है? और यदि हाँ — तो कैसे?
यह प्रश्न Stack Overflow पर सबसे लोकप्रिय प्रश्नों में से एक है (दसियों हज़ार व्यूज़)। कोई आश्चर्य नहीं: कंस्ट्रक्टर चेनिंग (constructor chaining) का विषय शुरुआती और यहां तक कि अनुभवी डेवलपर्स के लिए भी एक वास्तविक बाधा है, जो Java या Python से C++ पर आ रहे हैं। इस लेख में हम समस्या के तीन दृष्टिकोणों का विश्लेषण करेंगे, "अस्थायी समाधान" से लेकर C++11 के सुरुचिपूर्ण समाधानों तक, और पता लगाएंगे कि वास्तविक कोड में किसका उपयोग किया जाना चाहिए।
\n\nकंस्ट्रक्टर से कंस्ट्रक्टर को सीधे क्यों नहीं कॉल किया जा सकता?
\n\nभोला दृष्टिकोण इस तरह दिखता है:
\n\nclass MyClass {\npublic:\n MyClass() {\n // कुछ कोड\n }\n MyClass(int x) {\n MyClass(); // त्रुटि: ऐसा नहीं किया जा सकता!\n }\n};\n\nC++ में, कंस्ट्रक्टर एक सामान्य फ़ंक्शन नहीं है। यह ऑब्जेक्ट को एक बार इनिशियलाइज़ करता है। यदि आप बॉडी के अंदर किसी अन्य कंस्ट्रक्टर को स्पष्ट रूप से कॉल करने का प्रयास करते हैं, तो कंपाइलर इसे एक अस्थायी ऑब्जेक्ट बनाने के रूप में मानेगा — और वर्तमान इंस्टेंस में कुछ भी इनिशियलाइज़ नहीं होगा। परिणाम: या तो कंपाइलेशन त्रुटि, या (इससे भी बुरा) अपरिभाषित व्यवहार।
\n\nC++11 मानक से पहले, इनिशियलाइज़ेशन लॉजिक को "साझा" करने का एकमात्र कानूनी तरीका एक प्राइवेट init() मेथड को कॉल करना था। लेकिन यह वास्तविक डेलिगेशन नहीं था: क्लास के सदस्यों को पहले डिफ़ॉल्ट कंस्ट्रक्टर द्वारा इनिशियलाइज़ किया जाता था, और फिर ओवरराइट किया जाता था। अकुशल और बग-प्रवण।
दृष्टिकोण #1: डेलिगेटिंग कंस्ट्रक्टर (C++11 और नए)
\n\nC++11 से शुरू करके, भाषा में एक लंबे समय से प्रतीक्षित सुविधा जोड़ी गई — डेलिगेटिंग कंस्ट्रक्टर (delegating constructors)। अब आप सीधे इनिशियलाइज़ेशन लिस्ट में एक कंस्ट्रक्टर से दूसरे को कॉल कर सकते हैं:
\n\nclass MyClass {\n int value;\n std::string name;\npublic:\n // बेस कंस्ट्रक्टर\n MyClass(int v, const std::string& n) : value(v), name(n) {\n std::cout << "Full init: " << value << ", " << name << "\\n";\n }\n // डेलिगेटिंग कंस्ट्रक्टर\n MyClass(int v) : MyClass(v, "default") {}\n // एक और डेलिगेटिंग\n MyClass() : MyClass(0, "default") {}\n};\n\nयह कैसे काम करता है? कंस्ट्रक्टर MyClass(int v) नियंत्रण कंस्ट्रक्टर MyClass(int, string) को स्थानांतरित करता है। वह सभी इनिशियलाइज़ेशन करता है, और उसके बाद ही नियंत्रण वापस आता है। महत्वपूर्ण नियम: इनिशियलाइज़ेशन लिस्ट में किसी अन्य कंस्ट्रक्टर को कॉल करने और सदस्यों के सीधे इनिशियलाइज़ेशन को मिश्रित नहीं किया जा सकता। या तो डेलिगेशन, या अपना इनिशियलाइज़ेशन — दो में से एक।
लाभ और हानि
\n- \n
- ✅ सुरुचिपूर्ण: कोड बिना डुप्लिकेशन के साफ हो जाता है। \n
- ✅ प्रदर्शन: ऑब्जेक्ट एक बार इनिशियलाइज़ होता है, बिना अनावश्यक संचालन के। \n
- ⚠️ सीमा: डेलिगेशन केवल एक "गहराई" तक काम करता है — चेन रिकर्सिव नहीं हो सकती (आप स्टैक ओवरफ्लो का कारण बनेंगे)। \n
- ⚠️ संगतता: C++11 या नए की आवश्यकता है। यदि आप पुराने कंपाइलर पर अटके हैं — यह विकल्प आपके लिए नहीं है। \n
दृष्टिकोण #2: प्राइवेट init() मेथड
\n\nक्लासिक "पूर्व-C++11" तरीका — सामान्य लॉजिक को एक प्राइवेट मेथड में डालना। यह इस तरह दिखता है:
\n\nclass MyClass {\n int value;\n std::string name;\n void init(int v, const std::string& n) {\n value = v;\n name = n;\n std::cout << "Init called\\n";\n