स्मार्ट संकेत (बढ़ावा) समझाया


220

निम्नलिखित बिंदुओं के बीच अंतर क्या है? जब आप उत्पादन कोड में प्रत्येक पॉइंटर का उपयोग करते हैं, तो बिल्कुल भी?

उदाहरण की सराहना की जाएगी!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

क्या आप उत्पादन कोड में बढ़ावा का उपयोग करते हैं?

जवाबों:


339

स्मार्ट पॉइंटर्स के मूल गुण

यह आसान है जब आपके पास ऐसे गुण हैं जो आप प्रत्येक स्मार्ट पॉइंटर को असाइन कर सकते हैं। तीन महत्वपूर्ण गुण हैं।

  • कोई स्वामित्व नहीं
  • स्वामित्व का हस्तांतरण
  • स्वामित्व का हिस्सा

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

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

कुछ मालिक स्मार्ट पॉइंटर्स न तो दूसरे और न ही तीसरे का समर्थन करते हैं। इसलिए उन्हें फ़ंक्शंस से नहीं लौटाया जा सकता है या कहीं और पारित नहीं किया जा सकता है। यह उन RAIIउद्देश्यों के लिए सबसे उपयुक्त है जहां स्मार्ट पॉइंटर को स्थानीय रखा जाता है और इसे सिर्फ इसलिए बनाया जाता है ताकि यह किसी वस्तु को दायरे से बाहर जाने के बाद मुक्त कर दे।

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

C ++ 1x तथाकथित "मूव कंस्ट्रक्टर्स" और "मूव असाइनमेंट ऑपरेटर्स" को शुरू करके ट्रांसफर-ऑफ-स्वामित्व के लिए मूल समर्थन प्रदान करता है। यह ऐसे ट्रांसफर-ऑफ-ओनरशिप स्मार्ट पॉइंटर भी कहा जाता है unique_ptr

स्मार्ट पॉइंटर्स को वर्गीकृत करना

scoped_ptrएक स्मार्ट पॉइंटर है जो न तो हस्तांतरणीय है और न ही पहनने योग्य है। यह केवल उपयोग करने योग्य है यदि आपको स्थानीय रूप से मेमोरी आवंटित करने की आवश्यकता है, लेकिन यह सुनिश्चित करें कि यह फिर से मुक्त हो जाए जब यह दायरे से बाहर हो जाए। लेकिन अगर आप ऐसा करने की इच्छा रखते हैं, तो इसे दूसरे scoped_ptr के साथ स्वैप किया जा सकता है।

shared_ptrएक स्मार्ट पॉइंटर है जो स्वामित्व को साझा करता है (ऊपर तीसरा प्रकार)। यह संदर्भित संदर्भ है इसलिए यह देख सकता है कि इसकी अंतिम प्रति कब दायरे से बाहर हो जाती है और फिर यह प्रबंधित वस्तु को मुक्त कर देती है।

weak_ptrएक गैर-स्वामित्व वाला स्मार्ट पॉइंटर है। इसका उपयोग एक संदर्भ संख्या को जोड़ने के बिना एक प्रबंधित वस्तु (एक साझा_प्रक्रम द्वारा प्रबंधित) को संदर्भित करने के लिए किया जाता है। आम तौर पर, आपको कच्चे पॉइंटर को share_ptr से बाहर निकालना होगा और इसे चारों ओर कॉपी करना होगा। लेकिन यह सुरक्षित नहीं होगा, क्योंकि आपके पास यह जांचने का कोई तरीका नहीं होगा कि वस्तु वास्तव में कब हटाई गई। तो, weak_ptr द्वारा साझा किए गए ऑब्जेक्ट को संदर्भित करके साधन प्रदान करता है। यदि आपको ऑब्जेक्ट को एक्सेस करने की आवश्यकता है, तो आप इसके प्रबंधन को लॉक कर सकते हैं (इससे बचने के लिए किसी अन्य थ्रेड में एक साझा_पार्टर ऑब्जेक्ट का उपयोग करते समय इसे मुक्त करता है) और फिर इसका उपयोग करें। यदि पहले से हटाए गए ऑब्जेक्ट के लिए weak_ptr इंगित करता है, तो यह आपको एक अपवाद फेंककर नोटिस करेगा। कमजोर चक्रीय का उपयोग करना सबसे अधिक फायदेमंद है जब आपके पास चक्रीय संदर्भ होता है: संदर्भ गिनती आसानी से ऐसी स्थिति का सामना नहीं कर सकती है।

intrusive_ptrएक शेयर्ड_एप्ट्र की तरह है, लेकिन यह रेफरेंस काउंट को एक शेयर्ड_प्ट्र में नहीं रखता है, लेकिन कुछ हेल्पर फंक्शन्स के लिए काउंट को बढ़ाता / घटाता है, जिसे मैनेज किए गए ऑब्जेक्ट द्वारा परिभाषित करने की आवश्यकता होती है। इसका यह लाभ है कि पहले से संदर्भित ऑब्जेक्ट (जिसमें एक बाहरी संदर्भ गिनती तंत्र द्वारा बढ़ाई गई संदर्भ संख्या है) को एक घुसपैठ_प्रात में भरा जा सकता है - क्योंकि संदर्भ संख्या अब स्मार्ट पॉइंटर के लिए आंतरिक नहीं है, लेकिन स्मार्ट पॉटर एक मौजूदा का उपयोग करता है संदर्भ गिनती तंत्र।

unique_ptrस्वामित्व सूचक का स्थानांतरण है। आप इसे कॉपी नहीं कर सकते, लेकिन आप इसे C ++ 1x के मूव कंस्ट्रक्टर्स का उपयोग करके स्थानांतरित कर सकते हैं:

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

यह वह शब्दार्थ है जो std :: auto_ptr का पालन करता है, लेकिन हिलाने के लिए मूल समर्थन गायब होने के कारण, यह उन्हें नुकसान के बिना प्रदान करने में विफल रहता है। unique_ptr स्वचालित रूप से अस्थायी अस्थायी अनूठे_ptr से संसाधनों की चोरी करेगा जो चाल शब्दार्थों की प्रमुख विशेषताओं में से एक है। auto_ptr को अगले C ++ मानक रिलीज में unique_ptr के पक्ष में हटा दिया जाएगा। C ++ 1x उन वस्तुओं को भी भरने की अनुमति देगा जो केवल चल योग्य हैं, लेकिन कंटेनरों में प्रतिलिपि योग्य नहीं हैं। उदाहरण के लिए, आप यूनिक_प्रेट को वेक्टर में स्टफ कर सकते हैं। यदि आप इस बारे में अधिक पढ़ना चाहते हैं तो मैं यहां रुकूंगा और इसके बारे में एक अच्छा लेख लिखूंगा।


3
प्रशंसा के लिए धन्यवाद दोस्त। मैं इसकी सराहना तो आप +1 अब भी पाने के लिए जा रहे हैं: पी
litb - Johannes Schaub

@litb: मुझे "स्वामित्व के हस्तांतरण" में संदेह है; मैं मानता हूं कि C ++ 03 में वस्तुओं के बीच स्वामित्व का कोई वास्तविक हस्तांतरण नहीं है , लेकिन स्मार्ट पॉइंटर्स के लिए ऐसा नहीं किया जा सकता है, विध्वंसक प्रतिलिपि तंत्र द्वारा यहां बताया गया है Informit.com/articles/article.aspx?p=31529&seqbum= ५
लीजेंड्स 2

3
शानदार जवाब। नोट: auto_ptrपहले से ही पदावनत है (C ++ 11)।
निकोलय

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

मुझे यह भी बताया गया है कि बेहतर कैश सुसंगतता के लिए बेहतर intrusive_ptrहो सकता है shared_ptr। स्पष्ट रूप से कैश बेहतर प्रदर्शन करता है यदि आप संदर्भ संख्या को एक अलग ऑब्जेक्ट के बजाय प्रबंधित ऑब्जेक्ट की मेमोरी के हिस्से के रूप में संग्रहीत करते हैं। यह प्रबंधित ऑब्जेक्ट के टेम्पलेट या सुपरक्लास में लागू किया जा सकता है।
एलियट

91

scoped_ptr सबसे सरल है। जब यह दायरे से बाहर हो जाता है, तो यह नष्ट हो जाता है। निम्नलिखित कोड अवैध है (scoped_ptrs गैर-प्रतिलिपि योग्य हैं) लेकिन एक बिंदु को स्पष्ट करेगा:

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

shared_ptr को संदर्भ गिना जाता है। जब भी कोई प्रति या असाइनमेंट होता है, संदर्भ गणना बढ़ जाती है। हर बार एक उदाहरण के विध्वंसक को निकाल दिया जाता है, कच्चे टी * के लिए संदर्भ संख्या में कमी की जाती है। एक बार जब यह 0 होता है, तो पॉइंटर को मुक्त कर दिया जाता है।

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptr एक साझा पॉइंटर का एक कमजोर-संदर्भ है जिसकी आपको यह देखने के लिए जांच करने की आवश्यकता है कि क्या पॉइंट-टू- शेयर्ड_ptr अभी भी आसपास है

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr का उपयोग आम तौर पर तब किया जाता है जब कोई 3rd पार्टी स्मार्ट ptr होता है जिसका आपको उपयोग करना चाहिए। यह संदर्भ संख्या को जोड़ने और घटाने के लिए एक निशुल्क फ़ंक्शन को कॉल करेगा । अधिक जानकारी के लिए दस्तावेज़ को बढ़ावा देने के लिए लिंक देखें


नहीं if (tPtrAccessed[0].get() == 0)माना जाता है if (tPtrAccessed.get() == 0) ?
राजेश्वर

@DougT। क्या आप मानते हैं कि जावा संदर्भ के साथ एक ही विचार का उपयोग करता है? नरम, हार्ड, कमजोर आदि?
गन्सब

20

boost::ptr_containerस्मार्ट पॉइंटर्स को बढ़ावा देने के किसी भी सर्वेक्षण में अनदेखी न करें । वे उन स्थितियों में अमूल्य हो सकते हैं, जैसे एक std::vector<boost::shared_ptr<T> >बहुत धीमा होगा।


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

12

मैं प्रलेखन को देखने के बारे में सलाह देता हूं। यह उतना डरावना नहीं है जितना लगता है। और कुछ छोटे संकेत:

  • scoped_ptr- यह कार्यक्षेत्र से बाहर जाने पर एक सूचक स्वचालित रूप से हटा दिया जाता है। नोट - कोई असाइनमेंट संभव नहीं है, लेकिन ओवरहेड का परिचय नहीं देता है
  • intrusive_ptr- रेफरेंस काउंटिंग पॉइंटर जिसका ओवरहेड नहीं है smart_ptr। हालाँकि ऑब्जेक्ट स्वयं संदर्भ संख्या संग्रहीत करता है
  • weak_ptr- shared_ptrपरिपत्र निर्भरता के परिणामस्वरूप स्थितियों से निपटने के लिए एक साथ काम करता है (प्रलेखन पढ़ें, और अच्छी तस्वीर के लिए Google पर खोज करें;)
  • shared_ptr - स्मार्ट पॉइंटर्स के जेनेरिक, सबसे शक्तिशाली (और हैवीवेट)
  • पुराना भी है auto_ptr, जो यह सुनिश्चित करता है कि जिस बिंदु पर नियंत्रण होता है वह वस्तु अपने आप नष्ट हो जाती है। हालाँकि इसमें बाकी लोगों की तुलना में अलग-अलग कॉपी शब्दार्थ हैं।
  • unique_ptr- C ++ 0x के साथ आएगा

प्रतिक्रिया संपादित करने के लिए: हाँ


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