2013-07-17 5 views
31

मैं सिंगलेट्स के बारे में बहुत कुछ पढ़ रहा हूं, जब उन्हें उपयोग करना चाहिए और उनका उपयोग नहीं करना चाहिए, और उन्हें सुरक्षित रूप से कैसे कार्यान्वित किया जाए। मैं सी ++ 11 में लिख रहा हूँ, और के रूप में this question.एक सिंगलटन के मेयर्स के कार्यान्वयन वास्तव में सिंगलटन

इस कार्यान्वयन में देखा एक सिंगलटन के मेयर की आलसी प्रारंभ कार्यान्वयन भर में आ गए हैं, है:

static Singleton& instance() 
{ 
    static Singleton s; 
    return s; 
} 

मैं समझता हूँ कि यह कैसे धागा सुरक्षित दूसरे से है एसओ पर यहां प्रश्न, लेकिन जो मुझे समझ में नहीं आता है यह वास्तव में एक सिंगलटन पैटर्न है। मैं अन्य भाषाओं में एकमात्र लागू कर दिया है, और ये हमेशा Wikipedia से इस उदाहरण की तरह कुछ खत्म:

public class SingletonDemo { 
     private static volatile SingletonDemo instance = null; 

     private SingletonDemo() {  } 

     public static SingletonDemo getInstance() { 
       if (instance == null) { 
         synchronized (SingletonDemo .class){ 
           if (instance == null) { 
             instance = new SingletonDemo(); 
           } 
         } 
       } 
       return instance; 
     } 
} 

जब मैं इस दूसरे उदाहरण को देखो, यह बहुत सहज ज्ञान युक्त है कि यह कैसे एक सिंगलटन है, के बाद से वर्ग एक रखती है खुद के एक उदाहरण के संदर्भ में, और केवल उस घटना को वापस लौटाता है। हालांकि, पहले उदाहरण में, मुझे समझ में नहीं आता कि यह ऑब्जेक्ट के मौजूदा दो उदाहरणों को कैसे रोकता है। तो मेरे प्रश्न हैं:

  1. पहला कार्यान्वयन एक सिंगलटन पैटर्न को कैसे लागू करता है? मुझे लगता है कि इसे स्थिर खोजशब्द के साथ करना है, लेकिन मुझे आशा है कि कोई मुझे हुड के नीचे क्या हो रहा है गहराई से समझा सकता है।
  2. इन दो कार्यान्वयन शैलियों के बीच, एक दूसरे के ऊपर बेहतर है? पक्ष और विपक्ष क्या होते हैं?

किसी भी मदद के लिए धन्यवाद,

+6

सी ++ कोड, जिस तरह से आप इसका प्रतिनिधित्व करते हैं, उतना ही एक सिंगलटन नहीं है क्योंकि यह एक अच्छी तरह से प्रारंभिक वैश्विक वस्तु है। उनमें से किसी एक को रखने से आपको रोक नहीं है (एक और कार्य करें), और यह ठीक है। किसी को कुछ भी करने के लिए मजबूर करने में कोई बात नहीं है। यदि आप सिर्फ एक चाहते हैं, तो केवल एक ही बनाओ। –

+0

'स्थैतिक सिंगलटन एस; 'पहली बार फ़ंक्शन को इस चर कहा जाता है जिसे प्रारंभ किया जाएगा और गैर स्थैतिक चर के विपरीत यह कार्य समाप्त होने के बाद नष्ट नहीं किया जाएगा। तो जब आप इसे दोबारा कॉल करेंगे तो यह अभी भी अस्तित्व में रहेगा, इसलिए एकमात्र निर्देश जिसे दूसरे (तीसरे, चौथे, ...) समय पर निष्पादित किया जाएगा, वह वापस आ जाएगा। – Borgleader

+11

मुझे लगता है कि 'सिंगलटन' का निर्माता निजी है, इसलिए वास्तव में एक उदाहरण प्राप्त करने का एकमात्र तरीका 'instance()' फ़ंक्शन के माध्यम से है। –

उत्तर

38

यह एक सिंगलटन है क्योंकि static फ़ंक्शन स्थानीय के लिए संग्रहण अवधि का अर्थ है कि उस स्थानीय का केवल एक उदाहरण प्रोग्राम में मौजूद है।

हुड के अंतर्गत, यह बहुत ही मोटे तौर पर के बराबर होने का माना जा सकता है निम्नलिखित सी ++ 98 (और यहां तक ​​कि एक संकलक द्वारा इस तरह अस्पष्ट लागू किया जा सकता):

static bool __guard = false; 
static char __storage[sizeof(Singleton)]; // also align it 

Singleton& Instance() { 
    if (!__guard) { 
    __guard = true; 
    new (__storage) Singleton(); 
    } 
    return *reinterpret_cast<Singleton*>(__storage); 
} 

// called automatically when the process exits 
void __destruct() { 
    if (__guard) 
    reinterpret_cast<Singleton*>(__storage)->~Singleton(); 
} 

धागा सुरक्षा बिट्स बनाने यह थोड़ा और जटिल हो जाता है, लेकिन यह अनिवार्य रूप से वही बात है।

सी ++ 11 के लिए वास्तविक कार्यान्वयन को देखते हुए, प्रत्येक स्थिर (ऊपर बूलियन की तरह) के लिए गार्ड वैरिएबल है, जिसका उपयोग बाधाओं और धागे के लिए भी किया जाता है।

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

AMD64 -O1 पर AMD64 पर उबंटू का बजना 3.0 से instance के लिए विधानसभा (http://gcc.godbolt.org/ के सौजन्य से है:

instance():       # @instance() 
    pushq %rbp 
    movq %rsp, %rbp 
    movb guard variable for instance()::instance(%rip), %al 
    testb %al, %al 
    jne .LBB0_3 
    movl guard variable for instance()::instance, %edi 
    callq __cxa_guard_acquire 
    testl %eax, %eax 
    je .LBB0_3 
    movl instance()::instance, %edi 
    callq Singleton::Singleton() 
    movl guard variable for instance()::instance, %edi 
    callq __cxa_guard_release 
.LBB0_3: 
    movl instance()::instance, %eax 
    popq %rbp 
    ret 

आप देख सकते हैं यह एक वैश्विक गार्ड संदर्भ के लिए बजना के AMD64 उत्पादन को देखो यह देखने के लिए कि क्या प्रारंभिकता की आवश्यकता है, __cxa_guard_acquire का उपयोग करता है, फिर से प्रारंभिक परीक्षण करता है, और इसी तरह। वास्तव में एएमडी 64 असेंबली और Itanium ABI में निर्दिष्ट प्रतीकों/लेआउट को छोड़कर, विकिपीडिया से पोस्ट किए गए संस्करण की तरह लगभग हर तरह से।

ध्यान दें कि यदि आप उस परीक्षण को चलाते हैं तो आपको Singleton एक गैर-तुच्छ कन्स्ट्रक्टर देना चाहिए, इसलिए यह एक पीओडी नहीं है, अन्यथा अनुकूलक यह महसूस करेगा कि उस गार्ड/लॉकिंग कार्य को करने का कोई मतलब नहीं है।

+0

" // थ्रेड निकलने पर स्वचालित रूप से बुलाया जाता है "- क्या आपका मतलब है * प्रक्रिया निकलती है *? इस पैटर्न के पास टीएलएस के साथ कुछ लेना देना नहीं है, है ना? – kkm

+0

@kkm मेरा मतलब प्रक्रिया है, हाँ। –

+0

स्पष्टीकरण के लिए धन्यवाद! मैं उस से थोड़ा उलझन में था! :) – kkm

12
// Singleton.hpp 
class Singleton { 
public: 
    static Singleton& Instance() { 
     static Singleton S; 
     return S; 
    } 

private: 
    Singleton(); 
    ~Singleton(); 
}; 

इस कार्यान्वयन मेयर्स 'सिंगलटन के रूप में जाना जाता है। स्कॉट Meyers कहते हैं:

"यह दृष्टिकोण सी ++ के गारंटी नहीं है कि स्थानीय स्थिर वस्तुओं प्रारंभ कर रहे हैं जब वस्तु की परिभाषा पहले कि कार्य करने के लिए एक कॉल के दौरान का सामना करना पड़ा है पर आधारित है।" ... "बोनस के रूप में, यदि आप किसी गैर-स्थानीय स्थैतिक वस्तु को अनुकरण करने वाले फ़ंक्शन को कभी भी कॉल नहीं करते हैं, तो आप ऑब्जेक्ट को बनाने और नष्ट करने की लागत कभी भी नहीं लेते हैं।"

जब आप Singleton& s=Singleton::Instance() पहली बार वस्तु बन जाता है और करने के लिए Singleton::Instance() परिणाम एक ही वस्तु लौटाए जाने के साथ हर अगली कॉल पर कॉल करें। मुख्य मुद्दा:


एक और कार्यान्वयन भरोसेमंद टपकाया सिंगलटन कहा जाता है।

class Singleton { 
public: 
    static Singleton& Instance() { 
     if (I == nullptr) { I = new Singleton(); } 
     return *I; 
    } 

private: 
    Singleton(); 
    ~Singleton(); 

    static Singleton* I; 
}; 

// Singleton.cpp 
Singleton* Singleton::I = 0; 

दो मुद्दों:

  • लीक, जब तक आप एक रिलीज को लागू करने और यह (एक बार) कॉल करने के लिए सुनिश्चित करें कि
  • थ्रेड नहीं सुरक्षित
+3

लीकी सिंगलटन के लिए, क्यों नहीं 'स्थिर सिंगलटन और इंस्टेंस() {स्थिर सिंगलटन * I = नया सिंगलटन(); वापसी * ​​मैं; } '। –

+0

आपके कोड 'सिंगलटन एस = सिंगलटन :: इंस्टेंस()' में, क्या आप वास्तव में डिफ़ॉल्ट (गैर-निजी) प्रतिलिपि निर्माता के माध्यम से एक नया उदाहरण नहीं बना रहे हैं? (मैंने कल्पना की होगी कि सिंगलटन और एस = सिंगलटन :: इंस्टेंस() 'बदले में) – Boris

+0

हां, ज़ाहिर है, धन्यवाद – 4pie0

संबंधित मुद्दे