std :: shared_ptr अंतिम उपाय के रूप में?


59

मैं सिर्फ "गोइंग नेटिव 2012" स्ट्रीम देख रहा था और मैंने इस बारे में चर्चा की std::shared_ptr। मैं बज्ने के कुछ नकारात्मक विचारों std::shared_ptrऔर उनकी टिप्पणी को सुनकर थोड़ा आश्चर्यचकित था कि इसका उपयोग "अंतिम उपाय" के रूप में किया जाना चाहिए जब किसी वस्तु का जीवन-काल अनिश्चित हो (जो मैं मानता हूं, उसके अनुसार, अक्सर मामला होना चाहिए)।

क्या कोई इसे और गहराई से समझाने की परवाह करेगा? हम कैसे सुरक्षित रूपstd::shared_ptr से वस्तु जीवन-समय का प्रबंधन कर सकते हैं ?


8
पॉइंटर्स का उपयोग नहीं कर रहे हैं? वस्तु का एक अलग मालिक होने के नाते, जो जीवन भर का प्रबंधन करता है?
बो पर्सन

2
स्पष्ट रूप से साझा किए गए डेटा के बारे में क्या? पॉइंटर्स का उपयोग न करना कठिन है। इसके अलावा std :: shared_pointer उस मामले में गंदा "जीवनकाल का प्रबंधन" करेगा
कामिल क्लीमेक

6
क्या आपने प्रस्तुत सलाह के बारे में कम सुनने और उस सलाह के पीछे तर्क पर अधिक विचार किया है? वह बहुत अच्छी तरह से इस तरह की प्रणाली की सलाह देता है जिसमें इस तरह की सलाह काम करेगी।
निकोल बोल 15

@ एनकोलबोल: मैंने सलाह और तर्क सुने लेकिन जाहिर है मुझे नहीं लगा कि मैं इसे अच्छी तरह से समझ पाया हूं।
रोनग

वह किस समय "अंतिम उपाय" कहता है? 36 मिनट ( चैनल 9. msdn.com/Events/GoingNative/GoingNative-2012/… ) पर बिट को देखते हुए वह कहता है कि वह पॉइंटर्स के उपयोग से सावधान है, लेकिन उसका मतलब सामान्य रूप से पॉइंटर्स है, न कि केवल साझा किया गया और यूनिक_प्रेट लेकिन यहां तक ​​कि ' नियमित 'सूचक। उनका तात्पर्य है कि वस्तुओं को खुद (और नए के साथ आवंटित वस्तुओं के लिए संकेत नहीं) को प्राथमिकता दी जानी चाहिए। क्या आप बाद में प्रस्तुति के बारे में सोच रहे थे?
फराप

जवाबों:


55

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

इसे देखते हुए, स्वचालित भंडारण अवधि वाली वस्तुओं का उपयोग करना और "मूल्य" उप-वस्तुओं का होना बेहतर है। इसे विफल करना, unique_ptrहोने के साथ एक अच्छा विकल्प हो सकता है shared_ptr- यदि अंतिम उपाय नहीं है - वांछनीय उपकरणों की सूची से किसी तरह नीचे।


5
+1 यह बताने के लिए कि यह समस्या स्वयं तकनीकी (साझा स्वामित्व) नहीं है, लेकिन हमारे लिए जो कठिनाइयाँ हैं, वे केवल मनुष्य के लिए हैं, जिन्हें तब समझना होगा कि क्या हो रहा है।
Matthieu एम।

हालांकि, इस तरह के दृष्टिकोण को लेने से प्रोग्रामर की अधिकांश गैर-तुच्छ ओओपी कक्षाओं (गैर-कॉपीबिलिटी के कारण) पर संगामिति प्रोग्रामिंग पैटर्न लागू करने की क्षमता सीमित हो जाएगी। यह मुद्दा "गोइंग नेटिव 2013" में उठाया गया है।
रवांग १२'१३ को १६:०५

48

बेहतर जीवन की चाह के लिए, ब्रेज़ेन में रहने वाली दुनिया बहुत ... शैक्षणिक है। यदि आपके कोड को डिज़ाइन और संरचित किया जा सकता है जैसे कि वस्तुओं में बहुत ही जानबूझकर संबंधपरक पदानुक्रम होते हैं, जैसे कि स्वामित्व संबंध कठोर और अनियंत्रित होते हैं, तो कोड एक दिशा में बहता है (उच्च-स्तर से निम्न-स्तर तक), और ऑब्जेक्ट केवल उन निम्न में बात करते हैं पदानुक्रम, तो आप के लिए बहुत जरूरत नहीं मिलेगी shared_ptr। यह कुछ ऐसा है जो आप उन दुर्लभ अवसरों पर उपयोग करते हैं जहां किसी को नियमों को तोड़ना पड़ता है। लेकिन अन्यथा, आप केवल vectorएस या अन्य डेटा संरचनाओं में सब कुछ चिपका सकते हैं जो मूल्य शब्दार्थ का उपयोग करते हैं, और unique_ptrउन चीजों के लिए जो आपको अकेले आवंटित करना है।

जबकि वह रहने के लिए एक महान दुनिया है, यह वह नहीं है जो आपको हर समय मिलता है। यदि आप अपने कोड को उस तरह से व्यवस्थित नहीं कर सकते हैं , क्योंकि सिस्टम का डिज़ाइन जो आप बनाने की कोशिश कर रहे हैं, इसका मतलब है कि यह असंभव है (या बस गहराई से अप्रिय), तो आप अपने आप को वस्तुओं के साझा स्वामित्व की आवश्यकता को खोजने जा रहे हैं। ।

इस तरह की व्यवस्था में, नग्न बिंदुओं को पकड़ना ... बिल्कुल खतरनाक नहीं है, लेकिन यह सवाल उठाता है। इसके बारे shared_ptrमें महान बात यह है कि यह वस्तु के जीवनकाल के बारे में उचित वाक्यविन्यास प्रदान करता है । क्या इसे तोड़ा जा सकता है? बेशक। लेकिन लोग const_castबातें भी कर सकते हैं; बुनियादी देखभाल और खिलाने के shared_ptrलिए आवंटित वस्तुओं के लिए जीवन की उचित गुणवत्ता प्रदान करनी चाहिए जिन्हें स्वामित्व साझा किया जाना चाहिए।

फिर, वहाँ weak_ptrएस, जो अनुपस्थिति में इस्तेमाल नहीं किया जा सकता है shared_ptr। यदि आपका सिस्टम कठोर रूप से संरचित है, तो आप किसी ऑब्जेक्ट को एक नग्न पॉइंटर को स्टोर कर सकते हैं, इस ज्ञान में सुरक्षित है कि एप्लिकेशन की संरचना यह सुनिश्चित करती है कि ऑब्जेक्ट आपको इंगित करेगा। आप एक फ़ंक्शन को कॉल कर सकते हैं जो कुछ आंतरिक या बाह्य मान के लिए एक पॉइंटर लौटाता है (उदाहरण के लिए एक्स नामक ऑब्जेक्ट ढूंढें)। ठीक से संरचित कोड में, वह फ़ंक्शन केवल आपके लिए उपलब्ध होगा यदि ऑब्जेक्ट का जीवनकाल आपके खुद से अधिक होने की गारंटी हो; इस प्रकार, उस नग्न सूचक को अपनी वस्तु में संग्रहीत करना ठीक है।

चूंकि उस कठोरता को वास्तविक प्रणालियों में प्राप्त करना हमेशा संभव नहीं होता है, इसलिए आपको जीवनकाल सुनिश्चित करने के लिए किसी तरह की आवश्यकता होती है । कभी-कभी, आपको पूर्ण स्वामित्व की आवश्यकता नहीं होती है; कभी-कभी, आपको केवल यह जानने में सक्षम होना चाहिए कि सूचक खराब या अच्छा कब है। यही कारण है कि जहां है weak_ptrमें आता है। ऐसे मामले मैं किया गया है हो सकता है एक का इस्तेमाल किया है unique_ptrया boost::scoped_ptrहै, लेकिन मैं एक का उपयोग करने के लिए किया था shared_ptr, क्योंकि मैं विशेष रूप से किसी को एक "अस्थिर" सूचक देने के लिए की जरूरत है। एक सूचक जो जीवन भर अनिश्चित था, और वे उस सूचक को नष्ट होने पर क्वेरी कर सकते थे।

दुनिया की स्थिति के अनिश्चित होने पर जीवित रहने का एक सुरक्षित तरीका।

पॉइंटर को पाने के लिए कुछ फंक्शन कॉल द्वारा किया जा सकता था, इसके बजाय weak_ptr? हां, लेकिन यह आसानी से टूट सकता है। एक फ़ंक्शन जो एक नग्न पॉइंटर लौटाता है, उसके पास सिंटैक्टिक रूप से यह सुझाव देने का कोई तरीका नहीं है कि उपयोगकर्ता कुछ ऐसा नहीं करता है जो पॉइंटर को दीर्घकालिक बनाता है। किसी को लौटाना shared_ptrभी किसी के लिए इसे आसानी से संग्रहीत करना आसान बनाता है और संभवतः किसी वस्तु के जीवन-काल को लम्बा खींचता है। एक weak_ptrजोरदार रिटर्निंग से पता चलता है कि shared_ptrआपके द्वारा प्राप्त किया lockजा रहा भंडारण एक ... संदिग्ध विचार है। यह आपको ऐसा करने से नहीं रोकेगा, लेकिन C ++ में कुछ भी आपको कोड तोड़ने से नहीं रोकता है। weak_ptrप्राकृतिक काम करने से कुछ न्यूनतम प्रतिरोध प्रदान करता है।

अब, यह कहना है कि नहीं है shared_ptrनहीं किया जा सकता overused ; यह निश्चित रूप से कर सकते हैं। विशेष रूप से पूर्व- unique_ptr, ऐसे कई मामले थे जहां मैंने सिर्फ boost::shared_ptrइसलिए इस्तेमाल किया क्योंकि मुझे एक आरएआई पॉइंटर पास करने की आवश्यकता थी या इसे एक सूची में डाल दिया था। चाल शब्दार्थ के बिना और unique_ptr, boost::shared_ptrएकमात्र वास्तविक समाधान था।

और आप इसे उन जगहों पर उपयोग कर सकते हैं जहां यह काफी अनावश्यक है। जैसा कि ऊपर कहा गया है, उचित कोड संरचना के कुछ उपयोगों की आवश्यकता को समाप्त कर सकता है shared_ptr। लेकिन अगर आपके सिस्टम को इस तरह से संरचित नहीं किया जा सकता है और फिर भी इसे करने की आवश्यकता है, तो यह shared_ptrमहत्वपूर्ण उपयोग होगा।


4
+1: बढ़ावा देने के लिए उदाहरण देखें :: asio। मुझे लगता है कि यह विचार कई क्षेत्रों तक फैला हुआ है, आप संकलन समय पर नहीं जान सकते हैं कि कौन सा यूआई विजेट या अतुल्यकालिक कॉल किसी वस्तु को त्यागने के लिए अंतिम है, और शेयर्ड_एप्ट्र के साथ आपको जानने की आवश्यकता नहीं है। यह स्पष्ट रूप से हर स्थिति में लागू नहीं होता है, टूल-बॉक्स में सिर्फ एक और (बहुत उपयोगी) उपकरण।
गाय सिरटन

3
थोड़ी देर की टिप्पणी; shared_ptrसिस्टम के लिए बहुत अच्छा है जहाँ c ++ स्क्रिप्टिंग लैंग्वेज जैसे कि अजगर के साथ एकीकृत है। boost::pythonसी ++ और अजगर पक्ष पर संदर्भ गिनती का उपयोग करना , बहुत सहयोग करता है; सी ++ से किसी भी वस्तु को अभी भी अजगर में रखा जा सकता है और यह मर नहीं जाएगा।
यूडोक्सोस

1
सिर्फ संदर्भ के लिए मेरी समझ न तो WebKit है और न ही क्रोमियम का उपयोग है shared_ptr। दोनों अपने-अपने कार्यान्वयन का उपयोग करते हैं intrusive_ptr। मैं केवल इसलिए
उठाता हूं

1
@gman: मुझे आपकी टिप्पणी बहुत भ्रामक लगती है, क्योंकि स्ट्रॉस्ट्रुप की आपत्ति shared_ptrसमान रूप से लागू होती है intrusive_ptr: वह साझा स्वामित्व की पूरी अवधारणा पर आपत्ति कर रहा है, अवधारणा की किसी विशिष्ट वर्तनी पर नहीं। तो इस सवाल के प्रयोजनों के लिए, वे बड़े अनुप्रयोगों के दो वास्तविक दुनिया उदाहरण हैं जो उपयोग करते हैं shared_ptr। (और, क्या अधिक है, वे प्रदर्शित करते हैं कि shared_ptrतब भी उपयोगी है जब यह सक्षम नहीं होता है weak_ptr।)
बर्बाद करें

1
एफडब्ल्यूआईडब्ल्यू, यह दावा करने के लिए कि बज्ने अकादमिक दुनिया में रह रही है: मेरे सभी विशुद्ध रूप से औद्योगिक कैरियर में (जिसमें एक जी 20 स्टॉक एक्सचेंज का सह-आर्किटेक्चरिंग और पूरी तरह से 500K-खिलाड़ी MOG का आर्किटेक्चर शामिल था) मैंने केवल 3 मामलों को देखा है जिन्हें हमें वास्तव में ज़रूरत थी साझा स्वामित्व। मैं यहां बज्ने के साथ 200% हूं।
नहीं-बग्स हरे

37

मुझे विश्वास नहीं होता कि मैंने कभी इस्तेमाल किया है std::shared_ptr

अधिकांश समय, एक वस्तु कुछ संग्रह से जुड़ी होती है, जिसमें से वह अपने पूरे जीवनकाल के लिए होती है। जिस स्थिति में आप किसी वस्तु या स्वचालित चर के सदस्य होने के नाते whatever_collection<o_type>या whatever_collection<std::unique_ptr<o_type>>उस संग्रह का उपयोग कर सकते हैं । बेशक, अगर आपको वस्तुओं की एक गतिशील संख्या की आवश्यकता नहीं है, तो आप निश्चित आकार के एक स्वचालित सरणी का उपयोग कर सकते हैं।

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


निकोल बोलस ने टिप्पणी की कि "यदि कोई वस्तु नग्न सूचक पर रहती है और वह वस्तु मर जाती है ... उफ़।" और "ऑब्जेक्ट्स को यह सुनिश्चित करने की आवश्यकता है कि ऑब्जेक्ट उस ऑब्जेक्ट के जीवन के माध्यम से रहता है। केवल वही shared_ptrकर सकता है।"

मैं वह तर्क नहीं खरीदता। कम से कम shared_ptrयह समस्या हल नहीं होती है। व्हाट अबाउट:

  • यदि कुछ हैश टेबल एक ऑब्जेक्ट पर रखता है और उस ऑब्जेक्ट का हैशकोड बदलता है ... उफ़।
  • अगर कुछ फ़ंक्शन वेक्टर को पुनरावृत्त कर रहा है और उस वेक्टर में एक तत्व डाला जाता है ... उफ़।

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

ऑब्जेक्ट "डाई" नहीं करते हैं, कोड का कुछ टुकड़ा उन्हें नष्ट कर देता है। और shared_ptrकॉल कॉन्ट्रैक्ट का पता लगाने के बजाय समस्या पर फेंकना एक झूठी सुरक्षा है।


17
@ क्रोनग: मुझे संदेह है कि आपने इसका उपयोग करना शुरू कर दिया है जहां एक कच्चा सूचक बेहतर होता, क्योंकि "कच्चे पॉइंटर्स खराब होते हैं"। लेकिन कच्चे संकेत खराब नहीं हैं । केवल एक ऑब्जेक्ट के लिए पहला, मालिक को सूचक बनाना एक कच्चा सूचक खराब है, क्योंकि तब आपको मैन्युअल रूप से मेमोरी का प्रबंधन करना होगा, जो अपवादों की उपस्थिति में गैर-तुच्छ है। लेकिन हैंडल या पुनरावृत्तियों के रूप में कच्चे पॉइंटर्स का उपयोग करना ठीक है।
बेन वोइगट

4
@BenVoigt: बेशक, नग्न बिंदुओं के आसपास से गुजरने में कठिनाई यह है कि आप वस्तुओं के जीवनकाल को नहीं जानते हैं। यदि कोई वस्तु नग्न सूचक पर रहती है और वह वस्तु मर जाती है ... उफ़। यह बिल्कुल उसी तरह की बात है shared_ptrऔर इससे weak_ptrबचने के लिए डिजाइन किया गया था। ब्रेज़ने एक ऐसी दुनिया में रहने की कोशिश करता है जो सब कुछ एक अच्छा, स्पष्ट जीवनकाल और सब कुछ उसी के आसपास बनाया गया हो। और अगर तुम उस दुनिया का निर्माण कर सकते हो, महान। लेकिन असल दुनिया में ऐसा नहीं है। वस्तुओं को यह सुनिश्चित करने की आवश्यकता है कि वस्तु उस वस्तु के जीवन के माध्यम से रहती है। केवल वही shared_ptrकर सकता है।
निकोल बोल्स

5
@ नाइकोलोलस: यह झूठी सुरक्षा है। यदि किसी फ़ंक्शन का कॉलर सामान्य गारंटी प्रदान नहीं कर रहा है: "फ़ंक्शन कॉल के दौरान किसी भी बाहरी पार्टी द्वारा इस ऑब्जेक्ट को नहीं छुआ जाएगा" तो दोनों को इस बात पर सहमत होने की आवश्यकता है कि किस प्रकार के बाहरी संशोधनों की अनुमति है। shared_ptrकेवल एक विशिष्ट संशोधन के बाहर शमन करता है, और सबसे आम भी नहीं है। यदि यह फ़ंक्शन कॉल अनुबंध अन्यथा निर्दिष्ट करता है, तो यह सुनिश्चित करने के लिए ऑब्जेक्ट की ज़िम्मेदारी सही नहीं है।
बेन Voigt

6
@ निकोलबोल: यदि कोई फ़ंक्शन ऑब्जेक्ट बनाता है और पॉइंटर से लौटाता है, तो यह ए होना चाहिए, यह unique_ptrव्यक्त करते हुए कि ऑब्जेक्ट में केवल एक पॉइंटर मौजूद है और इसका स्वामित्व है।
बेन वोइगट

6
@ नाइकोल: यदि यह किसी संग्रह में एक संकेतक को देख रहा है, तो संभवतः उस संग्रह में जो भी पॉइंटर प्रकार है, या एक कच्चे पॉइंटर का उपयोग करना चाहिए, यदि संग्रह मान रखता है। यदि यह एक ऑब्जेक्ट बना रहा है और कॉलर ए चाहता है shared_ptr, तो उसे अभी भी ए वापस करना चाहिए unique_ptr। से रूपांतरण unique_ptrकरने के लिए shared_ptrआसान है, लेकिन रिवर्स तार्किक असंभव है।
बेन Voigt

16

मैं पूर्ण शब्दों में नहीं सोचना पसंद करता हूं (जैसे "अंतिम उपाय") लेकिन समस्या डोमेन के सापेक्ष।

C ++ जीवनकाल को प्रबंधित करने के लिए कई विभिन्न तरीकों की पेशकश कर सकता है। उनमें से कुछ वस्तुओं को स्टैक-चालित तरीके से फिर से कंडोम करने की कोशिश करते हैं। कुछ अन्य इस सीमा से बचने की कोशिश करते हैं। उनमें से कुछ "शाब्दिक" हैं, कुछ अन्य सन्निकटन हैं।

वास्तव में आप कर सकते हैं:

  1. शुद्ध मूल्य शब्दार्थ का उपयोग करें । अपेक्षाकृत छोटी वस्तुओं के लिए काम करता है जहां महत्वपूर्ण हैं "मूल्य" और "पहचान" नहीं, जहां आप यह मान सकते हैं कि दो Personएक nameही व्यक्ति एक ही हैं (बेहतर: एक ही व्यक्ति के दो प्रतिनिधित्व )। जीवनकाल मशीन स्टैक द्वारा दिया जाता है, अंत -सांस्कृतिक रूप से- कार्यक्रम के लिए कोई फर्क नहीं पड़ता (क्योंकि एक व्यक्ति यह नाम है , कोई फर्क नहीं पड़ता कि Personयह क्या है)
  2. स्टैक आबंटित ऑब्जेक्ट्स , और संबंधित संदर्भ या पॉइंटर्स का उपयोग करें: बहुरूपता की अनुमति देता है, और जीवन भर वस्तु को अनुदान देता है। "स्मार्ट पॉइंटर्स" की कोई आवश्यकता नहीं है, क्योंकि आप यह सुनिश्चित करते हैं कि कोई भी वस्तु संरचनाओं द्वारा "इंगित" नहीं की जा सकती है जो कि वे उस ऑब्जेक्ट की तुलना में लंबे समय तक स्टैक में छोड़ते हैं जो पहले इंगित करते हैं (फिर ऑब्जेक्ट का निर्माण करते हैं, फिर इसे संदर्भित करते हैं)।
  3. स्टैक प्रबंधित हीप आवंटित वस्तुओं का उपयोग करें : यह वही है जो std :: वेक्टर और सभी कंटेनर करते हैं, और वाट std::unique_ptrकरता है (आप इसे आकार 1 के साथ वेक्टर के रूप में सोच सकते हैं)। फिर से, आप स्वीकार करते हैं कि जिस डेटा संरचना को वे संदर्भित करते हैं, उससे पहले (और बाद में अपना अस्तित्व समाप्त) वस्तु का अस्तित्व होना शुरू हो जाता है।

इस mehtods की कमजोरी यह है कि ऑब्जेक्ट प्रकार और मात्रा जहाँ वे बनाए जाते हैं, के संबंध में गहरे स्टैक स्तर कॉल के निष्पादन के दौरान भिन्न नहीं हो सकते हैं। यह सभी तकनीकें उन सभी परिस्थितियों में अपनी ताकत को "विफल" करती हैं जहां निर्माण और हटाने का कार्य उपयोगकर्ता की गतिविधियों का परिणाम है, ताकि ऑब्जेक्ट के रनटाइम-प्रकार का संकलन-समय ज्ञात न हो और वस्तुओं का जिक्र करते हुए ओवर-स्ट्रक्चर हो सके; उपयोगकर्ता एक गहरी स्टैक-स्तरीय फ़ंक्शन कॉल को हटाने के लिए कह रहा है। इस स्थिति में, आपको या तो:

  • वस्तु और संबंधित संदर्भ संरचनाओं के प्रबंधन के बारे में कुछ अनुशासन का परिचय या ...
  • किसी तरह "शुद्ध स्टैक आधारित जीवनकाल से बच" के अंधेरे पक्ष पर जाएं: ऑब्जेक्ट को उन कार्यों से स्वतंत्र रूप से छोड़ना चाहिए जिन्होंने उन्हें बनाया था। और छोड़ना चाहिए ... जब तक वे आवश्यक हों

C ++ isteslf के पास उस घटना ( while(are_they_needed)) पर नजर रखने के लिए कोई मूल तंत्र नहीं है , इसलिए आपको इसके साथ अनुमानित करना होगा:

  1. साझा स्वामित्व का उपयोग करें : वस्तुओं का जीवन एक "संदर्भ काउंटर" के लिए बाध्य है: काम करता है यदि "स्वामित्व" को व्यवस्थित रूप से व्यवस्थित किया जा सकता है, तो विफल रहता है जहां स्वामित्व लूप मौजूद हो सकते हैं। यह वह है जो std :: share_ptr करता है। और लूप को तोड़ने के लिए weak_ptr का उपयोग किया जा सकता है। यह ज्यादातर समय काम करता है, लेकिन बड़े डिजाइन में विफल रहता है, जहां कई डिजाइनर विभिन्न टीमों में काम करते हैं और कोई स्पष्ट कारण नहीं है (कुछ आवश्यकता से कुछ) जो अपने स्वयं के बारे में जानते हैं कि क्या होना चाहिए (विशिष्ट उदाहरण दोहरी पसंद की गई श्रृंखलाएं हैं: सबसे अधिक है पिछले के कारण पिछले या अगले का जिक्र करते हुए पिछले का जिक्र करते हुए अगले की आवश्यकता होती है? एक आवश्यकता के संक्षिप्त में यद्यपि समाधान समतुल्य हैं, और बड़ी परियोजना में आप उन्हें मिश्रण करने का जोखिम उठाते हैं)
  2. एक कचरा इकट्ठा करने वाले ढेर का उपयोग करें : आप बस डटकर जीवनकाल की परवाह नहीं करते हैं। आप कलेक्टर को समय-समय पर चलाते हैं और जो अप्रतिबंधित है उसे "अब और नहीं" और ... अच्छी तरह से ... अहम ... नष्ट कर दिया जाता है? अंतिम रूप दे दिया? जमे हुए?। जीसी कलेक्टर के एक नंबर हैं, लेकिन मुझे कभी ऐसा नहीं मिला जो वास्तव में C ++ जागरूक हो। उनमें से अधिकांश मुक्त स्मृति, वस्तु विनाश के बारे में परवाह नहीं है।
  3. उचित मानक विधियों के इंटरफेस के साथ C ++ जागरूक कचरा संग्राहक का उपयोग करें। इसे पाने के लिए शुभकामनाएँ।

पिछले एक के पहले ही समाधान के लिए जा रहे हैं, वस्तु जीवनकाल में वृद्धि के प्रबंधन के लिए आवश्यक सहायक डेटा संरचना की मात्रा, जैसा कि समय इसे व्यवस्थित करने और इसे बनाए रखने के लिए खर्च करता है।

कचरा संग्रहकर्ता के पास लागत, शेयर्ड_एपीटीआर कम, यूनिक_प्रेट और भी कम है, और स्टैक प्रबंधित ऑब्जेक्ट बहुत कम हैं।

है shared_ptr"अंतिम उपाय" ?. नहीं, यह नहीं है: अंतिम उपाय कचरा बीनने वाले हैं। shared_ptrवास्तव में std::प्रस्तावित अंतिम उपाय है। लेकिन हो सकता है कि रिग सॉल्यूशन हो, अगर आप उस स्थिति में हैं, जिसे मैंने समझाया है।


9

बाद के सत्र में हर्ब सटर द्वारा उल्लिखित एक बात यह है कि हर बार जब आप कॉपी करते हैं shared_ptr<>तो एक इंटरलॉक्ड वेतन वृद्धि / कमी होती है जो कि होती है। मल्टी-कोर सिस्टम पर मल्टी-थ्रेडेड कोड पर, मेमोरी सिंक्रनाइज़ेशन नगण्य नहीं है। चुनाव को देखते हुए या तो स्टैक वैल्यू का उपयोग करना बेहतर होता है, या unique_ptr<>संदर्भों या कच्चे पॉइंटर्स के आसपास से गुजरना होता है।


1
या shared_ptrlvalue या rvalue संदर्भ से गुजरती हैं ...
रोंएग करें

8
मुद्दा यह है कि यह केवल shared_ptrचांदी की गोली की तरह उपयोग न करें , जो आपकी स्मृति रिसाव की सभी समस्याओं को हल कर देगा, क्योंकि यह मानक है। यह एक आकर्षक जाल है, लेकिन संसाधन स्वामित्व के बारे में जागरूक होना अभी भी महत्वपूर्ण है, और जब तक कि स्वामित्व साझा shared_ptr<>नहीं किया जाता है, यह एक सबसे अच्छा विकल्प नहीं है।
ग्रहण

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

1
@ जीबीजैनब: हाँ वे सीपीयू स्तर पर हैं, लेकिन एक मल्टी-कोर सिस्टम पर आप कैश को अमान्य कर रहे हैं और मेमोरी बाधाओं को मजबूर कर रहे हैं।
ग्रहण

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

7

मुझे याद नहीं है कि अंतिम "सहारा" वह सटीक शब्द था जिसका वह उपयोग करता था, लेकिन मेरा मानना ​​है कि उसने जो कहा उसका वास्तविक अर्थ अंतिम "विकल्प" था: स्पष्ट स्वामित्व की शर्तें; unique_ptr, weak_ptr, साझा_ptr और यहां तक ​​कि नग्न बिंदुओं का अपना स्थान है।

एक बात जिस पर वे सभी सहमत थे वह यह है कि हम (डेवलपर्स, पुस्तक लेखक, आदि) सभी C ++ 11 के "सीखने के चरण" में हैं और पैटर्न और शैलियों को परिभाषित किया जा रहा है।

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


5

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

दूसरी चीज़ जो लोग भूल जाते हैं (उपरोक्त सामग्री में उल्लिखित अशुद्धि को लॉक / अपडेट / अनलॉक करने के अलावा), वह यह है कि share_ptr अकेले चक्र की समस्याओं को हल नहीं करता है। आप अभी भी साझा किए गए संसाधनों को साझा कर सकते हैं:

ऑब्जेक्ट A, में एक अन्य ऑब्जेक्ट के लिए एक साझा पॉइंटर होता है एक ऑब्जेक्ट B एक A1 और A2 बनाता है, और a1.otherA = a2 असाइन करता है; और a2.otherA = a1; अब, ऑब्जेक्ट बी के साझा पॉइंटर्स जो इसे a1 बनाते थे, a2 स्कोप से बाहर जाते हैं (एक फ़ंक्शन के अंत में कहते हैं)। अब आपके पास एक रिसाव है- कोई भी व्यक्ति 1 और a2 को संदर्भित नहीं करता है, लेकिन वे एक-दूसरे को संदर्भित करते हैं, इसलिए उनके रेफरी काउंट्स हमेशा 1 होते हैं, और आपने लीक किया है।

यह सरल उदाहरण है, जब यह वास्तविक कोड में होता है तो यह आमतौर पर जटिल तरीकों से होता है। कमजोर_प्ट्र के साथ एक समाधान है, लेकिन इतने सारे लोग अब हर जगह share_ptr करते हैं और रिसाव समस्या या कमजोर_प्रति का भी पता नहीं लगाते हैं।

इसे लपेटने के लिए: मुझे लगता है कि ओपी द्वारा संदर्भित टिप्पणियां इस बात को उबालती हैं:

इससे कोई फर्क नहीं पड़ता कि आप किस भाषा में काम कर रहे हैं (प्रबंधित, अप्रबंधित, या साझा-वितरण जैसे संदर्भ मायने रखता है), आपको वस्तु निर्माण, जीवनकाल और विनाश पर निर्णय लेने और जानबूझकर निर्णय लेने की आवश्यकता है।

संपादित करें: भले ही इसका मतलब है कि "अज्ञात, मुझे एक साझा_ उपयोग करने की आवश्यकता है," आपने अभी भी इसके बारे में सोचा है और जानबूझकर ऐसा कर रहे हैं।


3

मैं अपने अनुभव के साथ ऑब्जेक्टिव-सी के साथ उत्तर दूंगा, एक ऐसी भाषा जहां सभी वस्तुओं को संदर्भ दिया जाता है और ढेर पर आवंटित किया जाता है। ऑब्जेक्ट्स का इलाज करने का एक तरीका होने के कारण, प्रोग्रामर के लिए चीजें बहुत आसान हैं। इसने मानक नियमों को परिभाषित करने की अनुमति दी है, जब पालन किया जाता है, तो कोड की मजबूती और कोई मेमोरी लीक की गारंटी नहीं दी जाती है। यह हाल ही में एआरसी (स्वचालित संदर्भ गिनती) की तरह उभरने के लिए चतुर कंपाइलर अनुकूलन के लिए भी संभव बना।

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


1

मैं इस सवाल का जवाब देने की कोशिश करूंगा:

हम बिना std :: share_ptr के कार्यक्रम कैसे कर सकते हैं और अभी भी सुरक्षित तरीके से वस्तु जीवन का प्रबंधन कर सकते हैं?

C ++ में मेमोरी करने के विभिन्न तरीके हैं, उदाहरण के लिए:

  1. struct A { MyStruct s1,s2; };क्लास स्कोप में शेयर्ड_प्ट्र के बजाय का उपयोग करें । यह केवल उन्नत प्रोग्रामर्स के लिए है क्योंकि इसके लिए आवश्यक है कि आप यह समझें कि निर्भरता कैसे काम करती है, और निर्भरता को नियंत्रित करने की क्षमता की आवश्यकता होती है ताकि उन्हें पेड़ तक सीमित रखा जा सके। हेडर फ़ाइल में कक्षाओं का क्रम इस का महत्वपूर्ण पहलू है। ऐसा लगता है कि यह उपयोग पहले से ही देशी बिलियन सी ++ प्रकारों के साथ आम है, लेकिन प्रोग्रामर-परिभाषित वर्गों के साथ इसका उपयोग इन निर्भरता और कक्षाओं की समस्याओं के आदेश के कारण कम उपयोग किया जाता है। इस समाधान में भी आकार के साथ समस्याएं हैं। प्रोग्रामर इसमें समस्याओं को आगे की घोषणाओं या अनावश्यक #includes का उपयोग करने की आवश्यकता के रूप में देखते हैं और इस प्रकार कई प्रोग्रामर वापस बिंदुओं के अवर समाधान और बाद में साझा करने के लिए साझा कर देंगे।
  2. के MyClass &find_obj(int i);बजाय + क्लोन () का उपयोग करें shared_ptr<MyClass> create_obj(int i);। कई प्रोग्रामर नई वस्तुओं को बनाने के लिए कारखानों का निर्माण करना चाहते हैं। share_ptr इस तरह के उपयोग के लिए आदर्श रूप से अनुकूल है। समस्या यह है कि यह पहले से ही स्टंप या ऑब्जेक्ट आधारित समाधान के बजाय, ढेर / मुफ्त स्टोर आवंटन का उपयोग करके जटिल मेमोरी प्रबंधन समाधान मानता है। गुड सी ++ क्लास पदानुक्रम सभी मेमोरी प्रबंधन योजनाओं का समर्थन करता है, न कि उनमें से केवल एक। संदर्भ आधारित समाधान काम कर सकता है यदि लौटी हुई वस्तु स्थानीय फ़ंक्शन स्कोप चर का उपयोग करने के बजाय, ऑब्जेक्ट के अंदर संग्रहीत होती है। कारखाने से उपयोगकर्ता कोड के स्वामित्व को पास करने से बचना चाहिए। Find_obj () का उपयोग करने के बाद ऑब्जेक्ट को कॉपी करना अच्छा तरीका है - सामान्य कॉपी-कंस्ट्रक्टर और सामान्य कंस्ट्रक्टर (अलग-अलग वर्ग के) के साथ रिफर्मरेंस पैरामीटर या क्लोन () के लिए पॉलीमॉर्फिक ऑब्जेक्ट्स इसे संभाल सकते हैं।
  3. संकेत या साझा करने के बजाय संदर्भों का उपयोग करें। प्रत्येक c ++ क्लास में कंस्ट्रक्टर हैं, और प्रत्येक संदर्भ डेटा सदस्य को आरंभीकृत करने की आवश्यकता है। यह उपयोग पॉइंटर्स और शेयर्ड_प्टर्स के कई उपयोगों से बच सकता है। आपको बस यह चुनने की ज़रूरत है कि आपकी मेमोरी ऑब्जेक्ट के अंदर है, या उसके बाहर है, और निर्णय के आधार पर संरचना समाधान या संदर्भ समाधान चुनें। इस समाधान के साथ समस्याएं आमतौर पर कंस्ट्रक्टर मापदंडों से बचने से संबंधित होती हैं जो सामान्य लेकिन समस्याग्रस्त अभ्यास और गलतफहमी है कि कक्षाओं के लिए इंटरफेस कैसे डिज़ाइन किए जाने चाहिए।

"कारखाने से उपयोगकर्ता कोड तक स्वामित्व पास करने से बचना चाहिए।" और क्या होता है जब यह संभव नहीं है? "पॉइंटर्स या शेयर्ड_प्टर्स के बजाय संदर्भों का उपयोग।" उम नहीं। संकेत दिए जा सकते हैं। सन्दर्भ नहीं दे सकते। यह एक वर्ग में संग्रहीत चीजों पर निर्माण-समय के प्रतिबंधों को लागू करता है। यह बहुत सी चीजों के लिए व्यावहारिक नहीं है। आपका समाधान अधिक तरल इंटरफ़ेस और उपयोग पैटर्न की जरूरतों के लिए बहुत कठोर और अनम्य लगता है।
निकोल बोल्स

@ नाइकोल बोलस: एक बार जब आप ऊपर दिए गए नियमों का पालन करते हैं, तो रेफ्स का उपयोग ऑब्जेक्ट्स के बीच निर्भरता के लिए किया जा सकता है, न कि आपके द्वारा सुझाए गए डेटा स्टोरेज के लिए। निर्भरताएं डेटा की तुलना में अधिक स्थिर होती हैं, इसलिए हम उस समस्या में कभी नहीं आते हैं जिस पर आप विचार कर रहे थे।
tp1

यहाँ एक बहुत ही सरल उदाहरण है। आपके पास एक गेम इकाई है, जो एक वस्तु है। इसे किसी अन्य ऑब्जेक्ट को संदर्भित करने की आवश्यकता है, जो कि लक्ष्य इकाई है जिसे बात करने की आवश्यकता है। हालाँकि, लक्ष्य बदल सकते हैं। विभिन्न बिंदुओं पर लक्ष्य मर सकते हैं । और इकाई को इन परिस्थितियों को संभालने में सक्षम होने की आवश्यकता है। आपका कठोर नो-पॉइंटर्स दृष्टिकोण लक्ष्य बदलने के रूप में सरल रूप में भी कुछ नहीं संभाल सकता है, अकेले लक्ष्य को मरने दें।
निकोल बोल

@nicol bolas: ओह, जो अलग तरह से संभाला जाता है; वर्ग का इंटरफ़ेस एक से अधिक "इकाई" का समर्थन करता है। ऑब्जेक्ट्स और एंटिटीज़ के बीच 1: 1 मैपिंग के बजाय, आप एन्टार्रे का उपयोग करेंगे। तब संस्थाओं को केवल सरणी से हटाकर बहुत आसानी से मर जाते हैं। पूरे खेल में केवल बहुत कम संख्या में लोग होते हैं और सरणियों के बीच निर्भरता बहुत बार नहीं बदलती है :)
tp1

2
नहीं, unique_ptrकारखानों के लिए सबसे उपयुक्त है। आप इसे एक unique_ptrमें बदल सकते हैं shared_ptr, लेकिन दूसरी दिशा में जाना तार्किक रूप से असंभव है।
बेन वायगट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.