2013-02-25 133 views
14

मेरे विशिष्ट प्रश्न है कि जब सी ++ में एक singleton class को लागू करने, वहाँ है प्रदर्शन, पक्ष मुद्दों या कुछ और के बारे में कोड नीचे दोनों के बीच कोई बड़ा मतभेद:ढेर/बनाम सी ++ सिंगलटन वर्ग उदाहरण के लिए स्थिर स्मृति आवंटन गतिशील

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // allocating on heap 
     static singleton* pInstance = new singleton(); 
     return *pInstance; 
    } 
    // ... 
}; 

और इस:

class singleton 
{ 
    // ... 
    static singleton& getInstance() 
    { 
     // using static variable 
     static singleton instance; 
     return instance; 
    } 
    // ... 
}; 


(ध्यान दें कि ढेर आधारित कार्यान्वयन में अपसंदर्भन प्रदर्शन प्रभावित नहीं होना चाहिए, AFAIK के रूप में वहाँ कोई अतिरिक्त मशीन कोड जेनरेट किया गया है dereferencing के लिए। यह वाक्य रचना का केवल एक मामला संकेत से अलग करने लगता है)

अद्यतन:।

मैं दिलचस्प जवाब और टिप्पणियों जो मैं उन्हें यहाँ संक्षेप में प्रस्तुत करने की कोशिश मिल गया है। (विस्तृत जवाब पढ़ना रुचि रखने वालों के लिए सिफारिश की है।):

  • सिंगलटन स्थिर स्थानीय चर का उपयोग कर में, वर्ग नाशक स्वचालित रूप से प्रक्रिया समाप्ति पर शुरू हो जाती है, गतिशील आवंटन मामले में, आप है, जबकि कुछ समय पर वस्तु विनाश का प्रबंधन करने के लिए, उदाहरण के लिए स्मार्ट संकेत का उपयोग करके:
static singleton& getInstance() { 
     static std::auto_ptr<singleton> instance (new singleton()); 
     return *instance.get(); 
    } 
  • सिंगलटन गतिशील आवंटन का उपयोग कर ("lazier" स्थिर सिंगलटन चर से, बाद में मामले में है, सिंगलटन वस्तु के लिए आवश्यक स्मृति है हमेशा ?) प्रक्रिया स्टार्ट-अप पर आरक्षित (प्रोग्राम लोड करने के लिए आवश्यक पूरी मेमोरी के हिस्से के रूप में) और केवल सिंगलटन कन्स्ट्रक्टर को कॉल करने के लिए getInstance() कॉल-टाइम पर स्थगित कर दिया गया है। इससे कोई फर्क नहीं पड़ता कि sizeof(singleton) बड़ा है।

  • दोनों सी ++ 11 में थ्रेड-सुरक्षित हैं। लेकिन सी ++ के पुराने संस्करणों के साथ, यह कार्यान्वयन-विशिष्ट है।

  • गतिशील आवंटन मामले जबकि स्थिर सिंगलटन वस्तु मामले में, वस्तु के प्रत्यक्ष पता निर्धारित किया जाता है और संकलन समय पर हार्ड कोड किए गए सिंगलटन वस्तु का उपयोग करने के अविवेक से एक स्तर का उपयोग करता है।


पी.एस .: मैं शब्दावली मैं मूल पोस्टिंग में @ TonyD के जवाब के अनुसार इस्तेमाल किया था को सही है।

+0

आप दो के लिए उत्पन्न विधानसभा की तुलना में है? –

+0

नहीं। यदि आप दो अलग-अलग कार्यान्वयन के लिए जेनरेटेड असेंबली का मतलब रखते हैं, तो वे स्पष्ट रूप से अलग हैं, क्योंकि एक ढेर पर आवंटित होता है और कोई लोड/कॉल टाइम प्रारंभ करता है। यदि आपका मतलब है कि डिफ्रेंसिंग के लिए जेनरेटेड असेंबली, नहीं, तो मैंने तुलना नहीं की है। मुझे बस ऐसा लगता है। –

उत्तर

7
  • new संस्करण जाहिर है, रन-टाइम में स्मृति को आबंटित करने की जरूरत है, जबकि गैर-सूचक संस्करण स्मृति संकलन समय पर आवंटित (लेकिन दोनों एक ही निर्माण करने की जरूरत है)

  • new संस्करण कार्यक्रम समाप्ति पर वस्तु नाशक आह्वान नहीं होगा, लेकिन गैर new संस्करण होगा: आप एक स्मार्ट सूचक इस्तेमाल कर सकते हैं सही करने के लिए इस

    • आपको सावधान रहने की जरूरत है कि कुछ स्थिर/नाम स्थान-गुंजाइश वस्तु की विनाशकर्ता अपने सिंगलटन आह्वान नहीं है के बाद अपनी स्थिर स्थानीय उदाहरण नाशक समाप्त हो गया है ... अगर आप इस बारे में चिंतित हैं, तो आप शायद सिंगलटन जीवन काल के बारे में थोड़ा और अधिक पढ़ सकते हैं और उन्हें प्रबंधन के लिए दृष्टिकोण चाहिए। एंड्री अलेक्जेंड्रेस्कू के आधुनिक सी ++ डिजाइन में एक बहुत ही पठनीय उपचार है।
  • सी ++ 03 के तहत, यह कार्यान्वयन-परिभाषित है कि या तो धागा सुरक्षित होगा या नहीं। (मेरा मानना ​​है कि जीसीसी का होना जरूरी है, जबकि विजुअल स्टूडियो की पुष्टि/सही की पुष्टि करने के लिए अनुबंध नहीं हैं।)

  • सी ++ 11 के तहत, यह सुरक्षित है: 6.7.4 "अगर नियंत्रण एक साथ घोषणापत्र के दौरान घोषणा में प्रवेश करता है आरंभ किया जा रहा है, समवर्ती निष्पादन प्रारंभिक समापन के लिए इंतजार करेगा। " (सैन्स रिकर्सन)।

चर्चा फिर से संकलन समय बनाम रन-टाइम आवंटन & initialisation

तरह से आप अपने सारांश और कुछ टिप्पणियां शब्दों में किया है से, मुझे लगता है कि आप पूरी तरह से एक सूक्ष्म पहलू समझ नहीं रहे संकलन: - a, b और c - विभिन्न कार्यों में आवंटन और स्थैतिक चर की initialisation ....

अपने कार्यक्रम 3 स्थानीय स्थिर 32-बिट int रों है कहो की आर के एक द्विआधारी कि 3x32-बिट = उन स्टैटिक्स के लिए स्मृति के 12 बाइट्स छोड़ने के लिए ओएस लोडर बताता संकलित करने के लिए की संभावना है। संकलक का फैसला करता है क्या वे चर में से प्रत्येक पर है ऑफसेट: यह 1008 में डेटा खंड में 1000 हेक्स, 1004 में b, और c ऑफसेट पर a डाल सकता है जब कार्यक्रम कार्यान्वित करता है, ओएस लोडर के लिए स्मृति को आबंटित करने की जरूरत नहीं है प्रत्येक अलग - इसके बारे में सब जानता है 12 बाइट्स है, जो यह या 0-आरंभ करने के लिए विशेष रूप से नहीं कहा हो सकता है हो सकता है किया गया की कुल है, लेकिन यह दूसरे से स्मृति सामग्री बचे नहीं देख सकते हैं वैसे भी ऐसा करने के लिए प्रक्रिया सुनिश्चित करने के लिए कर सकते हैं उपयोगकर्ता के कार्यक्रम। कार्यक्रम में मशीन कोड निर्देश होगा आम तौर पर कड़ी मेहनत से कोड ऑफसेट 1000, 1004, 1008 पहुंच के लिए a, b और c को - तो उन पतों का कोई आवंटन रन-टाइम में की जरूरत है।

गतिशील स्मृति आवंटन कि में अलग है संकेत (माना p_a, p_b, p_c) संकलन समय पर पते के रूप में ही वर्णित दिया जाएगा, लेकिन इसके साथ ही:

  • उठाई-टू स्मृति (a से प्रत्येक , b और c) रन-टाइम पर मिलते हैं (आमतौर पर जब स्थिर कार्य पहले निष्पादित होता है लेकिन संकलक को अन्य उत्तरों पर मेरी टिप्पणी के अनुसार पहले ऐसा करने की अनुमति दी जाती है), और
    • यदि बहुत कम स्मृति कर्सर है सफलतापूर्वक गतिशील आवंटन के लिए ऑपरेटिंग सिस्टम द्वारा प्रक्रिया को दिया गया है, तो प्रोग्राम लाइब्रेरी ओएस से अधिक स्मृति के लिए पूछेगी (उदा। का उपयोग कर sbreak()) - जो ओएस आम तौर पर सुरक्षा कारणों से
    • गतिशील a, b और c से प्रत्येक के लिए आवंटित पतों संकेत p_a, p_b और p_c में वापस कॉपी किया जा करने के लिए है के लिए मिटा देगा।

इस गतिशील दृष्टिकोण स्पष्ट रूप से और अधिक जटिल है।

+0

अच्छा अंक। "संकलन समय पर स्मृति आवंटन" के द्वारा, क्या आपका मतलब है कि आवश्यक मेमोरी स्पेस लिंक- और लोड-टाइम पर आरक्षित है, लेकिन प्रारंभिक कार्य फ़ंक्शन कॉल-टाइम पर स्थगित कर दिया गया है? (अन्यथा अपना पहला बिंदु गलत लगता है) –

+0

मैं सिर्फ देखा है [अपने उद्धरण] (http://stackoverflow.com/questions/15062767/heap-dynamic-vs-static-memory-allocation-for-c-singleton-class -इंसेन्स/15063036 # टिप्पणी 21179656_15062905) सी ++ 11 6.7.4 से। लेकिन सी ++ 03 या पुराने संस्करणों के बारे में क्या? –

+0

@MassoodKhaari: पुन आवंटन: हाँ, निर्णय फिर स्मृति (राशि की जरूरत है, खंड, ऑफसेट) के लिए स्टैटिक्स संकलन समय पर बना रहे हैं और बाइनरी छवि इसके बारे में पर्याप्त संकेत देगा (जैसे स्मृति क्षेत्र का कुल आकार) के लिए ओएस लोडर के लिए स्मृति को अलग रखें। पुन: संक्षेप - मेरे उत्तर के अनुसार - यह कार्यान्वयन परिभाषित किया गया है (यदि बिल्कुल) ... सी ++ 03 मानक ने धागे का उल्लेख नहीं किया है, इसलिए यह तय करने के लिए कार्यान्वयन पर निर्भर था कि उन्हें और कैसे समर्थन किया जाए। –

2

मुख्य अंतर यह है कि स्थानीय static का उपयोग करके ऑब्जेक्ट को प्रोग्राम बंद करते समय नष्ट कर दिया जाएगा, इसके बजाय ढेर-आवंटित वस्तुओं को नष्ट किए बिना छोड़ दिया जाएगा।

ध्यान दें कि यदि आप किसी फ़ंक्शन के अंदर एक स्थैतिक चर घोषित करते हैं तो सी ++ में इसे पहली बार शुरू किया जाएगा, जब आप प्रोग्राम शुरू नहीं करेंगे (जैसे वैश्विक स्थैतिक अवधि चर के लिए ऐसा होता है)।

सामान्य वर्षों में मैंने आलसी प्रारंभिकरण को स्पष्ट नियंत्रित प्रारंभिकरण के उपयोग से स्विच करने के लिए स्विच किया क्योंकि प्रोग्राम स्टार्टअप और शट डाउन नाजुक चरण हैं और डीबग करना काफी कठिन है। यदि आपकी कक्षा कुछ भी जटिल नहीं कर रही है और बस असफल नहीं हो सकती है (उदा।यह सिर्फ एक रजिस्ट्री है) तो आलसी शुरुआत भी ठीक है ... अन्यथा नियंत्रण में रहना आपको बहुत सारी समस्याओं को बचाएगा।

एक प्रोग्राम है जो main के पहले अनुदेश प्रवेश करने से पहले या main के अंतिम अनुदेश को क्रियान्वित करने के बाद दुर्घटनाओं डिबग करने के लिए कठिन है।

सिंगलेट्स के आलसी निर्माण का उपयोग करने की एक और समस्या यह है कि यदि आपका कोड मल्टीथ्रेड है तो आपको एक ही समय में सिंगलटन को प्रारंभ करने वाले समवर्ती धागे होने के जोखिम पर ध्यान देना होगा। प्रारंभिककरण और एक थ्रेड संदर्भ में शट डाउन करना आसान है।

+0

हां, मैं बल्कि सरल वस्तुओं के लिए सिंगलेट का उपयोग कर रहा हूं। लेकिन यह दिलचस्प लगता है; क्या आप "स्पष्ट नियंत्रित प्रारंभिकरण" को लागू करने के कुछ तरीकों का उल्लेख कर सकते हैं? –

+0

और विनाश के मुद्दे को इंगित करने के लिए अच्छा लगा। मैंने यह नहीं देखा था। इसलिए इस मामले में, स्थिर प्रारंभिक बेहतर विकल्प लगता है। –

+0

"यह पहली बार जब आप गुंजाइश प्रवेश प्रारंभ हो जाएगा" - कभी कभी/C++ 11 6.7.4: "एक कार्यान्वयन एक ही के तहत स्थिर या धागा भंडारण अवधि के साथ अन्य ब्लॉक गुंजाइश चर के प्रारंभिक प्रारंभ प्रदर्शन करने के लिए अनुमति दी है । की स्थिति है कि एक कार्यान्वयन स्थिर नाम स्थान गुंजाइश (3.6.2) में स्थिर या धागा भंडारण अवधि के साथ एक चर प्रारंभ करने की अनुमति दी है अन्यथा इस तरह के एक चर पहली बार नियंत्रण अपनी घोषणा के माध्यम से गुजरता आरंभ नहीं हो जाता है, ऐसे में एक चर पर प्रारंभ माना जाता है इसकी शुरुआत के समापन। " –