C++ आज भी वरिष्ठ डेवलपर्स को क्यों डराता है?
\nC++ एक ऐसी भाषा है जो आपको मेमोरी पर पूर्ण नियंत्रण देती है, लेकिन तब तक जब तक आप सेमीकोलन लगाना नहीं भूलते या गलती से कंस्ट्रक्टर से वर्चुअल फंक्शन को कॉल नहीं करते। C++ साक्षात्कार अपनी कठोरता के लिए प्रसिद्ध हैं: आपसे बिट फील्ड्स, वर्चुअल टेबल की संरचना और std::move की बारीकियों के बारे में पूछा जा सकता है। यदि आप तकनीकी साक्षात्कार की तैयारी कर रहे हैं, तो यह लेख आपकी चेकलिस्ट है। हम सबसे कपटी विषयों पर चर्चा करेंगे जो लगभग निश्चित रूप से साक्षात्कार में सामने आएंगे, और दिखाएंगे कि उनका उत्तर कैसे दें ताकि आप असफल न हों।
शैक्षिक पोर्टल PythonLib पर, हम अक्सर चर्चा करते हैं कि Python से C++ और इसके विपरीत कैसे स्विच किया जाए। लेकिन आज - शुद्ध C++। कोई समझौता नहीं।
\n\n1. वर्चुअल फंक्शन्स और VTable टेबल्स: जाल में कैसे न फंसें
\nप्रश्न: "यदि आप बेस क्लास के कंस्ट्रक्टर से वर्चुअल फंक्शन को कॉल करते हैं तो क्या होगा?" - यह एक क्लासिक है। 70% उम्मीदवार गलत उत्तर देते हैं। आइए समझते हैं।
\n\nयह खतरनाक क्यों है?
\nजब आप बेस क्लास के कंस्ट्रक्टर में वर्चुअल फंक्शन को कॉल करते हैं, तो डिराइव्ड क्लास का ऑब्जेक्ट अभी तक नहीं बना है। उस समय वर्चुअल टेबल (VTable) डिराइव्ड क्लास के तरीकों के बजाय बेस क्लास के तरीकों की ओर इशारा करती है। परिणामस्वरूप, पॉलीमॉर्फिज्म काम नहीं करता है।
\n\n#include <iostream>\n\nclass Base {\npublic:\n Base() { callFoo(); } // कंस्ट्रक्टर में कॉल\n virtual void foo() { std::cout << "Base::foo\\n"; }\n void callFoo() { foo(); }\n};\n\nclass Derived : public Base {\npublic:\n Derived() {}\n virtual void foo() override { std::cout << "Derived::foo\\n"; }\n};\n\nint main() {\n Derived d; // आउटपुट: Base::foo, Derived::foo नहीं\n return 0;\n}\n\nनिष्कर्ष: कंस्ट्रक्टर और डिस्ट्रक्टर में कभी भी वर्चुअल फंक्शन को कॉल न करें। यदि साक्षात्कारकर्ता पूछता है - तो साहसपूर्वक VTable तंत्र समझाएं।
\n\n2. स्मार्ट पॉइंटर्स: RAII और मेमोरी प्रबंधन
\nइस विषय के बिना कोई भी गंभीर C++ साक्षात्कार पूरा नहीं होता है। std::unique_ptr, std::shared_ptr और std::weak_ptr के बारे में प्रश्न - बुनियादी हैं। लेकिन अक्सर साइक्लिक रेफरेंस और std::enable_shared_from_this के बारे में पूछा जाता है।
shared_ptr के साथ मेमोरी लीक से कैसे बचें?
\nकल्पना करें कि आपके पास दो ऑब्जेक्ट हैं जो shared_ptr के माध्यम से एक-दूसरे को संदर्भित करते हैं। रेफरेंस काउंटर कभी शून्य नहीं होगा - मेमोरी लीक हो जाएगी। समाधान: पॉइंटर्स में से एक weak_ptr होना चाहिए।
#include <memory>\n#include <iostream>\n\nstruct Node {\n std::shared_ptr<Node> next;\n std::weak_ptr<Node> prev; // चक्र से बचने के लिए कमजोर संदर्भ\n ~Node() { std::cout << "Node destroyed\\n"; }\n};\n\nint main() {\n auto a = std::make_shared<Node>();\n auto b = std::make_shared<Node>();\n a->next = b;\n b->prev = a; // weak_ptr काउंटर नहीं बढ़ाता\n // स्कोप से बाहर निकलने पर दोनों नोड सही ढंग से हट जाएंगे\n return 0;\n}\n\nसाक्षात्कार में RAII (Resource Acquisition Is Initialization) अवधारणा का उल्लेख करना सुनिश्चित करें - यह C++ का दर्शन है जो संसाधन के जीवनकाल को ऑब्जेक्ट के जीवनकाल से जोड़ता है।
\n\n3. मूव-सिमैंटिक्स और std::move: जादू या चाल?
\nएक सामान्य प्रश्न: "std::move क्या करता है?" उत्तर "ऑब्जेक्ट को स्थानांतरित करता है" - गलत है। std::move केवल प्रकार को rvalue-रेफरेंस में बदलता है। वास्तविक स्थानांतरण मूव-कंस्ट्रक्टर या मूव-असाइनमेंट ऑपरेटर में होता है।
साक्षात्कार के लिए उदाहरण
\n#include <string>\n#include <vector>\n#include <iostream>\n\nclass MyString {\n std::string data;\npublic:\n MyString(const std::string& s) : data(s) {}\n // मूव-कंस्ट्रक्टर\n MyString(MyString&& other) noexcept : data(std::move(other.data)) {\n std::cout << "Move constructor c