share_ptr जादू :)


89

श्री लिडस्ट्रॉम और मेरे पास एक तर्क था :)

श्री लिडस्ट्रॉम का दावा है कि एक निर्माण के shared_ptr<Base> p(new Derived);लिए आधार को एक आभासी विध्वंसक की आवश्यकता नहीं होती है:

आर्मेन Tsirunyan : "वास्तव में विल shared_ptr सही ढंग से साफ आप इस मामले में खुश कर सके प्रदर्शित कैसे उस प्रभाव से लागू किया जा सकता है??"

डैनियल लिडस्ट्रॉम : " शेयर्ड_एप्ट्र कंक्रीट के इंस्टेंस को हटाने के लिए अपने स्वयं के विध्वंसक का उपयोग करता है। इसे C ++ समुदाय के भीतर RAII के रूप में जाना जाता है। मेरी सलाह है कि आप RAII के बारे में सभी सीख सकते हैं। यह आपके C ++ को इतना आसान बना देगा जब आप उपयोग करते हैं। सभी स्थितियों में आरएआई। "

आर्मेन त्सिरुयन : "मैं RAII के बारे में जानता हूं, और मुझे यह भी पता है कि अंततः share_ptr विध्वंसक संग्रहीत px को हटा सकता है जब pn 0. तक पहुंचता है, लेकिन अगर px के पास स्थिर प्रकार सूचक Baseऔर गतिशील प्रकार सूचक करने के लिए है Derived, तो जब तक Baseकि एक आभासी विध्वंसक नहीं है, यह अपरिभाषित व्यवहार का परिणाम होगा। अगर मैं गलत हूं तो मुझे सुधारें। "

डैनियल लिडस्ट्रॉम : " शेयर्ड_प्ट्र को पता है कि स्टैटिक टाइप कंक्रीट है। यह जानता है क्योंकि मैंने इसे इसके कंस्ट्रक्टर में पास किया है। जादू जैसा लगता है, लेकिन मैं आपको विश्वास दिलाता हूं कि यह डिजाइन और बेहद अच्छी है।"

इसलिए हमें जज कीजिए। यह कैसे संभव है (यदि यह है) वर्चुअल विध्वंसक होने के लिए पॉलीमॉर्फिक कक्षाओं की आवश्यकता के बिना साझा करने के लिए लागू करना संभव है ? अग्रिम में धन्यवाद


3
आप मूल धागे से जुड़े हो सकते हैं ।
डारिन दिमित्रोव

8
एक और दिलचस्प बात यह है कि यह विध्वंसक द्वारा वस्तु shared_ptr<void> p(new Derived)को नष्ट कर देगा Derived, भले ही वह हो virtualया न हो।
dalle

7
एक सवाल पूछने का बहुत बढ़िया तरीका :)
रुबेंव

5
भले ही share_ptr यह अनुमति देता है, लेकिन वर्चुअल डोर के बिना एक वर्ग के रूप में एक वर्ग को डिजाइन करना वास्तव में एक बुरा विचार है। RAII के बारे में डैनियल की टिप्पणियां भ्रामक हैं- इसका इससे कोई लेना-देना नहीं है- लेकिन उद्धृत वार्तालाप एक साधारण गलत संचार की तरह लगता है (और साझा करने की गलत धारणा कैसे काम करती है)।

6
RAII नहीं, बल्कि यह विध्वंसक को टाइप करता है। आपको सावधान रहना होगा, क्योंकि shared_ptr<T>( (T*)new U() )जहां struct U:Tआप सही काम नहीं करेंगे (और यह अप्रत्यक्ष रूप से आसानी से किया जा सकता है, जैसे कि एक फ़ंक्शन जो एक लेता है T*और एक पास हो जाता है U*)
यक - एडम नेवरामॉन्ट

जवाबों:


74

हाँ, इस तरह से साझा करना संभव है। बूस्ट करता है और C ++ 11 मानक को भी इस व्यवहार की आवश्यकता होती है। एक अतिरिक्त लचीलेपन के रूप में साझा किया गया एक साझा काउंटर से अधिक का प्रबंधन करता है। एक तथाकथित डिलेटर आमतौर पर एक ही मेमोरी ब्लॉक में डाला जाता है जिसमें संदर्भ काउंटर भी होते हैं। लेकिन मज़े की बात यह है कि इस डीलेटर का प्रकार शेयर्ड_प्ट्र प्रकार का हिस्सा नहीं है। इसे "टाइप इरेज़र" कहा जाता है और मूल रूप से एक ही तकनीक है जिसका उपयोग "पॉलीमॉर्फिक फ़ंक्शंस" को बढ़ावा देने के लिए किया जाता है :: वास्तविक फ़ंक्शर्स के प्रकार को छिपाने के लिए फ़ंक्शन या std :: फ़ंक्शन। आपके उदाहरण को काम करने के लिए, हमें एक अस्थायी निर्माणकर्ता की आवश्यकता है:

template<class T>
class shared_ptr
{
public:
   ...
   template<class Y>
   explicit shared_ptr(Y* p);
   ...
};

इसलिए, यदि आप इसका उपयोग अपनी कक्षाओं बेस और व्युत्पन्न के साथ करते हैं ...

class Base {};
class Derived : public Base {};

int main() {
   shared_ptr<Base> sp (new Derived);
}

... Y = व्युत्पन्न के साथ templated कंस्ट्रक्टर का उपयोग साझा करने के लिए किया जाता है। कंस्ट्रक्टर के पास उपयुक्त डेलेटर ऑब्जेक्ट और संदर्भ काउंटर बनाने का मौका होता है और डेटा कंट्रोलर के रूप में इस कंट्रोल ब्लॉक के लिए एक पॉइंटर को स्टोर करता है। यदि संदर्भ काउंटर शून्य तक पहुँच जाता है, तो पहले से निर्मित और व्युत्पन्न-अवगत डेलेटर का उपयोग वस्तु के निपटान के लिए किया जाएगा।

C ++ 11 मानक में इस निर्माता (20.7.2.2.1) के बारे में कहने के लिए निम्नलिखित है:

आवश्यक है: p के लिए परिवर्तनीय होना चाहिए T*Yएक पूर्ण प्रकार होगा। अभिव्यक्ति delete pअच्छी तरह से बनाई जाएगी, अच्छी तरह से परिभाषित व्यवहार होगा और अपवाद नहीं फेंकेंगे।

प्रभाव: एक shared_ptrऑब्जेक्ट को इंगित करता है जो सूचक का मालिक है p

...

और विध्वंसक के लिए (20.7.2.2.2):

प्रभाव: तो *thisहै खाली दूसरे के साथ या शेयरों स्वामित्व shared_ptrउदाहरण ( use_count() > 1), वहाँ कोई दुष्प्रभाव होते हैं। अन्यथा, यदि *thisएक वस्तु का मालिक है pऔर एक Deleter d, d(p)कहा जाता है। अन्यथा, अगर *thisएक सूचक का मालिक है p, और delete pकहा जाता है।

(बोल्ड फ़ॉन्ट का उपयोग मेरा जोर है)।


the upcoming standard also requires this behaviour: (ए) आप किस मानक और (बी) के संदर्भ (मानक) प्रदान कर सकते हैं?
केविनरपे

जब से मेरे पास पर्याप्त अंक नहीं हैं, मैं सिर्फ @sellibitze के उत्तर में एक टिप्पणी जोड़ना चाहता हूं add a comment। IMO, यह इससे अधिक Boost does thisहै the Standard requires। मुझे नहीं लगता कि मानक को उस चीज की आवश्यकता है जो मैं समझ रहा हूं। @Sellibitze के उदाहरण के बारे में बात करते हुए shared_ptr<Base> sp (new Derived);, केवल अच्छी तरह से परिभाषित और अच्छी तरह से गठित होने के लिए पूछना आवश्यक है । के विनिर्देश के लिए , एक भी है , लेकिन मुझे नहीं लगता कि यह विनिर्देश के विनिर्देश में संदर्भित है । constructordelete Deriveddestructorppconstructor
लूजुन वेंग

28

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

उदाहरण के लिए, आप एक कस्टम डीलेटर बना सकते हैं:

void DeleteDerived(Derived* d) { delete d; } // EDIT: no conversion needed.

shared_ptr<Base> p(new Derived, DeleteDerived);

p नुकीली वस्तु को नष्ट करने के लिए DeleteDerived को कॉल करेगा। कार्यान्वयन यह स्वचालित रूप से करता है।


4
अपूर्ण प्रकार के बारे में टिप्पणी के लिए +1, shared_ptrएक विशेषता के रूप में उपयोग करते समय बहुत आसान है ।
मथिउ एम।

16

सीधे शब्दों में,

shared_ptr विशेष डिलेटर फ़ंक्शन का उपयोग करता है जो कि कंस्ट्रक्टर द्वारा बनाया गया है जो हमेशा दिए गए ऑब्जेक्ट के डिस्ट्रक्टर का उपयोग करता है न कि बेस के डिस्ट्रक्टर का, यह टेम्प्लेट मेटा प्रोग्रामिंग के साथ थोड़ा काम है, लेकिन यह काम करता है।

ऐसा कुछ

template<typename SomeType>
shared_ptr(SomeType *p)
{
   this->destroyer = destroyer_function<SomeType>(p);
   ...
}

1
हम्म ... दिलचस्प है, मैं इस पर विश्वास करना शुरू कर रहा हूं :)
अर्मेन Tsirunyan

1
@Armen Tsirunyan आपको चर्चा शुरू करने से पहले शेयर्ड_प्ट्र के डिज़ाइन विवरण पर ध्यान देना चाहिए। यह 'डिलेटर को पकड़ना' शेयर्ड_प्ट्र की जरूरी विशेषताओं में से एक है ...
पॉल माइकालिक

6
@ paul_71: मैं आपसे सहमत हूँ। दूसरी ओर मेरा मानना ​​है कि यह चर्चा न केवल मेरे लिए उपयोगी थी, बल्कि अन्य लोगों के लिए भी थी जो इस तथ्य को share_ptr के बारे में नहीं जानते थे। इसलिए मुझे लगता है कि इस सूत्र को वैसे भी शुरू करना बहुत बड़ा पाप नहीं था :)
आर्मेन त्सिरुयन

3
@ निश्चित रूप से नहीं। बल्कि, आपने यह साझा करने के लिए एक अच्छा काम किया है कि वास्तव में बहुत ही महत्वपूर्ण फीचर share_ptr <T> है जो अक्सर अनुभवी c ++ डेवलपर्स द्वारा भी ओवरसाइज़ किया जाता है।
पॉल मिशालिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.