क्या सी ++ स्मार्ट सूचक कार्यान्वयन उपलब्ध हैं?


121

तुलना, पेशेवरों, विपक्ष, और जब उपयोग करने के लिए?

यह एक कचरा संग्रह धागे से एक स्पिन-ऑफ है जहां मैंने जो सोचा था वह एक सरल उत्तर था कुछ विशिष्ट स्मार्ट पॉइंटर कार्यान्वयन के बारे में बहुत सारी टिप्पणियां उत्पन्न हुईं, इसलिए यह एक नया पोस्ट शुरू करने के लायक लग रहा था।

अंततः सवाल यह है कि C ++ में स्मार्ट पॉइंटर्स के विभिन्न कार्यान्वयन क्या हैं और उनकी तुलना कैसे की जाती है? बस सरल पेशेवरों और विपक्ष या अपवाद और कुछ करने के लिए gotchas अन्यथा आप सोच सकते हैं कि काम करना चाहिए।

मैंने कुछ क्रियान्वयन पोस्ट किए हैं जिन्हें मैंने कम से कम इस्तेमाल किया है या उन्हें नीचे दिए गए उत्तर के रूप में उपयोग करने पर विचार किया है और उनकी भिन्नता और समानता के बारे में मेरी समझ है जो 100% सटीक नहीं हो सकती है इसलिए स्वतंत्र रूप से मुझे जांचने या जरूरत के अनुसार सही करने के लिए स्वतंत्र महसूस करें।

लक्ष्य कुछ नई वस्तुओं और पुस्तकालयों के बारे में सीखना है या मौजूदा उपयोगों के बारे में व्यापक रूप से उपयोग और अंत में दूसरों के लिए एक सभ्य संदर्भ के साथ मेरे उपयोग और समझ को सही करना है।


5
मुझे लगता है कि इस प्रश्न के उत्तर के रूप में इसे फिर से पोस्ट किया जाना चाहिए, और वास्तविक प्रश्न में बनाया गया प्रश्न। अन्यथा, मुझे लगता है कि लोग इसे "एक वास्तविक प्रश्न नहीं" के रूप में बंद कर देंगे।
स्ट्रगल

3
अन्य स्मार्ट पॉइंटर्स के सभी प्रकार हैं, जैसे एटीएल स्मार्ट पॉइंटर्स या ओपनसेकेग्राफosg::ref_ptr
जेम्स मैकनेलिस

11
यहाँ एक सवाल है?
कोड़ी ग्रे

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

3
आप कहते हैं कि आप स्मार्ट पॉइंटर्स के विशेषज्ञ नहीं हैं, लेकिन आपका सारांश बहुत ही संपूर्ण है, और सही है ( auto_ptr_refएक कार्यान्वयन विवरण होने के बारे में मामूली झगड़े को छोड़कर )। फिर भी, मैं मानता हूं कि आपको इसे उत्तर के रूप में पोस्ट करना चाहिए और प्रश्न को वास्तविक प्रश्न के रूप में सुधारना चाहिए। यह तब भविष्य के संदर्भ के रूप में काम कर सकता है।
कोनराड रूडोल्फ

जवाबों:


231

सी ++ 03

std::auto_ptr- शायद पहले ड्राफ्ट सिंड्रोम से पीड़ित मूल में से एक केवल सीमित कचरा संग्रह की सुविधा प्रदान करता है। पहला नकारात्मक पक्ष यह है कि यह deleteनष्ट हो जाने पर उन्हें आबंटित वस्तुओं को रखने के लिए अस्वीकार्य बनाता है ( new[])। यह पॉइंटर का स्वामित्व लेता है इसलिए दो ऑटो पॉइंटर्स में समान ऑब्जेक्ट नहीं होना चाहिए। असाइनमेंट स्वामित्व हस्तांतरित कर देगा और रिवॉल्यू ऑटो पॉइंटर को नल पॉइंटर में रीसेट कर देगा । जो शायद सबसे खराब दोष की ओर जाता है; नकल करने में असमर्थता के कारण वे एसटीएल कंटेनरों के भीतर उपयोग नहीं किए जा सकते हैं। किसी भी उपयोग के मामले में अंतिम झटका वे सी ++ के अगले मानक में पदावनत किए जाने के लिए निर्धारित हैं।

std::auto_ptr_ref- यह एक स्मार्ट पॉइंटर नहीं है यह वास्तव में एक डिज़ाइन विवरण है जिसका उपयोग std::auto_ptrकुछ स्थितियों में नकल और असाइनमेंट की अनुमति देने के लिए किया जाता है। विशेष रूप से इसका उपयोग एक नॉन-कास्ट std::auto_ptrको एक लैवल्यू में परिवर्तित करने के लिए किया जा सकता है, जिसे कॉल्विन-गिबन्स चाल का भी उपयोग किया जाता है, जिसे एक मूव कंस्ट्रक्टर के रूप में जाना जाता है। स्वामित्व स्थानांतरित करने के लिए ।

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


सी ++ 11

std::unique_ptr- यह हमारा दोस्त है जो इसकी जगह ले std::auto_ptrलेगा और इसकी कमजोरियों को ठीक करने के लिए महत्वपूर्ण सुधारों को छोड़कर यह समान होगाstd::auto_ptr जैसे कि ऐरे के साथ काम करने , प्राइवेट कॉपी कंस्ट्रक्टर के माध्यम से लैवल्यू प्रोटेक्शन, एसटीएल कंटेनर और एल्गोरिदम आदि के साथ प्रयोग करने योग्य होने के नाते, क्योंकि यह ओवरहेड है। और स्मृति पदचिह्न सीमित हैं यह प्रतिस्थापित करने के लिए एक आदर्श उम्मीदवार है, या शायद अधिक उपयुक्त रूप से मालिक, कच्चे संकेत के रूप में वर्णित है। जैसा कि "अद्वितीय" का तात्पर्य है, पिछले की तरह ही सूचक का केवल एक मालिक है std::auto_ptr

std::shared_ptr - मेरा मानना ​​है कि यह TR1 और पर आधारित है boost::shared_ptr लेकिन इसमें एलियासिंग और पॉइंटर अंकगणित को भी शामिल करने के लिए सुधार किया गया है। संक्षेप में, यह एक डायनामिक रूप से आवंटित ऑब्जेक्ट के आसपास गिने हुए स्मार्ट पॉइंटर को संदर्भित करता है। जैसा कि "साझा" का अर्थ है कि सूचक एक से अधिक साझा सूचक के स्वामित्व में हो सकता है जब अंतिम साझा सूचक का अंतिम संदर्भ कार्यक्षेत्र से बाहर हो जाता है तो वस्तु को उचित रूप से हटा दिया जाएगा। ये थ्रेड सुरक्षित भी हैं और अधिकांश मामलों में अपूर्ण प्रकारों को संभाल सकते हैं। डिफ़ॉल्ट आवंटनकर्ता का उपयोग करके एक ढेर आवंटन के साथ std::make_sharedकुशलतापूर्वक निर्माण करने के लिए इस्तेमाल किया जा सकता है std::shared_ptr

std::weak_ptr- इसी तरह TR1 और boost::weak_ptr। यह एक के स्वामित्व वाली वस्तु का संदर्भ है std::shared_ptrऔर इसलिए std::shared_ptrसंदर्भ की संख्या शून्य हो जाने पर वस्तु को हटाने से नहीं रोका जा सकता है। कच्चे पॉइंटर तक पहुंच प्राप्त करने के लिए, आपको पहले std::shared_ptrकॉल करके एक्सेस करने की आवश्यकता lockहोगी जो कि एक खाली वापस आ जाएगी std::shared_ptrयदि स्वामित्व वाले पॉइंटर की समय सीमा समाप्त हो गई है और पहले से ही नष्ट हो गई है। यह कई स्मार्ट पॉइंटर्स का उपयोग करते समय अनिश्चितकालीन हैंगिंग रेफरेंस काउंट्स से बचने के लिए मुख्य रूप से उपयोगी है।


बढ़ावा

boost::shared_ptr- संभवतः सबसे अलग परिदृश्यों (एसटीएल, पीआईएमपीएल, आरएआईआई, आदि) में उपयोग करने के लिए सबसे आसान यह एक साझा संदर्भित स्मार्ट पॉइंटर है। मैंने कुछ स्थितियों में प्रदर्शन और ओवरहेड के बारे में कुछ शिकायतें सुनी हैं, लेकिन मुझे उन्हें अनदेखा करना चाहिए क्योंकि मुझे याद नहीं है कि तर्क क्या था। जाहिरा तौर पर यह एक लंबित मानक C ++ ऑब्जेक्ट बनने के लिए पर्याप्त लोकप्रिय था और स्मार्ट पॉइंटर्स के बारे में मानक पर कोई कमियां दिमाग में नहीं आती हैं।

boost::weak_ptr- std::weak_ptrइस कार्यान्वयन के आधार पर पिछले विवरण की तरह , यह गैर-मालिकाना संदर्भ को अनुमति देता है boost::shared_ptr। आप आश्चर्यजनक रूप से फोन नहीं करते हैंlock() से "मजबूत" साझा सूचक तक पहुंचने के लिए करते हैं और यह सुनिश्चित करने के लिए जांचना चाहिए कि यह वैध है क्योंकि यह पहले ही नष्ट हो सकता था। बस यह सुनिश्चित करें कि साझा किए गए पॉइंटर को वापस स्टोर न करें और इसे जल्द से जल्द स्कोप से बाहर जाने दें, अन्यथा आप इसके साथ कर रहे हैं अन्यथा आप चक्रीय संदर्भ समस्या पर वापस आ गए हैं जहां आपका संदर्भ मायने रखता है और ऑब्जेक्ट्स नष्ट नहीं होंगे।

boost::scoped_ptr- यह एक साधारण स्मार्ट पॉइंटर क्लास है जिसमें थोड़ा ओवरहेड संभवत: बेहतर प्रदर्शन के विकल्प के लिए डिज़ाइन किया गया है boost::shared_ptrजब उपयोग करने योग्य है। यह std::auto_ptrविशेष रूप से इस तथ्य से तुलनीय है कि इसे एसटीएल कंटेनर के तत्व के रूप में या एक ही वस्तु के लिए कई बिंदुओं के साथ सुरक्षित रूप से उपयोग नहीं किया जा सकता है।

boost::intrusive_ptr- मैंने इसका उपयोग कभी नहीं किया है, लेकिन अपनी समझ से इसे तब उपयोग करने के लिए डिज़ाइन किया गया है जब आप अपने स्मार्ट पॉइंटर संगत क्लासेस बना रहे हैं। आपको अपने आप को गिनते हुए संदर्भ को लागू करने की आवश्यकता है, आपको कुछ तरीकों को लागू करने की भी आवश्यकता होगी यदि आप चाहते हैं कि आपकी कक्षा सामान्य हो, इसके अलावा आपको अपनी स्वयं की थ्रेड सुरक्षा को लागू करना होगा। प्लस साइड पर यह संभवतः आपको चुनने और चुनने का सबसे कस्टम तरीका देता है कि आप कितना या कितना "स्मार्टनेस" चाहते हैं। intrusive_ptrआम तौर पर अधिक कुशल है shared_ptrक्योंकि यह आपको प्रति वस्तु एक ही आवंटन करने की अनुमति देता है। (धन्यवाद अरविद)

boost::shared_array- यह boost::shared_ptrसरणियों के लिए है। मूल रूप new []से operator[], और निश्चित रूप delete []से बेक किया जाता है। इसका उपयोग एसटीएल कंटेनरों में किया जा सकता है और जहाँ तक मुझे पता है कि सब कुछ boost:shared_ptrकरता है, हालांकि आप boost::weak_ptrइनका उपयोग नहीं कर सकते । आप हालांकि वैकल्पिक रूप से boost::shared_ptr<std::vector<>>समान कार्यक्षमता के लिए उपयोग कर सकते हैं boost::weak_ptrऔर संदर्भ के लिए उपयोग करने की क्षमता हासिल कर सकते हैं ।

boost::scoped_array- यह boost::scoped_ptrसरणियों के लिए है। जैसा कि boost::shared_arrayसभी आवश्यक सरणी अच्छाई में बेक किया गया है। यह एक गैर-प्रतिलिपि योग्य है और इसलिए एसटीएल कंटेनरों में इसका उपयोग नहीं किया जा सकता है। मैं लगभग कहीं भी पाया आप अपने आप को इस का उपयोग करना चाहते हैं आप शायद उपयोग कर सकते हैं std::vector। मैंने कभी यह निर्धारित नहीं किया है कि वास्तव में कौन तेज है या कम ओवरहेड है, लेकिन यह स्कॉप्ड सरणी एसटीएल वेक्टर की तुलना में बहुत कम शामिल है। जब आप स्टैक पर आवंटन रखना चाहते हैं तो boost::arrayइसके बजाय विचार करें ।


क्यूटी

QPointer- क्यूटी 4.0 में पेश किया गया यह एक "कमजोर" स्मार्ट पॉइंटर है जो केवल QObjectऔर व्युत्पन्न वर्गों के साथ काम करता है , जो कि क्यूटी फ्रेमवर्क में लगभग सब कुछ है, इसलिए यह वास्तव में एक सीमा नहीं है। हालाँकि ऐसी सीमाएँ हैं कि यह एक "मजबूत" पॉइंटर की आपूर्ति नहीं करता है और यद्यपि आप जाँच कर सकते हैं कि अंतर्निहित वस्तु isNull()आपके साथ वैध है या नहीं, क्योंकि आप विशेष रूप से बहु-थ्रेडेड परिवेशों में उस चेक को पास करने के बाद अपनी वस्तु को नष्ट कर सकते हैं। Qt लोग इस पदावनत को मानते हैं जो मैं मानता हूं।

QSharedDataPointer- यह एक "मजबूत" स्मार्ट पॉइंटर संभावित रूप से तुलनीय है, boost::intrusive_ptrहालांकि इसमें कुछ थ्रेड सेफ्टी हैं, लेकिन इसके लिए आपको रेफरेंस काउंटिंग के तरीकों ( refऔर deref) को शामिल करना होगा, जिसे आप सबक्लास करके कर सकते हैं QSharedData। क्यूटी के बहुत से ऑब्जेक्ट्स का उपयोग पर्याप्त विरासत के माध्यम से किया जाता है और सबकुछ सबकुछ करने के लिए इच्छित डिज़ाइन प्रतीत होता है।

QExplicitlySharedDataPointer- बहुत समान है, QSharedDataPointerसिवाय इसके कि यह अंतर्निहित रूप से कॉल नहीं करता है detach()। मैं इस संस्करण को 2.0 के QSharedDataPointerरूप में नियंत्रित करने के लिए मामूली वृद्धि के रूप में बिल्कुल वैसा ही कहूंगा जब संदर्भ की संख्या शून्य होने के बाद अलग हो जाना विशेष रूप से एक पूरी नई वस्तु के लायक नहीं है।

QSharedPointer- एटॉमिक रेफरेंस काउंटिंग, थ्रेड सेफ, शार्बल पॉइंटर, कस्टम डिलीट (एरे सपोर्ट), लगता है कि सब कुछ स्मार्ट पॉइंटर होना चाहिए। यह वही है जो मैं मुख्य रूप से क्यूटी में एक स्मार्ट पॉइंटर के रूप में उपयोग करता हूं और मुझे यह तुलनात्मक लगता है, boost:shared_ptrहालांकि संभवतः क्यूटी में कई वस्तुओं की तरह अधिक ओवरहेड है।

QWeakPointer- क्या तुम एक reoccurring पैटर्न समझ में आता है? बस के रूप में std::weak_ptrऔर boost::weak_ptrयह के साथ संयोजन के रूप में प्रयोग किया जाता है QSharedPointerजब आपको दो स्मार्ट पॉइंटर्स के बीच संदर्भ की आवश्यकता होती है जो अन्यथा आपकी वस्तुओं को कभी भी नष्ट नहीं किया जाएगा।

QScopedPointer- यह नाम भी परिचित होना चाहिए और वास्तव boost::scoped_ptrमें साझा और कमजोर संकेत के क्यूटी संस्करणों के विपरीत आधारित था । यह ओवरहेड के बिना एक एकल स्वामी स्मार्ट पॉइंटर प्रदान करने का कार्य करता है, QSharedPointerजो इसे अनुकूलता, अपवाद सुरक्षित कोड और उन सभी चीजों के लिए अधिक उपयुक्त बनाता है , जिनका आप उपयोग std::auto_ptrया उपयोग कर सकते हैं boost::scoped_ptr


1
मुझे लगता है कि दो चीजें ध्यान देने योग्य हैं: intrusive_ptrआमतौर पर की तुलना में अधिक कुशल है shared_ptr, क्योंकि यह आपको प्रति वस्तु एक ही आवंटन करने की अनुमति देता है। shared_ptrसामान्य स्थिति में संदर्भ काउंटरों के लिए एक अलग छोटा ढेर ऑब्जेक्ट आवंटित करेगा। std::make_sharedदोनों दुनिया का सबसे अच्छा पाने के लिए इस्तेमाल किया जा सकता है। shared_ptrसिर्फ एक ढेर के आवंटन के साथ।
अरविद

मेरे पास शायद एक असंबंधित प्रश्न है: क्या कचरा संग्रह केवल सभी बिंदुओं को बदलकर लागू किया जा सकता है shared_ptr? (चक्रीय संदर्भों को हल करते हुए गिनती नहीं)
सेठ कार्नेगी

@ सेथ कार्नेगी: सभी पॉइंटर्स फ्री स्टोर पर आवंटित चीज़ की ओर इशारा करते हैं।
सिलिको में

2
@the_mandrill लेकिन यह काम करता है यदि ग्राहक वर्ग की तुलना में सहयोगी वर्ग के विध्वंसक को एक अलग अनुवाद इकाई (.cpp-file) में परिभाषित किया गया है, जो पिंपल-मुहावरे में वैसे भी दिया गया है। क्योंकि इस अनुवाद इकाई को आमतौर पर पिम्पल की पूरी परिभाषा पता होती है और इसलिए इसका विध्वंसक (जब यह ऑटो_प्ट्र को नष्ट कर देता है) सही ढंग से पिंपल को नष्ट कर देता है। जब मैंने उन चेतावनियों को देखा, तो मुझे इस बात का डर था, लेकिन मैंने इसे आजमाया और यह काम करता है (पिंपल का विनाशकर्ता कहलाता है)। पुनश्च: कृपया किसी भी उत्तर को देखने के लिए @ -syntax का उपयोग करें।
क्रिश्चियन राऊ

2
डॉक्स के लिए उपयुक्त लिंक जोड़कर सूची की उपयोगिता बढ़ाई गई।
ulidtko

5

लोकी भी है जो नीति-आधारित स्मार्ट पॉइंटर्स को लागू करता है।

नीति-आधारित स्मार्ट पॉइंटर्स पर अन्य संदर्भ, कई कंपाइलरों द्वारा कई उत्तराधिकारियों के साथ खाली आधार अनुकूलन के खराब समर्थन की समस्या को संबोधित करते हैं:


1

दिए गए लोगों के अलावा, कुछ सुरक्षा उन्मुख भी हैं:

SaferCPlusPlus

mse::TRefCountingPointerएक संदर्भ की तरह स्मार्ट सूचक है std::shared_ptr। यह अंतर mse::TRefCountingPointerसुरक्षित, छोटा और तेज है, लेकिन इसमें थ्रेड सुरक्षा तंत्र नहीं है। और यह "नहीं अशक्त" और "निश्चित" (नॉन-रेटरगेटेबल) संस्करणों में आता है जिन्हें हमेशा एक वैध रूप से आवंटित वस्तु की ओर इशारा करते हुए सुरक्षित रूप से ग्रहण किया जा सकता है। इसलिए मूल रूप से, यदि आपकी लक्ष्य वस्तु को अतुल्यकालिक धागे के बीच साझा किया जाता है std::shared_ptr, तो उपयोग करें , अन्यथाmse::TRefCountingPointer अधिक इष्टतम है।

mse::TScopeOwnerPointerके समान है boost::scoped_ptr, लेकिन mse::TScopeFixedPointer"मजबूत-कमजोर" पॉइंटर रिश्ते की तरह std::shared_ptrऔर के साथ मिलकर काम करता हैstd::weak_ptr

mse::TScopeFixedPointerउन वस्तुओं को इंगित करता है जो स्टैक पर आवंटित किए जाते हैं, या जिनके "मालिक" सूचक को स्टैक पर आवंटित किया जाता है। यह (जानबूझकर) सीमित रन-टाइम लागत के साथ संकलन-समय सुरक्षा बढ़ाने के लिए इसकी कार्यक्षमता में सीमित है। "गुंजाइश" बिंदुओं का बिंदु अनिवार्य रूप से परिस्थितियों के एक सेट की पहचान करना है जो सरल और निर्धारक पर्याप्त हैं कि कोई (रनटाइम) सुरक्षा तंत्र आवश्यक नहीं है।

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

यहाँ एक लेख है कि उनका उपयोग क्यों और कैसे करना है। (नोट, बेशर्म प्लग।)

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