कस्टम आवंटनकर्ता द्वारा साझा की गई मेमोरी में एक साझा_प्राथर डिलेटर संग्रहीत है?


22

कहें कि मेरे पास एक shared_ptrकस्टम आवंटनकर्ता और एक कस्टम डेलेटर है।

मुझे मानक में कुछ भी नहीं मिला जो इस बात पर चर्चा करता है कि डिलेटर को कहाँ संग्रहीत किया जाना चाहिए: यह नहीं कहता है कि कस्टम एलोलेटर का उपयोग डेलेटर की मेमोरी के लिए किया जाएगा, और यह नहीं कहता है कि यह नहीं होगा।

क्या यह अनिर्दिष्ट है या मैं सिर्फ कुछ याद कर रहा हूं?

जवाबों:


11

C ++ 11 में use.smartptr.saring.const / 9:

प्रभाव: एक साझा_प्रोट ऑब्जेक्ट का निर्माण करता है जो ऑब्जेक्ट पी और डीलेटर डी का मालिक है। दूसरे और चौथे निर्माणकर्ता आंतरिक उपयोग के लिए मेमोरी आवंटित करने के लिए एक की एक प्रति का उपयोग करेंगे।

दूसरे और चौथे निर्माणकर्ताओं के पास ये प्रोटोटाइप हैं:

template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template<class D, class A> shared_ptr(nullptr_t p, D d, A a);

नवीनतम मसौदा में, हमारे उद्देश्य के लिए उपयोग .smartptr.saring.const / 10 समतुल्य है:

प्रभाव: एक साझा_प्रोट ऑब्जेक्ट का निर्माण करता है जो ऑब्जेक्ट पी और डीलेटर डी का मालिक है। जब T एक सरणी प्रकार नहीं है, तो पहले और दूसरे निर्माणकर्ता p के साथ साझा_फ्रेम_थिस को सक्षम करते हैं। दूसरे और चौथे निर्माणकर्ता आंतरिक उपयोग के लिए मेमोरी आवंटित करने के लिए एक की एक प्रति का उपयोग करेंगे। यदि कोई अपवाद फेंका जाता है, तो d (p) कहा जाता है।

तो आवंटितकर्ता का उपयोग किया जाता है यदि आवंटित मेमोरी में इसे आवंटित करने की आवश्यकता होती है। वर्तमान मानक और प्रासंगिक दोष रिपोर्टों के आधार पर, आवंटन अनिवार्य नहीं है, लेकिन समिति द्वारा माना जाता है।

  • के इंटरफेस हालांकि shared_ptrएक कार्यान्वयन जहां कभी नहीं एक नियंत्रण ब्लॉक और सभी की अनुमति देता है shared_ptrऔर weak_ptrएक लिंक्ड सूची में डाल दिया गया है, इसलिए व्यवहार में ऐसी कोई कार्यान्वयन है। इसके अतिरिक्त, शब्दों को संशोधित किया गया है, उदाहरण के लिए, use_countयह साझा किया गया है।

  • डीलेटर केवल रचनात्मक रूप से स्थानांतरित करने के लिए आवश्यक है। इस प्रकार, कई प्रतियों में होना संभव नहीं है shared_ptr

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

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


2
"आंतरिक उपयोग के लिए मेमोरी आवंटित करने के लिए" क्या होगा यदि कार्यान्वयन आंतरिक उपयोग के लिए मेमोरी को आवंटित करने के लिए नहीं जा रहा है? यह एक सदस्य का उपयोग कर सकता है।
LF

1
@LF यह नहीं कर सकता, इंटरफ़ेस इसके लिए अनुमति नहीं देता है।
एपीग्रामग्राम

सैद्धांतिक रूप से, यह अभी भी "छोटे डेलेटर अनुकूलन" के कुछ प्रकार का उपयोग कर सकता है, है ना?
LF

क्या अजीब है कि मैं एक ही संभाजक (की नकल का उपयोग कर के बारे में कुछ भी नहीं मिल सकता है aके लिए) पुनःआवंटन कि स्मृति। जिससे उस कॉपी का कुछ भंडारण हो जाएगा a। इसके बारे में कोई जानकारी [use.smartptr.sared.dest] में नहीं है।
डैनियल लैंगर

1
@DanielsaysreinstateMonica, मुझे आश्चर्य है कि अगर if.smartptr.sared / 1 में उपयोग किया जाता है: "share_ptr वर्ग टेम्पलेट एक पॉइंटर को संग्रहीत करता है, जो आमतौर पर नए स्वामित्व के माध्यम से प्राप्त होता है। शेयर्ड_ptr साझा स्वामित्व के शब्दार्थ को लागू करता है, पॉइंटर का अंतिम शेष स्वामी ऑब्जेक्ट को नष्ट करने के लिए जिम्मेदार होता है। या अन्यथा संग्रहीत पॉइंटर से जुड़े संसाधनों को जारी करना। " संग्रहीत पॉइंटर से जुड़े संसाधनों को जारी करना उसके लिए अभिप्रेत नहीं है। लेकिन नियंत्रण खंड को तब तक जीवित रहना चाहिए जब तक कि अंतिम कमजोर सूचक हटा नहीं दिया जाता है।
एपीग्रामग्राम

4

से std :: shared_ptr हमने:

नियंत्रण ब्लॉक एक गतिशील रूप से आवंटित वस्तु है जो रखती है:

  • या तो प्रबंधित ऑब्जेक्ट के लिए एक संकेतक या स्वयं प्रबंधित ऑब्जेक्ट;
  • डीलेटर (टाइप-मिट);
  • आवंटनकर्ता (टाइप-मिट);
  • शेयर्ड_पार्ट्स की संख्या जो प्रबंधित ऑब्जेक्ट का मालिक है;
  • प्रबंधित ऑब्जेक्ट को संदर्भित करने वाले weak_ptrs की संख्या।

और std से : हमें आवंटित करें :

template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );

प्रकार T की एक वस्तु का निर्माण करता है और इसे एक साझा std :: share_ptr [...] में साझा करता है ताकि दोनों सूचक और T ऑब्जेक्ट के नियंत्रण खंड के लिए एक आवंटन का उपयोग किया जा सके ।

तो यह एसटीडी की तरह दिखता है :: आबंटित_समेद को deleterआपके साथ आबंटित होना चाहिए Alloc

संपादित करें: और n4810.1120.11.3.6 से [निर्माण।

1 आम आवश्यकताओं कि सभी पर लागू होते हैं make_shared, allocate_shared, make_shared_default_init, और allocate_shared_default_initभार के, जब तक अन्यथा निर्दिष्ट, नीचे वर्णित हैं।

[...]

7 टिप्पणी: (7.1) - कार्यान्वयन एक से अधिक मेमोरी आवंटन नहीं करना चाहिए। [नोट: यह एक घुसपैठिया स्मार्ट पॉइंटर के बराबर दक्षता प्रदान करता है। ध्यान दें]

[सभी मेरा जोर]

तो मानक कह रहा है कि नियंत्रण ब्लॉक के लिए उपयोग std::allocate_shared करना चाहिएAlloc


1
मुझे खेद है कि cppreference एक आदर्श पाठ नहीं है। यह एक महान संसाधन है, लेकिन जरूरी नहीं कि भाषा-वकील के सवालों के लिए।
स्टोरीटेलर - अनसलैंडर मोनिका

@ StoryTeller-UnslanderMonica पूरी तरह से सहमत हैं - नवीनतम मानक के माध्यम से देखा और ऐसा कुछ भी नहीं पाया जिससे cppreference के साथ चला गया।
पॉल इवांस


मिला n4810और अद्यतन जवाब।
पॉल इवांस

1
हालांकि, यह make_sharedनिर्माणकर्ताओं के बारे में नहीं बल्कि खुद के बारे में बात कर रहा है । फिर भी, मैं छोटे हटाने वालों के लिए एक सदस्य का उपयोग कर सकता हूं।
LF

3

मेरा मानना ​​है कि यह अनिर्दिष्ट है।

यहां संबंधित कंस्ट्रक्टर्स के विनिर्देशन हैं: [use.smartptr.sared.const] / 10

template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);

प्रभाव: एक ऐसी shared_­ptrवस्तु का निर्माण करता है जो वस्तु pऔर निपुणता का स्वामी है d। जब Tएक सरणी प्रकार नहीं होता है, तो पहला और दूसरा निर्माणकर्ता सक्षम shared_­from_­thisहोता है p। दूसरे और चौथे निर्माणकर्ता आंतरिक उपयोग के लिए मेमोरी आवंटित करने के aलिए एक प्रति का उपयोग करेंगे । यदि एक अपवाद फेंक दिया d(p)जाता है , तो कहा जाता है।

अब, मेरी व्याख्या यह है कि जब कार्यान्वयन को आंतरिक उपयोग के लिए मेमोरी की आवश्यकता होती है, तो यह उपयोग करके ऐसा करता है a। इसका मतलब यह नहीं है कि कार्यान्वयन को इस मेमोरी का उपयोग सब कुछ करने के लिए करना है। उदाहरण के लिए, मान लीजिए कि यह अजीब कार्यान्वयन है:

template <typename T>
class shared_ptr : /* ... */ {
    // ...
    std::aligned_storage<16> _Small_deleter;
    // ...
public:
    // ...
    template <class _D, class _A>
    shared_ptr(nullptr_t, _D __d, _A __a) // for example
        : _Allocator_base{__a}
    {
        if constexpr (sizeof(_D) <= 16)
            _Construct_at(&_Small_deleter, std::move(__d));
        else
            // use 'a' to allocate storage for the deleter
    }
// ...
};

क्या यह कार्यान्वयन " aआंतरिक उपयोग के लिए मेमोरी आवंटित करने की एक प्रति" का उपयोग करता है ? हाँ यह करता है। यह कभी-कभी उपयोग करके स्मृति को आवंटित नहीं करता है a। इस भोले-भाले कार्यान्वयन के साथ कई समस्याएं हैं, लेकिन हम कहते हैं कि यह आवंटितकर्ताओं का उपयोग करने के लिए स्विच करता है लेकिन सबसे सरल मामला है जिसमें shared_ptrसीधे एक पॉइंटर से निर्माण किया जाता है और इसे कभी भी कॉपी या स्थानांतरित या अन्यथा संदर्भित नहीं किया जाता है और कोई अन्य जटिलताएं नहीं होती हैं। मुद्दा यह है, सिर्फ इसलिए कि हम एक मान्य कार्यान्वयन की कल्पना करने में विफल रहते हैं, यह साबित नहीं करता कि यह सैद्धांतिक रूप से मौजूद नहीं है। मैं यह नहीं कह रहा हूं कि ऐसा कार्यान्वयन वास्तव में वास्तविक दुनिया में पाया जा सकता है, बस यह मानक सक्रिय रूप से इसे प्रतिबंधित नहीं करता है।


IMO आपके shared_ptrछोटे प्रकारों के लिए स्टैक पर मेमोरी आवंटित करता है। और इसलिए मानक आवश्यकताओं को पूरा नहीं करता है
बार्टॉप

1
@bartop यह स्टैक पर किसी भी मेमोरी को "आवंटित" नहीं करता है। _Smaller_deleter बिना शर्त साझा किए गए_प्रत्र के प्रतिनिधित्व का एक हिस्सा है। इस स्थान पर एक निर्माता को बुलाने का मतलब कुछ भी आवंटित करना नहीं है। अन्यथा, यहां तक ​​कि नियंत्रण ब्लॉक के लिए एक पॉइंटर पकड़कर "मेमोरी आवंटित करना" सही है? :-)
LF

लेकिन डेलीटर की नकल करने की आवश्यकता नहीं है, तो यह कैसे काम करेगा?
निकोल बोल्स

@NicolBolas उम्म ... का उपयोग करें std::move(__d), और allocateजब प्रतिलिपि की आवश्यकता है वापस गिर जाते हैं ।
LF
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.