2010-05-10 16 views

उत्तर

52

असल में, shared_ptr में दो पॉइंटर्स हैं: साझा ऑब्जेक्ट के लिए एक पॉइंटर और एक संदर्भ में एक पॉइंटर जिसमें दो संदर्भ संख्याएं होती हैं: एक "मजबूत संदर्भ" या संदर्भ वाले स्वामित्व वाले संदर्भ, और "कमजोर संदर्भ" के लिए, संदर्भ जिनके पास स्वामित्व नहीं है।

जब आप shared_ptr कॉपी करते हैं, तो कॉपी कन्स्ट्रक्टर मजबूत संदर्भ गणना को बढ़ाता है। जब आप shared_ptr को नष्ट करते हैं, तो विनाशक मजबूत संदर्भ गणना को कम करता है और परीक्षण करता है कि संदर्भ गणना शून्य है या नहीं; यदि ऐसा है, तो विनाशक साझा ऑब्जेक्ट को हटा देता है क्योंकि अब shared_ptr इससे कोई बात नहीं है।

कमजोर संदर्भ गणना weak_ptr का समर्थन करने के लिए उपयोग की जाती है; मूल रूप से, किसी भी समय weak_ptrshared_ptr से बनाया गया है, कमजोर संदर्भ संख्या बढ़ी है, और किसी भी समय नष्ट हो जाने पर कमजोर संदर्भ गणना कम हो जाती है। जब तक या तो मजबूत संदर्भ गणना या कमजोर संदर्भ संख्या शून्य से अधिक है, संदर्भ गणना संरचना नष्ट नहीं की जाएगी।

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

+0

एस/जब तक कमजोर संदर्भ संख्या अधिक होती है/जब तक संदर्भ गणना अधिक होती है, निश्चित रूप से। – MSalters

+0

@MSalters: धन्यवाद। –

+1

बहुत अच्छा जवाब ... मैं सामान्य रूप से स्मार्ट पॉइंटर्स कैसे काम करता हूं, लेकिन मैं कभी भी shared_ptr – Polaris878

0

वे एक आंतरिक संदर्भ गणना रखते हैं जो साझा_प्टर कॉपी कन्स्ट्रक्टर/असाइनमेंट ऑपरेटर में वृद्धि हुई है और विनाशक में कमी आई है। जब गिनती शून्य तक पहुंच जाती है, तो आयोजित पॉइंटर हटा दिया जाता है।

यहां स्मार्ट पॉइंटर्स के लिए बूस्ट लाइब्रेरी documentation है। मुझे लगता है कि टीआर 1 कार्यान्वयन ज्यादातर boost::shared_ptr जैसा ही है।

0

"साझा पॉइंटर एक स्मार्ट सूचक (एक सी ++ ऑब्जेक्ट विह ओवरलोडेड ऑपरेटर *() और ऑपरेटर ->()) है जो किसी ऑब्जेक्ट को पॉइंटर रखता है और साझा पॉइंट पर पॉइंटर रखता है। हर बार जब कोई प्रति स्मार्ट पॉइंटर कॉपी कन्स्ट्रक्टर का उपयोग करके किया जाता है, संदर्भ गणना बढ़ जाती है। जब कोई साझा पॉइंटर नष्ट हो जाता है, तो उसके ऑब्जेक्ट के लिए संदर्भ गणना कम हो जाती है। कच्चे पॉइंटर्स से निर्मित साझा पॉइंटर्स में प्रारंभिक रूप से 1 की संदर्भ संख्या होती है। जब संदर्भ गणना पहुंच जाती है 0, बिंदु वस्तु नष्ट हो जाती है, और जिस स्मृति पर कब्जा होता है वह मुक्त हो जाता है। आपको वस्तुओं को स्पष्ट रूप से नष्ट करने की आवश्यकता नहीं होती है: आखिरी सूचक का विनाशक चलता है जब यह स्वचालित रूप से किया जाएगा। here से।

10

कम से कम तीन प्रसिद्ध तंत्र हैं।

बाहरी काउंटर

जब एक वस्तु के लिए सबसे पहले साझा सूचक बन जाता है, एक अलग संदर्भ गिनती वस्तु बनाया है और 1. जब सूचक नकल करने के लिए आरंभ नहीं हो जाता, संदर्भ गिनती बढ़ जाती है; जब एक सूचक नष्ट हो जाता है तो यह कम हो जाता है। सूचक असाइनमेंट एक गिनती बढ़ाता है और दूसरे को कम करता है (उस क्रम में, या फिर स्वयं-असाइनमेंट ptr=ptr टूट जाएगा)। यदि संदर्भ संख्या शून्य हिट करती है, तो कोई और पॉइंटर्स मौजूद नहीं है और ऑब्जेक्ट हटा दिया गया है।

आंतरिक काउंटर

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

परिपत्र लिंक

(बाहरी काउंटर के साथ, आप एक वस्तु के लिए दो मायने रखता है पहुंचते हैं)

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

नकारात्मकता यह है कि एक परिपत्र एकल-लिंक्ड सूची से नोड को हटाने के लिए महंगा है क्योंकि आपको पूर्ववर्ती को खोजने के लिए सभी नोड्स पर फिर से चलना होगा। संदर्भ के खराब इलाके के कारण यह विशेष रूप से दर्दनाक हो सकता है।

बदलाव

2 और 3 विचार जोड़ा जा सकता है: आधार वर्ग के बजाय एक गिनती युक्त, कि परिपत्र ग्राफ का हिस्सा हो सकता है। बेशक, इसका मतलब यह है कि ऑब्जेक्ट केवल तभी हटाया जा सकता है जब यह स्वयं को इंगित करता है (चक्र लंबाई 1, इसमें कोई शेष पॉइंटर्स नहीं)। फिर, लाभ यह है कि आप कमजोर पॉइंटर्स से स्मार्ट पॉइंटर्स बना सकते हैं, लेकिन श्रृंखला से पॉइंटर हटाने का खराब प्रदर्शन एक मुद्दा बना हुआ है।

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

+0

'boost :: shared_ptr' 'बाहरी काउंटर' दृष्टिकोण का उपयोग करता है। 'आंतरिक काउंटर' दृष्टिकोण (पृथ्वी को फिर से लिखने के बिना) के साथ weak_ptr को लागू करने का कोई तरीका है? – Pavel

+0

@ पावेल: हाँ, लेकिन यह जटिल है। आपको बेस क्लास में कस्टम 'ऑपरेटर न्यू' और विशेष रूप से इसके मिलान 'ऑपरेटर डिलीट' की आवश्यकता होगी। आप इसका उपयोग "टॉम्बस्टोन" बनाने के लिए करते हैं जहां बेस क्लास होता था, ताकि 'weak_ptr' उस टॉम्बस्टोन को इंगित करता रहे। – MSalters

14

मैं आमतौर पर जेम्स मैकनेलिस के उत्तर से सहमत हूं। हालांकि एक और बिंदु है जिसका उल्लेख किया जाना चाहिए।

आपको बता दें कि, shared_ptr<T> भी जब प्रकार T पूरी तरह से परिभाषित नहीं है इस्तेमाल किया जा सकता है।

है:

class AbraCadabra; 

boost::shared_ptr<AbraCadabra> myPtr; 
// ... 

यह & काम संकलित कर देगा। स्मार्ट पॉइंटर्स के कई अन्य कार्यान्वयन के विपरीत, जो वास्तव में इनका उपयोग करने के लिए encapsulated प्रकार को पूरी तरह से परिभाषित करने की मांग करते हैं। यह इस तथ्य से संबंधित है कि स्मार्ट पॉइंटर को encapsulated ऑब्जेक्ट को हटाने के लिए जाना चाहिए जब इसे और संदर्भित नहीं किया जाता है, और किसी ऑब्जेक्ट को हटाने के लिए पता होना चाहिए कि यह क्या है।

यह निम्नलिखित चाल द्वारा हासिल की है: shared_ptr वास्तव में होते हैं निम्नलिखित:

  1. वस्तु के लिए एक अपारदर्शी सूचक
  2. साझा संदर्भ काउंटर (क्या जेम्स McNellis वर्णित)
  3. करने के लिए एक सूचक आवंटित फैक्टरी जो जानता है कि आपकी वस्तु को कैसे नष्ट किया जाए।

उपर्युक्त कारखाना एक एकल वर्चुअल फ़ंक्शन के साथ एक सहायक वस्तु है, जो आपके ऑब्जेक्ट को सही तरीके से हटाना है।

यह कारखाना वास्तव में बनाया गया है जब आप अपने साझा सूचक के लिए असाइन करते हैं।

है, निम्नलिखित कोड

AbraCadabra* pObj = /* get it from somewhere */; 
myPtr.reset(pObj); 

यह जहां इस कारखाने आवंटित किया जाता है है। नोट: reset फ़ंक्शन वास्तव में टेम्पलेट फ़ंक्शन है। यह वास्तव में निर्दिष्ट प्रकार के लिए फैक्ट्री बनाता है (पैरामीटर के रूप में पारित वस्तु का प्रकार)। यह वह जगह है जहां आपका प्रकार पूरी तरह से परिभाषित किया जाना चाहिए। यही है, अगर यह अभी भी परिभाषित नहीं है - आपको एक संकलन त्रुटि मिलेगी।

यह भी ध्यान रखें: आप वास्तव में एक व्युत्पन्न प्रकार (AbraCadabra से प्राप्त) की एक वस्तु बना सकते हैं और shared_ptr को असाइन करता है, तो - यह एक सही तरीका में से हटा दिया जाएगा, भले ही आपके नाशक आभासी नहीं है। shared_ptrreset फ़ंक्शन में देखे गए प्रकार के अनुसार ऑब्जेक्ट को हमेशा हटा देगा।

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

दूसरी ओर - तथाकथित "घुसपैठ" स्मार्ट पॉइंटर्स हैं। उनके पास वह लचीलापन नहीं है, हालांकि इसके विपरीत वे सर्वश्रेष्ठ प्रदर्शन देते हैं। shared_ptr की

पेशेवरों inrusive स्मार्ट संकेत की तुलना में:

  • बहुत लचीला उपयोग। shared_ptr पर इसे निर्दिष्ट करते समय केवल encapsulated प्रकार को परिभाषित करना होगा। बड़ी परियोजनाओं के लिए यह बहुत मूल्यवान है, निर्भरता को बहुत कम करता है।
  • encapsulated प्रकार में वर्चुअल विनाशक होना आवश्यक नहीं है, फिर भी polymorphic प्रकार सही ढंग से हटा दिया जाएगा।
  • कमजोर पॉइंटर्स के साथ उपयोग किया जा सकता है। shared_ptr की

विपक्ष inrusive स्मार्ट संकेत की तुलना में:

  1. बहुत बर्बर प्रदर्शन और ढेर स्मृति की बर्बादी। असाइनमेंट पर 2 और ऑब्जेक्ट आवंटित करते हैं: संदर्भ काउंटर, साथ ही कारखाना (स्मृति का अपशिष्ट, धीमा)। हालांकि यह केवल reset पर होता है। जब एक shared_ptr किसी अन्य को सौंपा गया है - कुछ और आवंटित नहीं किया जाता है।
  2. उपर्युक्त अपवाद फेंक सकता है। (आउट-ऑफ-मेमोरी हालत)। इसके विपरीत घुसपैठ करने वाले स्मार्ट पॉइंटर्स कभी फेंक नहीं सकते (अमान्य मेमोरी एक्सेस, स्टैक ओवरफ्लो और इत्यादि से संबंधित प्रक्रिया अपवादों के अलावा)
  3. आपकी ऑब्जेक्ट को हटाना भी धीमा है: किसी अन्य दो structs को डिलीकेट करने की आवश्यकता है।
  4. घुसपैठ स्मार्ट पॉइंटर्स के साथ काम करते समय आप कच्चे लोगों के साथ स्मार्ट पॉइंटर्स को स्वतंत्र रूप से मिश्रित कर सकते हैं। यह ठीक है क्योंकि वास्तविक संदर्भ गिनती ऑब्जेक्ट के अंदर ही रहता है, जो एकल है। इसके विपरीत - shared_ptr के साथ आप कच्चे पॉइंटर्स के साथ मिश्रण कर सकते हैं।

    अबराकदबरा * pobb =/* इसे कहीं से प्राप्त करें * /; myPtr.reset (पीओबीजे); // ... pObj = myPtr।प्राप्त(); बूस्ट :: shared_ptr myPtr2 (पीओबीजे); // ओओएस

उपरोक्त क्रैश हो जाएगा।

+0

आप shared_ptr डिज़ाइन के लाभ के रूप में weak_ptr का उल्लेख करते हैं। क्या घुसपैठ पीआरटी के साथ कमजोर_पीआरआर होना संभव नहीं है? या इसमें घुसपैठ करने वाले पीआरटी के साथ साझा_प्टर को पुन: पेश करना शामिल होगा? – Pavel

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