C++ में सिंगलटन: यह अंदर से कैसे काम करता है? प्रसिद्ध प्रश्न का विश्लेषण

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

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

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

परिचय: सिंगलटन सिर्फ "एक ऑब्जेक्ट" क्यों नहीं है

\n\n

यदि आपने कभी Stack Overflow पर इस प्रश्न का उत्तर खोजा हो: "How do you implement the Singleton design pattern?", तो आपने हज़ारों वोट, दर्जनों विकल्प और टिप्पणियों में गरमागरम बहस देखी होगी। यह प्रश्न क्लासिक बन गया है क्योंकि सिंगलटन समझने में सबसे सरल, लेकिन सही ढंग से लागू करने में सबसे कठिन पैटर्न में से एक है।

\n\n

इस लेख में हम केवल C++ में सिंगलटन लिखना नहीं दिखाएंगे। हम अंदर की बात देखेंगे: स्थैतिक मेमोरी कैसे काम करती है, इनिशियलाइज़ेशन गारंटी क्या हैं, सरल दृष्टिकोण प्रदर्शन को क्यों खराब कर सकता है, और अंतिम संस्करण (Meyers Singleton) वास्तविक मानक कैसे बन गया।

\n\n

यदि आप साक्षात्कार की तैयारी कर रहे हैं या C++ को गहराई से समझना चाहते हैं, तो यह लेख आपके लिए है। चलिए शुरू करते हैं!

\n\n

सिंगलटन क्या है? संक्षिप्त अनुस्मारक

\n\n

सिंगलटन (एकल) एक सृजनात्मक डिज़ाइन पैटर्न है जो गारंटी देता है कि एक क्लास का केवल एक उदाहरण है, और उस तक वैश्विक पहुँच प्रदान करता है।

\n\n

सरल लगता है, है ना? लेकिन C++ में कुछ बारीकियाँ हैं:

\n
    \n
  • पहली बार ऑब्जेक्ट बनाते समय थ्रेड-सुरक्षा।
  • \n
  • जीवनकाल प्रबंधन (ऑब्जेक्ट कब नष्ट होता है?)।
  • \n
  • कॉपी और असाइनमेंट को रोकना।
  • \n
  • मल्टीथ्रेडिंग समस्याएँ (data race)।
  • \n
\n\n

आइए कार्यान्वयन के विकास को देखें - सरल से औद्योगिक तक।

\n\n

सरल कार्यान्वयन: इसमें क्या गलत है?

\n\n

सबसे आसान तरीका कंस्ट्रक्टर को प्राइवेट बनाना और एक स्थैतिक पॉइंटर संग्रहीत करना है:

\n\n
class Singleton {\npublic:\n    static Singleton* getInstance() {\n        if (instance == nullptr) {\n            instance = new Singleton();\n        }\n        return instance;\n    }\n\n    void doSomething() {\n        // तर्क\n    }\n\nprivate:\n    Singleton() {} // प्राइवेट कंस्ट्रक्टर\n    Singleton(const Singleton&) = delete; // कॉपी निषेध\n    Singleton& operator=(const Singleton&) = delete; // असाइनमेंट निषेध\n\n    static Singleton* instance;\n};\n\n// स्थैतिक सदस्य की परिभाषा\nSingleton* Singleton::instance = nullptr;
\n\n

समस्याएँ:

\n
    \n
  1. थ्रेड-सुरक्षित नहीं। यदि दो थ्रेड एक साथ getInstance() कॉल करते हैं, तो दोनों nullptr देखेंगे और दो ऑब्जेक्ट बनाएँगे। यह सिंगलटन के इनवेरिएंट का उल्लंघन करता है।
  2. \n
  3. मेमोरी लीक। new के माध्यम से बनाया गया ऑब्जेक्ट स्वचालित रूप से हटाया नहीं जाता है। (हालाँकि व्यवहार में, प्रक्रिया समाप्त होने पर मेमोरी OS को वापस कर दी जाती है, लेकिन डिस्ट्रक्टर कॉल नहीं होता है)।
  4. \n
  5. स्थैतिक इनिशियलाइज़ेशन समस्या। यदि कोई अन्य स्थैतिक ऑब्जेक्ट अपने इनिशियलाइज़ेशन से पहले सिंगलटन का उपयोग करने का प्रयास करता है - तो UB होता है।
  6. \n
\n\n

म्यूटेक्स जोड़ा जा सकता है, लेकिन यह getInstance() की प्रत्येक कॉल को धीमा कर देगा, भले ही ऑब्जेक्ट पहले ही बन चुका हो। बुरा है।

\n\n

डबल-चेक्ड लॉकिंग (DCLP): लगभग सही, लेकिन खतरनाक

\n\n

एक लोकप्रिय अनुकूलन पॉइंटर को दो बार जाँचना है: पहले बिना लॉक के, फिर लॉक के साथ। इसे डबल-चेक्ड लॉकिंग पैटर्न कहा जाता है।

\n\n
#include <mutex>\n\nclass Singleton {\npublic:\n    static Singleton* getInstance() {\n        if (instance == nullptr) {          // पहली जाँच (बिना लॉक के)\n            std::lock_guard<std::mutex> lock(mutex);\n            if (instance == nullptr) {      // दूसरी जाँच (लॉक के साथ)\n                instance = new Singleton();\n            }\n        }\n        return instance;\n    }\n\nprivate:\n    Singleton() {}\n    Singleton(const Singleton&) = delete;\n    Singleton& operator=(const Singleton&) = delete;\n\n    static Singleton* instance;\n    static std::mutex mutex;\n};\n\nSingleton* Singleton::instance = nullptr;\nstd::mutex Singleton::mutex;<                

ब्लॉग

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