सी ++ सिंगलटन डिजाइन पैटर्न


735

हाल ही में मैं C ++ के लिए सिंगलटन डिज़ाइन पैटर्न के एक एहसास / कार्यान्वयन में टकरा गया हूं। यह इस तरह दिखता है (मैंने इसे वास्तविक जीवन के उदाहरण से अपनाया है):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

इस घोषणा से मैं यह अनुमान लगा सकता हूं कि ढेर पर उदाहरण क्षेत्र शुरू किया गया है। इसका मतलब है कि मेमोरी एलोकेशन है। मेरे लिए पूरी तरह से अस्पष्ट है जब वास्तव में स्मृति को समाप्त किया जा रहा है? या वहाँ एक बग और स्मृति रिसाव है? ऐसा लगता है कि कार्यान्वयन में कोई समस्या है।

मेरा मुख्य सवाल यह है कि मैं इसे सही तरीके से कैसे लागू करूं?



10
आपको इस पेपर में C ++ में थ्रेड-सेफ्टी के साथ-साथ एक सिंगलटन को कैसे कार्यान्वित करना है, इसकी शानदार चर्चा मिलेगी। aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

106
@sbi - केवल एक सिथति निरपेक्षता में है। क्या सिंगलेट्स के बिना अधिकांश समस्याओं को हल किया जा सकता है? पूर्ण रूप से। क्या सिंग्लेट्स अपनी खुद की समस्याओं का कारण बनते हैं? हाँ। हालाँकि, मैं ईमानदारी से यह नहीं कह सकता कि वे खराब हैं , क्योंकि डिज़ाइन सभी ट्रेडऑफ़ पर विचार करने और आपके दृष्टिकोण की बारीकियों को समझने के बारे में है।
derekerdmann

11
@derekerdmann: मैंने यह नहीं कहा कि आपको कभी भी वैश्विक चर की आवश्यकता नहीं है (और जब आपको एक की आवश्यकता होती है, तो एक सिंगलटन कभी-कभी बेहतर होता है)। मैंने जो कहा है, उनका उपयोग यथासंभव कम किया जाना चाहिए। एक मूल्यवान डिजाइन पैटर्न के रूप में सिंग्लटन का महिमामंडन करना इसका उपयोग करने के लिए अच्छा लग रहा है, बजाय इसके कि यह एक हैक है , कोड को समझना मुश्किल है, बनाए रखना मुश्किल है, और परीक्षण करना कठिन है। यही कारण है कि मैंने अपनी टिप्पणी पोस्ट की। आपने अभी तक जो भी कहा उसमें से किसी ने भी इसका खंडन नहीं किया।
sbi

13
@ एसबीआई: आपने जो कहा था, "उनका उपयोग न करें।" बहुत अधिक उचित नहीं "उन्हें जितना संभव हो उतना कम इस्तेमाल किया जाना चाहिए" आप बाद में बदल गए - निश्चित रूप से आप अंतर देखते हैं।
jwd

जवाबों:


1106

2008 में मैंने एक C ++ 98 सिंगलटन डिजाइन पैटर्न को लागू किया, जो आलसी-मूल्यांकित, गारंटीकृत-विनाश के लिए है, न कि तकनीकी रूप से-थ्रेड-सेफ में:
क्या कोई भी मुझे c ++ में सिंगलटन का नमूना प्रदान कर सकता है?

यहाँ एक अद्यतन C ++ 11 कार्यान्वयन है जो सिंगलटन डिज़ाइन पैटर्न का है जो आलसी-मूल्यांकित, सही ढंग से नष्ट, और थ्रेड-सुरक्षित है

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

इस लेख को देखें कि किसी सिंगलटन का उपयोग कब करना है: (अक्सर नहीं)
सिंगलटन: इसका उपयोग कैसे किया जाना चाहिए

आरंभीकरण क्रम और कैसे सामना करें के बारे में यह दो लेख देखें:
स्थैतिक चर आरंभीकरण क्रम
ढूँढना C ++ स्थैतिक आरंभीकरण आदेश समस्याएँ

जीवनकाल का वर्णन करने वाला यह लेख देखें: C ++ फ़ंक्शन में एक स्थिर चर का जीवनकाल
क्या है?

इस लेख को देखें जो एकल के कुछ सूत्रणों के बारे में चर्चा करता है:
GetInstance विधि के स्थिर चर के रूप में घोषित सिंगलटन उदाहरण, क्या यह थ्रेड-सुरक्षित है?

इस लेख को देखें जो बताता है कि डबल चेक्ड लॉकिंग C ++ पर काम क्यों नहीं करेगा:
सभी सामान्य अपरिभाषित व्यवहार क्या हैं जिनके बारे में C ++ प्रोग्रामर को पता होना चाहिए?
डॉ। डॉब्स: सी ++ और द पेरिल्स ऑफ डबल-चेक्ड लॉकिंग: भाग I


22
अच्छा उत्तर। लेकिन ध्यान रखें कि इस सूत्र-सुरक्षित नहीं है stackoverflow.com/questions/1661529/...
वरुण

4
@ ज़ोर्टनी: बहुत से लोगों को एहसास नहीं है कि आपने अभी क्या किया :)
जोहान जेरेल

4
@MaximYegorushkin: जब यह नष्ट हो जाता है तो बहुत अच्छी तरह से परिभाषित किया जाता है (कोई अस्पष्टता नहीं है)। देखें: stackoverflow.com/questions/246564/…
मार्टिन यॉर्क

3
What irks me most though is the run-time check of the hidden boolean in getInstance()यह कार्यान्वयन तकनीक पर एक धारणा है। इसके जीवित होने के बारे में कोई धारणा नहीं है। देखें stackoverflow.com/a/335746/14065 आप एक स्थिति को मजबूर कर सकते हैं ताकि यह हमेशा जीवित रहे (कम उपरि की तुलना में Schwarz counter)। वैश्विक चर के पास आरंभीकरण क्रम (संकलन इकाइयों के पार) के साथ अधिक मुद्दे हैं क्योंकि आप एक आदेश को बाध्य नहीं करते हैं। इस मॉडल का लाभ 1) आलसी आरंभीकरण है। 2) एक आदेश लागू करने की क्षमता (श्वार्ज मदद करता है लेकिन बदसूरत है)। हां get_instance()बहुत बदसूरत है।
मार्टिन यॉर्क

3
@kol: नहीं। यह सामान्य नहीं है। सिर्फ इसलिए कि शुरुआती लोग बिना सोचे-समझे कॉपी और पेस्ट कोड को सामान्य नहीं बनाते हैं। आपको हमेशा उपयोग के मामले को देखना चाहिए और यह सुनिश्चित करना चाहिए कि असाइनमेंट ऑपरेटर वही करता है जो अपेक्षित है। कॉपी और पेस्ट कोड आपको त्रुटियों में ले जाने वाला है।
मार्टिन यॉर्क

47

एक सिंगलटन होने के नाते, आप आमतौर पर इसे नष्ट नहीं करना चाहते हैं।

जब प्रोग्राम समाप्त हो जाएगा, जो एक सिंगलटन के लिए सामान्य, वांछित व्यवहार है, तो यह खराब हो जाएगा और निपटा जाएगा। यदि आप इसे स्पष्ट रूप से साफ़ करने में सक्षम होना चाहते हैं, तो उस वर्ग के लिए एक स्थिर विधि जोड़ना काफी आसान है जो आपको इसे एक स्वच्छ स्थिति में पुनर्स्थापित करने की अनुमति देता है, और अगली बार इसका उपयोग करने पर इसे फिर से चालू करें, लेकिन यह एक के दायरे से बाहर है "क्लासिक" सिंगलटन।


4
यदि हटाए जाने को स्पष्ट रूप से स्थिर सिंगलटन * उदाहरण पर नहीं कहा जाता है, तो क्या यह तकनीकी रूप से स्मृति रिसाव नहीं माना जाएगा?
एंड्रयू गैरीसन

7
यह वैश्विक चर की एक साधारण घोषणा की तुलना में अब स्मृति रिसाव नहीं है।
एलिया एन।

15
कुछ सीधे सेट करने के लिए ... "मेमोरी लीक" चिंताएं एक-दृष्टि वाले एकल एकल पूरी तरह से अप्रासंगिक हैं। यदि आपके पास राज्य के संसाधन हैं जिसमें पुनर्निर्माण के आदेश मायने रखते हैं, तो सिंगलनेट खतरनाक हो सकते हैं; लेकिन सभी मेमोरी को प्रोग्राम समाप्ति पर ऑपरेटिंग सिस्टम द्वारा साफ किया जाता है ... 99.9% मामलों में इस पूरी तरह से शैक्षणिक बिंदु को शून्य करता है। यदि आप व्याकरण के बारे में बहस करना चाहते हैं कि आगे क्या है और "मेमोरी लीक" नहीं है, तो यह ठीक है, लेकिन यह महसूस करें कि यह वास्तविक डिज़ाइन निर्णयों से एक व्याकुलता है।
जुकरियन

12
@ जकरियन: मेमोरी लीक और सी ++ संदर्भ में विनाश वास्तव में मेमोरी लीक के बारे में नहीं है। वास्तव में यह संसाधन नियंत्रण के बारे में है। यदि आप मेमोरी को लीक करते हैं तो डिस्ट्रक्टर को नहीं बुलाया जाता है और इस प्रकार ऑब्जेक्ट से जुड़े किसी भी संसाधन को सही ढंग से जारी नहीं किया जाता है। मेमोरी केवल सरल उदाहरण है जिसका उपयोग हम प्रोग्रामिंग सिखाते समय करते हैं लेकिन वहाँ बहुत अधिक जटिल संसाधन हैं।
मार्टिन यॉर्क

7
@ मैं आपसे पूरी तरह सहमत हूँ। यहां तक ​​कि अगर एकमात्र संसाधन स्मृति है, तो भी आप अपने कार्यक्रम में वास्तविक लीक खोजने की कोशिश में मुश्किल में पड़ जाएंगे, यदि आपको लीक की सूची के माध्यम से उतारा जाना है, तो उन लोगों को फ़िल्टर करना जो "कोई फर्क नहीं पड़ता।" इन सभी को साफ करने के लिए बेहतर है कि कोई भी उपकरण जो रिपोर्ट लीक करता है वह केवल उन चीजों की रिपोर्ट करता है जो एक समस्या है।
डॉल्फिन

38

आप मेमोरी आवंटन से बच सकते हैं। कई वेरिएंट हैं, जो सभी पर्यावरण के मामले में समस्या हैं।

मैं इस तरह के कार्यान्वयन को पसंद करता हूं (वास्तव में, यह सही ढंग से नहीं कहा जाता है कि मैं पसंद करता हूं, क्योंकि मैं जितना संभव हो उतना एकल से बचता हूं):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

इसका कोई डायनामिक मेमोरी आवंटन नहीं है।


3
कुछ उदाहरणों में, यह आलसी आरंभिक अनुसरण करने के लिए आदर्श पैटर्न नहीं है। एक उदाहरण यह है कि अगर सिंगलटन का कंस्ट्रक्टर हीप से मेमोरी आवंटित करता है और आप चाहते हैं कि एंबेडेड सिस्टम या अन्य नियंत्रित वातावरण में उदाहरण के लिए, वह आवंटन अनुमानित हो। मैं पसंद करता हूं, जब सिंग्लटन पैटर्न का उपयोग करने के लिए सबसे अच्छा पैटर्न है, उदाहरण के लिए कक्षा के एक स्थिर सदस्य के रूप में।
dma

3
कई बड़े कार्यक्रमों के लिए, विशेष रूप से गतिशील पुस्तकालयों के साथ। कोई भी वैश्विक या स्थिर वस्तु जो कि कोई भी आदिम नहीं है, पुस्तकालयों को अनलोड करने पर विनाश के मुद्दों के आदेश के कारण कई प्लेटफार्मों पर प्रोग्राम से बाहर निकलने पर सीगफॉल्ट / क्रैश हो सकती है। कई गैर-तुच्छ स्थैतिक और वैश्विक वस्तुओं के उपयोग पर प्रतिबंध लगाने के कई कारणों में से एक यह है (Google सहित)।
18

ऐसा लगता है कि इस तरह के कार्यान्वयन में स्थिर उदाहरण में आंतरिक संबंध हैं, और विभिन्न अनुवाद इकाई में अद्वितीय और स्वतंत्र प्रतियां होंगी, जिससे भ्रमित और गलत व्यवहार होगा। लेकिन मैंने कई ऐसे कार्यान्वयन देखे, क्या मुझे कुछ याद आ रहा है?
फेसब्रो

उपयोगकर्ता को ऐसे कई ऑब्जेक्ट्स को असाइन करने से रोकता है जहां पर्दे के पीछे कंपाइलर अपनी कॉपी कंस्ट्रक्टर का उपयोग करता है?
टोनी टैनस

19

@ लोकी एस्टरी का जवाब बेहतरीन है।

हालांकि कई स्थिर वस्तुओं के साथ समय होता है जहां आपको यह गारंटी देने में सक्षम होने की आवश्यकता होती है कि सिंगलटन तब तक नष्ट नहीं होगा जब तक कि आपके सभी स्थिर ऑब्जेक्ट जो सिंगलटन का उपयोग नहीं करते हैं, उन्हें इसकी आवश्यकता नहीं है।

इस मामले में सभी उपयोगकर्ताओं के लिए सिंगलटन को जीवित std::shared_ptrरखने के लिए उपयोग किया जा सकता है , तब भी जब प्रोग्राम के अंत में स्थिर विध्वंसक को बुलाया जा रहा हो:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

9

एक और गैर-आवंटन विकल्प: एक सिंगलटन बनाएं, वर्ग का कहना है C, जैसा कि आपको इसकी आवश्यकता है:

singleton<C>()

का उपयोग करते हुए

template <class X>
X& singleton()
{
    static X x;
    return x;
}

वर्तमान C ++ में न तो यह और न ही Cătălin का उत्तर स्वचालित रूप से थ्रेड-सुरक्षित है, लेकिन C ++ 0x में होगा।


वर्तमान में gcc के अंतर्गत यह थ्रेड सेफ है (और कुछ समय के लिए है)।
मार्टिन यॉर्क

13
इस डिजाइन के साथ समस्या यह है कि अगर कई पुस्तकालयों में उपयोग किया जाता है। प्रत्येक पुस्तकालय के पास उस पुस्तकालय की स्वयं की प्रति होती है, जिसका उपयोग पुस्तकालय करता है। इसलिए यह अब एकल नहीं है।
मार्टिन

6

मुझे जवाबों के बीच एक CRTP कार्यान्वयन नहीं मिला, इसलिए यहां यह है:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

अपनी कक्षा को इस से प्राप्त करने के लिए, जैसे: class Test : public Singleton<Test>


1
C ++ 17 के साथ काम करने के लिए इसे प्राप्त नहीं किया जा सका, जब तक कि मैंने डिफ़ॉल्ट कंस्ट्रक्टर को संरक्षित और '=' डिफ़ॉल्ट नहीं बनाया।
WFranczyk

6

स्वीकृत उत्तर में समाधान में एक महत्वपूर्ण खामी है - नियंत्रण को main()कार्य छोड़ने के बाद सिंगलटन के लिए विध्वंसक कहा जाता है । वास्तव में समस्याएं हो सकती हैं, जब कुछ निर्भर वस्तुओं को अंदर आवंटित किया जाता है main

मैं इस समस्या से मिला, जब Qt अनुप्रयोग में एक सिंगलटन को पेश करने की कोशिश कर रहा था। मैंने फैसला किया, कि मेरे सभी सेटअप संवाद सिंग्लेटन्स होने चाहिए, और ऊपर दिए गए पैटर्न को अपनाया जाना चाहिए। दुर्भाग्य से, Qt के मुख्य वर्ग QApplicationको mainफ़ंक्शन में स्टैक पर आवंटित किया गया था , और कोई एप्लिकेशन ऑब्जेक्ट उपलब्ध नहीं होने पर क्यूटी को डायलॉग बनाने / नष्ट करने से मना किया जाता है।

यही कारण है कि मैं ढेर-आवंटित सिंग्लेटों को पसंद करता हूं। मैं सभी सिंग्लेटों के लिए एक स्पष्ट init()और term()तरीके प्रदान करता हूं और उन्हें अंदर बुलाता हूं main। इस प्रकार सिंगलेट्स के निर्माण / विनाश के आदेश पर मेरा पूर्ण नियंत्रण है, और मैं यह भी गारंटी देता हूं कि सिंग्लेटों का निर्माण किया जाएगा, भले ही किसी ने फोन किया हो getInstance()या नहीं।


2
यदि आप वर्तमान में स्वीकृत उत्तर का उल्लेख कर रहे हैं तो आप पहले कथन को गलत मानते हैं। विध्वंसक को तब तक नहीं कहा जाता है जब तक कि सभी स्थिर भंडारण अवधि ऑब्जेक्ट नष्ट नहीं हो जाते हैं।
मार्टिन यॉर्क

5

यहाँ एक आसान कार्यान्वयन है।

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

केवल एक ऑब्जेक्ट बनाया गया है और यह ऑब्जेक्ट संदर्भ प्रत्येक और उसके बाद हर बार लौटाया जाता है।

SingletonClass instance created!
00915CB8
00915CB8

यहाँ 00915CB8 सिंगलटन ऑब्जेक्ट की मेमोरी लोकेशन है, जो प्रोग्राम की अवधि के लिए समान है लेकिन (सामान्य रूप से!) हर बार प्रोग्राम चलने पर अलग है।

एनबी यह एक धागा सुरक्षित नहीं है। आपको धागा सुरक्षा सुनिश्चित करना है।


5

यदि आप हीप में ऑब्जेक्ट को आवंटित करना चाहते हैं, तो एक अद्वितीय पॉइंटर का उपयोग क्यों न करें। मेमोरी को भी हटा दिया जाएगा क्योंकि हम एक अद्वितीय पॉइंटर का उपयोग कर रहे हैं।

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

3
सी ++ 11 में पदावनत। इसके बजाय unique_ptr की सिफारिश की जाती है। cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
एंड्रयू

2
यह धागा सुरक्षित नहीं है। बेहतर बनाने के लिए m_sएक स्थानीय staticकी getInstance()और एक परीक्षण के बिना तुरंत आरंभ कर देगा।
गालिक

2

यह वास्तव में शायद ढेर से आवंटित किया गया है, लेकिन स्रोतों के बिना जानने का कोई तरीका नहीं है।

विशिष्ट कार्यान्वयन (पहले से ही ईएमएसी में मेरे पास मौजूद कुछ कोड से लिया जाएगा):

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... और बाद में सफाई के दायरे से बाहर होने वाले कार्यक्रम पर भरोसा करें।

यदि आप एक ऐसे प्लेटफ़ॉर्म पर काम करते हैं जहाँ मैन्युअल रूप से क्लीनअप किया जाना चाहिए, तो मैं संभवतः एक मैनुअल क्लीनअप रूटीन जोड़ूंगा।

इसे इस तरह से करने के साथ एक और मुद्दा यह है कि यह थ्रेड-सुरक्षित नहीं है। एक बहुआयामी वातावरण में, दो धागे "यदि" के माध्यम से प्राप्त कर सकते हैं या तो पहले नए उदाहरण (इसलिए दोनों होगा) को आवंटित करने का मौका है। यदि आप किसी भी तरह से सफाई करने के लिए कार्यक्रम समाप्ति पर भरोसा कर रहे हैं तो यह अभी भी बहुत बड़ी बात नहीं है।


आप कटौती कर सकते हैं, क्योंकि आप देख सकते हैं कि उदाहरण चर वर्ग उदाहरण के लिए एक संकेतक है।
आर्टेम बार्गर

3
एकल को गतिशील रूप से आवंटित करने की कोई आवश्यकता नहीं है। वास्तव में यह एक बुरा विचार है क्योंकि उपरोक्त डिज़ाइन का उपयोग करके स्वचालित रूप से डी-आवंटित करने का कोई तरीका नहीं है। इसे दायरे से बाहर होने दें, यह विनाशकारी नहीं है और सिर्फ आलसी है।
मार्टिन यॉर्क

आप स्वचालित रूप से एटैक्सिट फ़ंक्शन का उपयोग करके निपटा सकते हैं। यही हम करते हैं (यह एक अच्छा विचार नहीं कह रहे हैं)
जो

2

किसी को भी उल्लेख किया है std::call_onceऔर std::once_flag? अधिकांश अन्य दृष्टिकोण - जिनमें डबल चेकिंग लॉकिंग शामिल है - टूटे हुए हैं।

सिंगलटन पैटर्न कार्यान्वयन में एक बड़ी समस्या सुरक्षित आरंभीकरण है। एकमात्र सुरक्षित तरीका अवरोधकों के साथ सिंक्रनाइज़ेशन अनुक्रम की रक्षा करना है। लेकिन उन बाधाओं को स्वयं सुरक्षित रूप से शुरू करने की आवश्यकता है। std::once_flagसुरक्षित आरंभीकरण की गारंटी पाने के लिए तंत्र है।


2

हमने हाल ही में अपने ईईसीएस वर्ग में इस विषय पर काम किया। यदि आप व्याख्यान के नोट्स को विस्तार से देखना चाहते हैं, तो http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf पर जाएँ

दो तरीके हैं जो मैं एक सिंगलटन क्लास को सही तरीके से बनाना जानता हूं।

पहला तरीका:

इसे उसी तरह से लागू करें जिस तरह से आपके पास इसका उदाहरण है। विनाश के लिए के रूप में, "सिंगलेट्स आमतौर पर कार्यक्रम चलाने की अवधि के लिए सहन करते हैं; अधिकांश ओएस स्मृति को पुनर्प्राप्त करेंगे और जब कोई कार्यक्रम समाप्त होता है तो अधिकांश अन्य संसाधन, इसलिए इस बारे में चिंता न करने का एक तर्क है।"

हालांकि, कार्यक्रम समाप्ति पर सफाई करना अच्छा है। इसलिए, आप इसे एक सहायक स्टेटिक सिंगलटनडिस्ट्रक्टर वर्ग के साथ कर सकते हैं और घोषणा कर सकते हैं कि आपके सिंगलटन में एक दोस्त के रूप में।

class Singleton {
public:
  static Singleton* get_instance();

  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

Singleton_destroyer प्रोग्राम स्टार्टअप पर बनाया जाएगा, और "जब प्रोग्राम समाप्त हो जाता है, रनटाइम लाइब्रेरी शटडाउन कोड (लिंकर द्वारा सम्मिलित) द्वारा सभी वैश्विक / स्थैतिक ऑब्जेक्ट नष्ट हो जाते हैं, तो the_destroyer नष्ट हो जाएगा; इसका विध्वंसक सिंग्लटन को हटा देगा, इसे चलाने वाला नाशक। "

दूसरा तरीका

यह Meyers Singleton कहा जाता है, जिसे C ++ विज़ार्ड स्कॉट मेयर्स द्वारा बनाया गया है। बस get_instance () को अलग तरीके से परिभाषित करें। अब आप पॉइंटर मेंबर वेरिएबल से भी छुटकारा पा सकते हैं।

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

यह साफ-सुथरा है क्योंकि लौटाया गया मूल्य संदर्भ से है और आप सदस्य चर का उपयोग करने के .बजाय सिंटैक्स का उपयोग कर सकते हैं ->

"कंपाइलर स्वचालित रूप से कोड बनाता है जो घोषणा के माध्यम से पहली बार बनाता है, उसके बाद नहीं, और फिर प्रोग्राम समाप्ति पर स्थिर ऑब्जेक्ट को हटा देता है।"

यह भी ध्यान दें कि मेयर्स सिंगलटन के साथ आप "बहुत मुश्किल स्थिति में आ सकते हैं यदि ऑब्जेक्ट समाप्ति के समय एक दूसरे पर भरोसा करते हैं - सिंग्लटन कब अन्य वस्तुओं के सापेक्ष गायब हो जाता है? लेकिन सरल अनुप्रयोगों के लिए, यह ठीक काम करता है।"


1

यहां अन्य चर्चा के अलावा, यह ध्यान देने योग्य हो सकता है कि आपके पास एक उदाहरण के उपयोग को सीमित किए बिना, वैश्विक-नेस हो सकता है। उदाहरण के लिए, संदर्भ की गिनती के मामले पर विचार करें ...

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

अब एक समारोह के अंदर कहीं (जैसे main) आप कर सकते हैं:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

रेफरी को अपने संबंधित को वापस पॉइंटर स्टोर करने की आवश्यकता नहीं है Storeक्योंकि यह जानकारी संकलन-समय पर आपूर्ति की जाती है। आपको Storeजीवन भर के बारे में चिंता करने की ज़रूरत नहीं है क्योंकि संकलक को यह आवश्यक है कि वह वैश्विक हो। अगर वास्तव में केवल एक ही उदाहरण है Storeतो इस दृष्टिकोण में कोई उपरि नहीं है; एक से अधिक उदाहरणों के साथ यह संकलक तक कोड पीढ़ी के बारे में चतुर होना है। यदि आवश्यक हो, ItemRefवर्ग भी एक बनाया जा सकता है friendकी Store(आप टेम्प्लेट की गई है सकते हैं मित्र!)।

यदि Storeस्वयं एक टेम्पल क्लास है तो चीजें गड़बड़ हो जाती हैं, लेकिन इस पद्धति का उपयोग करना अभी भी संभव है, शायद निम्नलिखित हस्ताक्षर के साथ एक सहायक वर्ग को लागू करके:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

उपयोगकर्ता अब StoreWrapperप्रत्येक वैश्विक Storeउदाहरण के लिए एक प्रकार (और वैश्विक उदाहरण) बना सकता है , और हमेशा अपने आवरण उदाहरण के माध्यम से दुकानों तक पहुंच सकता है (इस प्रकार उपयोग करने के लिए आवश्यक टेम्पलेट मापदंडों के गोर विवरण के बारे में भूल जाता है Store)।


0

यह ऑब्जेक्ट लाइफ-टाइम मैनेजमेंट के बारे में है। मान लीजिए कि आपके सॉफ्टवेयर में सिंगलेट्स से अधिक है। और वे लोगगर सिंगलटन पर निर्भर हैं। अनुप्रयोग विनाश के दौरान, मान लें कि एक और सिंगलटन ऑब्जेक्ट अपने विनाश चरणों को लॉग करने के लिए लकड़हारे का उपयोग करता है। आपको यह गारंटी देनी होगी कि लकड़हारा आखिरी बार साफ किया जाना चाहिए। इसलिए, कृपया इस पेपर को भी देखें: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


0

मेरा कार्यान्वयन गैलिक के समान है। अंतर यह है कि मेरा कार्यान्वयन, साझा किए गए पॉइंटर्स को आवंटित मेमोरी को साफ करने की अनुमति देता है, जब तक कि एप्लिकेशन से बाहर निकलने तक मेमोरी पर पकड़ रखने का विरोध किया जाता है और स्थिर पॉइंटर्स को साफ किया जाता है।

#pragma once

#include <memory>

template<typename T>
class Singleton
{
private:
  static std::weak_ptr<T> _singleton;
public:
  static std::shared_ptr<T> singleton()
  {
    std::shared_ptr<T> singleton = _singleton.lock();
    if (!singleton) 
    {
      singleton.reset(new T());
      _singleton = singleton;
    }

    return singleton;
  }
};

template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;

0

आपका कोड सही है, सिवाय इसके कि आपने कक्षा के बाहर उदाहरण सूचक की घोषणा नहीं की । स्टैटिक वेरिएबल्स के अंदर के क्लास डिक्लेरेशन को C ++ में डिक्लेरेशन नहीं माना जाता है, हालाँकि अन्य भाषाओं जैसे C # या Java आदि में इसकी अनुमति है ।

class Singleton
{
   public:
       static Singleton* getInstance( );
   private:
       Singleton( );
       static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global

आपको पता होना चाहिए कि सिंगलटन उदाहरण को हमारे द्वारा मैन्युअल रूप से हटाने की आवश्यकता नहीं है । हमें पूरे कार्यक्रम में इसकी एक ही वस्तु की आवश्यकता है, इसलिए कार्यक्रम के निष्पादन के अंत में, यह स्वचालित रूप से निपटा जाएगा।


-1

पेपर जो ऊपर से जोड़ा गया था, दोहरी जाँच लॉकिंग की कमी के बारे में बताता है कि कंपाइलर ऑब्जेक्ट के लिए मेमोरी आवंटित कर सकता है और ऑब्जेक्ट के कंस्ट्रक्टर को बुलाए जाने से पहले आवंटित मेमोरी के पते पर एक पॉइंटर सेट कर सकता है। यह c ++ में काफी आसान है, हालांकि मेमोरी को मैन्युअल रूप से आवंटित करने के लिए एलाटेटर का उपयोग करना है, और फिर मेमोरी को आरंभीकृत करने के लिए एक निर्माण कॉल का उपयोग करें। इस मूल्यांकन का उपयोग करते हुए, डबल-चेक किया गया लॉकिंग ठीक काम करता है।


2
दुर्भाग्य से नहीं। यह वहाँ बाहर कुछ सबसे अच्छा सी ++ डेवलपर्स द्वारा बड़ी गहराई से चर्चा की गई है। C ++ 03 में डबल चेक किया गया लॉकिंग टूट गया है।
मार्टिन यॉर्क

-1
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

उदाहरण:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

-1

साधारण सिंगलटन क्लास, यह आपकी हेडर क्लास फाइल होनी चाहिए

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

अपने सिंगलटन को इस तरह एक्सेस करें:

sSingletonClass->Relocate(1, 2, 5);

-3

मुझे लगता है कि आपको एक स्थिर फ़ंक्शन लिखना चाहिए जिसमें आपकी स्थिर ऑब्जेक्ट हटा दी जाती है। जब आप अपना एप्लिकेशन बंद करने वाले हों तो आपको इस फ़ंक्शन को कॉल करना चाहिए। यह सुनिश्चित करेगा कि आपके पास मेमोरी लीकेज नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.