जब std :: weak_ptr उपयोगी है?


268

मैंने C ++ 11 के स्मार्ट पॉइंटर्स का अध्ययन करना शुरू किया और मुझे इसका कोई उपयोगी उपयोग नहीं दिखता std::weak_ptr। क्या कोई मुझे बता सकता है कि std::weak_ptrउपयोगी / आवश्यक कब है?


जवाबों:


231

एक अच्छा उदाहरण कैश होगा।

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

लेकिन क्या होगा अगर वह वस्तु उपयोग में है और कोई अन्य कोड उसके लिए एक मजबूत संकेतक रखता है? यदि कैश को ऑब्जेक्ट के अपने एकमात्र पॉइंटर से छुटकारा मिल जाता है, तो वह इसे फिर से कभी नहीं पा सकता है। तो कैश उन वस्तुओं के लिए एक कमजोर संकेतक रखता है जिन्हें यह खोजने की आवश्यकता है कि क्या वे स्मृति में रहने के लिए होते हैं।

यह ठीक वही है जो एक कमजोर सूचक करता है - यह आपको एक वस्तु का पता लगाने की अनुमति देता है यदि यह अभी भी आस-पास है, लेकिन अगर इसे और कुछ नहीं चाहिए तो इसे आसपास नहीं रखें।


8
तो std :: aw_ptr केवल उसी बिंदु को इंगित कर सकता है जहां कोई अन्य सूचक इंगित करता है और यह इंगित करने के लिए nullptr को इंगित करता है जब किसी अन्य पॉइंटर्स द्वारा किसी अन्य बिंदु द्वारा हटाए गए / इंगित नहीं किए जाते हैं?

27
@ आरएम: असल में, हाँ। जब आपके पास एक कमजोर सूचक होता है, तो आप इसे एक मजबूत सूचक को बढ़ावा देने का प्रयास कर सकते हैं। यदि वह वस्तु अभी भी मौजूद है (क्योंकि कम से कम एक मजबूत सूचक अभी भी मौजूद है) तो वह ऑपरेशन सफल हो जाता है और आपको इसके लिए एक मजबूत सूचक देता है। यदि वह वस्तु मौजूद नहीं है (क्योंकि सभी मजबूत पॉइंटर्स चले गए), तो वह ऑपरेशन विफल हो जाता है (और आमतौर पर आप कमजोर पॉइंटर को फेंककर प्रतिक्रिया करते हैं)।
डेविड श्वार्ट्ज

12
जबकि एक मजबूत पॉइंटर किसी ऑब्जेक्ट को जीवित रखता है, एक कमजोर_प्राथर इसे देख सकता है ... ऑब्जेक्ट के जीवन समय के साथ मैकिंग के बिना।
विवान्डियर

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

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

299

std::weak_ptrलटकती सूचक समस्या को हल करने का एक बहुत अच्छा तरीका है। केवल कच्चे पॉइंटर्स का उपयोग करके यह जानना असंभव है कि संदर्भित डेटा डील किया गया है या नहीं। इसके बजाय, std::shared_ptrडेटा का प्रबंधन करने और डेटा के std::weak_ptrउपयोगकर्ताओं को आपूर्ति करने से, उपयोगकर्ता कॉल करके expired()या डेटा की वैधता की जांच कर सकते हैं lock()

आप std::shared_ptrअकेले ऐसा नहीं कर सकते थे, क्योंकि सभी std::shared_ptrउदाहरण डेटा के स्वामित्व को साझा करते हैं जो हटाए जाने के सभी उदाहरणों से पहले हटाए नहीं std::shared_ptrजाते हैं। यहाँ एक उदाहरण है कि कैसे झूलने वाले पॉइंटर का उपयोग करके जांच करें lock():

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}

1
ठीक है, यह ऐसा है जैसे कि आप स्थानीय रूप से एक (मालिक) सूचक को अशक्त करने के लिए सेट करें (मेमोरी हटाएं), उसी मेमोरी में अन्य सभी (कमजोर) पॉइंटर्स भी शून्य पर सेट हैं
Pat-Laugh

std::weak_ptr::lockएक नया बनाता है std::shared_ptrजो प्रबंधित ऑब्जेक्ट के स्वामित्व को साझा करता है।
साहिब यार

129

एक और जवाब, उम्मीद है कि सरल। (साथी गोगलर्स के लिए)

मान लीजिए आपके पास Teamऔर Memberवस्तुएं हैं।

जाहिर है कि यह एक संबंध है: Teamवस्तु को इसके संकेत मिलेंगे Members। और यह संभावना है कि सदस्यों को उनकी Teamवस्तु का बैक पॉइंटर भी होगा ।

तब आपके पास एक निर्भरता चक्र होता है। यदि आप उपयोग करते हैं shared_ptr, तो वस्तुओं को स्वचालित रूप से मुक्त नहीं किया जाएगा जब आप उन पर संदर्भ छोड़ देते हैं, क्योंकि वे एक दूसरे को चक्रीय तरीके से संदर्भित करते हैं। यह एक मेमोरी लीक है।

आप उपयोग करके इसे तोड़ते हैं weak_ptr। "स्वामी" आमतौर पर उपयोग करते हैं shared_ptrऔर "स्वामित्व" weak_ptrअपने माता-पिता के लिए उपयोग करते हैं , और इसे अस्थायी रूप से shared_ptrतब परिवर्तित करते हैं जब इसे अपने माता-पिता तक पहुंच की आवश्यकता होती है।

एक कमजोर ptr स्टोर करें:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

फिर जरूरत पड़ने पर इसका इस्तेमाल करें

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
  // yes, it may fail if the parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope

1
यह एक स्मृति रिसाव कैसे है? यदि टीम को नष्ट कर दिया जाता है, तो वह अपने सदस्यों को नष्ट कर देगा, इस प्रकार शेयर्ड_प्ट्र रेफरी संख्या 0 होगी और यह भी नष्ट हो जाएगी?
पॉलम

4
@paulm टीम "अपने" सदस्यों को नष्ट नहीं करेगी। संपूर्ण बिंदु shared_ptrस्वामित्व को साझा करना है, इसलिए किसी के पास स्मृति को मुक्त करने की कोई विशेष जिम्मेदारी नहीं है, यह स्वचालित रूप से मुक्त हो जाता है जब इसका उपयोग नहीं किया जाता है। जब तक कोई पाश नहीं है ... आपके पास एक ही खिलाड़ी (पिछली टीमों?) को साझा करने वाली कई टीमें हो सकती हैं। यदि टीम सदस्यों को "मालिक" करती है, तो shared_ptrशुरू करने के लिए ए का उपयोग करने की कोई आवश्यकता नहीं है ।
ऑफिरमो

1
यह उन्हें नष्ट नहीं करेगा, लेकिन इसके शेयर्ड_एप्ट्र इसके साथ स्कोप से बाहर चले जाएंगे, use_count को घटा देगा, इस प्रकार इस बिंदु का उपयोग_count 0 है और इसलिए शेयर्ड_ptr इसे क्या इंगित करेगा?
१६’१४

2
@ अंपुलम आप सही हैं। लेकिन चूंकि, इस उदाहरण में, टीम shared_ptrअपने "टीम के सदस्यों" द्वारा संदर्भित है, यह कब नष्ट हो जाएगी? आप जो वर्णन कर रहे हैं वह एक ऐसा मामला है जहां कोई लूप नहीं है।
ऑफिरमो

14
यह इतना बुरा नहीं है, मैं सोचूंगा। यदि कोई सदस्य कई टीमों से संबंधित हो सकता है, तो संदर्भ का उपयोग करने से काम नहीं चलेगा।
Mazyod

22

यहाँ एक उदाहरण दिया गया है, @jleahy द्वारा मुझे दिया गया: मान लीजिए कि आपके पास कार्यों का एक संग्रह है, जिसे एसिंक्रोनस रूप से निष्पादित किया गया है, और एक में प्रबंधित किया गया है std::shared_ptr<Task>। आप समय-समय पर उन कार्यों के साथ कुछ करना चाहते हो सकते हैं, इसलिए एक टाइमर घटना हो सकती है std::vector<std::weak_ptr<Task>>और कार्यों को कुछ करने के लिए दे सकती है। हालाँकि, एक साथ एक कार्य ने समवर्ती रूप से निर्णय लिया हो सकता है कि अब इसकी आवश्यकता नहीं है और मर जाते हैं। टाइमर इस प्रकार जाँच सकता है कि क्या कार्य अभी भी कमजोर पॉइंटर से एक साझा पॉइंटर बनाकर और उस साझा पॉइंटर का उपयोग करके जीवित है, बशर्ते यह अशक्त न हो।


4
: एक अच्छे उदाहरण की तरह लगता है लेकिन क्या आप कृपया विस्तार से बता सकते हैं कि उदाहरण थोड़ा और है? मैं सोच रहा हूं कि जब कोई कार्य समाप्त हो जाता है, तो इसे पहले ही बिना किसी आवधिक जांच के, पहले से ही हटा दिया जाना चाहिए: :: वेक्टर <std :: weak_ptr <टास्क >>। तो यकीन नहीं है कि अगर std :: वेक्टर <std :: weak_ptr <>> यहाँ बहुत मददगार है।
Gob00st

कतारों के साथ इसी तरह की टिप्पणी: कहते हैं कि आपके पास वस्तुएं हैं और आप उन्हें कुछ संसाधन के लिए कतार में लगाते हैं, प्रतीक्षा करते समय वस्तुओं को हटाया जा सकता है। इसलिए, यदि आप कमज़ोर_पार्ट्स को कतारबद्ध करते हैं, तो आपको वहाँ से प्रविष्टियाँ हटाने से परेशान होने की ज़रूरत नहीं है। Weak_ptrs को अमान्य कर दिया जाएगा और फिर एनकाउंटर किए जाने पर छोड़ दिया जाएगा।
zzz777

1
@ zzz777: ऑब्जेक्ट को अमान्य करने वाला तर्क शायद पर्यवेक्षकों की कतार या वेक्टर के अस्तित्व के बारे में भी नहीं जानता होगा। इसलिए पर्यवेक्षक कमजोर बिंदुओं पर एक अलग लूप करता है, जो अभी भी जीवित हैं, और कंटेनर से मृत लोगों को हटाने पर अभिनय कर रहा है ...
केरेक एसबी

1
@KerekSB: हाँ और कतार के मामले में आपके पास एक अलग लूप भी नहीं है - तब संसाधन उपलब्ध है जब तक आप वैध एक (यदि कोई हो) प्राप्त करने के लिए आप एक्सपायर हो चुके कमजोर_पार्ट (यदि कोई हो) को त्याग दें।
zzz777

आप थ्रेड्स को संग्रह से खुद को निकाल सकते हैं, लेकिन यह एक निर्भरता पैदा करेगा और लॉकिंग की आवश्यकता होगी।
23

16

वे Boost.Asio के साथ उपयोगी होते हैं जब आपको गारंटी नहीं दी जाती है कि एक एसिंक्रोनस हैंडलर के आह्वान पर लक्ष्य वस्तु अभी भी मौजूद है। चाल को weak_ptrअसंबद्ध हैंडलर ऑब्जेक्ट में बांधना है , लैम्बडा std::bindकैप्चर का उपयोग करना है ।

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}

यह self = shared_from_this()Boost.Asio उदाहरणों में अक्सर देखे जाने वाले मुहावरों का एक प्रकार है , जहां एक लंबित अतुल्यकालिक हैंडलर लक्ष्य ऑब्जेक्ट के जीवनकाल को लम्बा नहीं करेगा , फिर भी लक्ष्य ऑब्जेक्ट हटाए जाने पर अभी भी सुरक्षित है।


इस उत्तर को खोजने में इतना समय क्यों लगा ... PS आप अपने कब्जे का उपयोग नहीं कर रहे हैंthis
Orwellophile

@ ऑरवेलोफाइल तय। self = shared_from_this()मुहावरे का उपयोग करते समय आदत का बल जब हैंडलर उसी कक्षा के भीतर तरीकों का आह्वान करता है।
एमिल कॉर्मियर

16

share_ptr : असली वस्तु रखता है।

weak_ptr : lockवास्तविक मालिक से कनेक्ट करने के लिए उपयोग करता है या shared_ptrअन्यथा NULL देता है ।

कमजोर पीटीआर

मोटे तौर पर, weak_ptrभूमिका हाउसिंग एजेंसी की भूमिका के समान है । एजेंटों के बिना, किराए पर घर लेने के लिए हमें शहर में यादृच्छिक घरों की जांच करनी पड़ सकती है। एजेंट यह सुनिश्चित करते हैं कि हम केवल उन घरों का दौरा करें जो अभी भी सुलभ हैं और किराए पर उपलब्ध हैं


14

weak_ptrकिसी वस्तु के सही विलोपन की जांच करना भी अच्छा है - विशेषकर इकाई परीक्षणों में। विशिष्ट उपयोग मामला इस तरह दिख सकता है:

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());

13

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

  • कच्चे संकेत:
    • कच्चा सूचक [अर्थात SomeClass* ptrToSomeClass = new SomeClass();]
  • स्मार्ट संकेत:
    • अद्वितीय संकेत [यानी
      std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() );
      ]
    • साझा संकेत [यानी
      std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );
      ]
    • कमजोर संकेत [यानी
      std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr );
      ]

रॉ पॉइंटर्स (कभी-कभी "लीगेसी पॉइंटर्स", या "सी पॉइंटर्स" के रूप में संदर्भित) 'नंगे-हड्डियों' सूचक व्यवहार प्रदान करते हैं और बग और मेमोरी लीक का एक सामान्य स्रोत हैं। कच्चे पॉइंटर्स संसाधन के स्वामित्व पर नज़र रखने के लिए कोई साधन प्रदान नहीं करते हैं और डेवलपर्स को यह सुनिश्चित करने के लिए मैन्युअल रूप से 'डिलीट' करना होगा कि वे स्मृति रिसाव नहीं बना रहे हैं। यह मुश्किल हो जाता है यदि संसाधन साझा किया जाता है क्योंकि यह जानना चुनौतीपूर्ण हो सकता है कि क्या कोई वस्तु अभी भी संसाधन की ओर इशारा कर रही है। इन कारणों से, कच्चे पॉइंटर्स को आम तौर पर टाला जाना चाहिए और केवल सीमित दायरे वाले कोड के प्रदर्शन-महत्वपूर्ण खंडों में उपयोग किया जाता है।

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

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

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

  • आप व्यस्त हैं और मीटिंग को ओवरलैप कर रहे हैं: मीटिंग ए और मीटिंग बी
  • आप मीटिंग A में जाने का निर्णय लेते हैं और आपका सहकर्मी मीटिंग B में जाता है
  • आप अपने सहकर्मी से कहते हैं कि यदि मीटिंग A समाप्त होने के बाद भी मीटिंग B चल रही है, तो आप शामिल हो जाएंगे
  • निम्नलिखित दो परिदृश्य खेल सकते हैं:
    • मीटिंग ए समाप्त और मीटिंग बी अभी भी चल रही है, इसलिए आप शामिल हों
    • मीटिंग ए समाप्त और मीटिंग बी भी समाप्त हो गई है, इसलिए आप शामिल नहीं हो सकते

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

उदाहरण दिखाता है कि एक कमजोर सूचक कैसे काम करता है और उपयोगी है जब किसी वस्तु को बाहरी पर्यवेक्षक होने की आवश्यकता होती है , लेकिन स्वामित्व साझा करने की जिम्मेदारी नहीं चाहता है। यह इस परिदृश्य में विशेष रूप से उपयोगी है कि दो वस्तुओं को एक दूसरे को इंगित करने की आवश्यकता है (उर्फ एक परिपत्र संदर्भ)। साझा पॉइंटर्स के साथ, न तो ऑब्जेक्ट को जारी किया जा सकता है क्योंकि वे अभी भी अन्य ऑब्जेक्ट द्वारा इंगित 'दृढ़ता से' हैं। जब बिंदु में से एक कमजोर सूचक होता है, तो कमजोर सूचक रखने वाली वस्तु जरूरत पड़ने पर भी दूसरी वस्तु तक पहुंच सकती है, बशर्ते वह अभी भी मौजूद हो।


6

अन्य पहले से ही मान्य उपयोग के मामलों के अलावा std::weak_ptrएक बहुआयामी वातावरण में एक भयानक उपकरण है, क्योंकि

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

एक निर्देशिका की सभी छवियों (~ 10.000) को एक साथ मेमोरी में लोड करने के लिए एक कार्य पर विचार करें (उदाहरण के लिए थंबनेल कैश)। जाहिर है ऐसा करने का सबसे अच्छा तरीका एक नियंत्रण धागा है, जो छवियों को संभालता है और प्रबंधित करता है, और कई कार्यकर्ता धागे, जो छवियों को लोड करते हैं। अब यह एक आसान काम है। यहाँ एक बहुत ही सरल कार्यान्वयन है (join() आदि को छोड़ दिया गया है, थ्रेड्स को वास्तविक कार्यान्वयन आदि में अलग तरीके से संभाला जाना होगा)

// a simplified class to hold the thumbnail and data
struct ImageData {
  std::string path;
  std::unique_ptr<YourFavoriteImageLibData> image;
};

// a simplified reader fn
void read( std::vector<std::shared_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageData : imagesToLoad )
     imageData->image = YourFavoriteImageLib::load( imageData->path );
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::shared_ptr<ImageData>>> splitDatas = 
        splitImageDatas( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

लेकिन यह बहुत अधिक जटिल हो जाता है, यदि आप छवियों के लोडिंग को बाधित करना चाहते हैं, उदाहरण के लिए क्योंकि उपयोगकर्ता ने एक अलग निर्देशिका चुनी है। या यहां तक ​​कि अगर आप प्रबंधक को नष्ट करना चाहते हैं।

इससे पहले कि आप अपना बदलाव कर सकें, आपको थ्रेड कम्युनिकेशन की आवश्यकता होगी और सभी लोडर थ्रेड्स को रोकना होगा m_imageDatas क्षेत्र । अन्यथा लोडर तब तक लोड करते रहेंगे जब तक कि सभी चित्र नहीं हो जाते - भले ही वे पहले से ही अप्रचलित हों। सरलीकृत उदाहरण में, यह बहुत कठिन नहीं होगा, लेकिन वास्तविक वातावरण में चीजें बहुत अधिक जटिल हो सकती हैं।

धागे संभवतः कई प्रबंधकों द्वारा उपयोग किए जाने वाले थ्रेड पूल का हिस्सा होंगे, जिनमें से कुछ को रोका जा रहा है, और कुछ नहीं हैं आदि। सरल पैरामीटर imagesToLoadएक बंद कतार होगी, जिसमें वे प्रबंधक अलग-अलग नियंत्रण थ्रेड्स से अपनी छवि अनुरोधों को धक्का देते हैं। पाठकों को अनुरोधों के साथ - एक अनियंत्रित क्रम में - दूसरे छोर पर। और इसलिए संचार कठिन, धीमा और त्रुटि रहित हो जाता है। ऐसे मामलों में किसी भी अतिरिक्त संचार से बचने के लिए एक बहुत ही सुंदर तरीका है, std::shared_ptrजिसके साथ संयोजन के रूप में उपयोग करना है std::weak_ptr

// a simplified reader fn
void read( std::vector<std::weak_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageDataWeak : imagesToLoad ) {
     std::shared_ptr<ImageData> imageData = imageDataWeak.lock();
     if( !imageData )
        continue;
     imageData->image = YourFavoriteImageLib::load( imageData->path );
   }
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::weak_ptr<ImageData>>> splitDatas = 
        splitImageDatasToWeak( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

यह कार्यान्वयन लगभग पहले के रूप में आसान है, किसी भी अतिरिक्त थ्रेड संचार की आवश्यकता नहीं है, और एक वास्तविक कार्यान्वयन में थ्रेड पूल / कतार का हिस्सा हो सकता है। चूंकि एक्सपायर्ड इमेजेज को छोड़ दिया जाता है, और नॉन-एक्सपायर्ड इमेजेस को प्रोसेस किया जाता है, इसलिए थ्रेड्स को कभी भी नॉर्मल ऑपरेशन के दौरान बंद नहीं करना पड़ेगा। यदि पाठक के पास सूचक समाप्त नहीं हुआ है, तो आप हमेशा सुरक्षित रूप से पथ बदल सकते हैं या अपने प्रबंधकों को नष्ट कर सकते हैं।


2

http://en.cppreference.com/w/cpp/memory/weak_ptr std :: weak_ptr एक स्मार्ट पॉइंटर है जो एक ऑब्जेक्ट के लिए एक गैर-स्वामित्व ("कमजोर") संदर्भ रखता है जिसे std द्वारा प्रबंधित किया जाता है - Share_ptr। संदर्भित ऑब्जेक्ट तक पहुँचने के लिए इसे std :: shared_ptr में परिवर्तित किया जाना चाहिए।

std :: weak_ptr मॉडल अस्थायी स्वामित्व: जब किसी वस्तु को केवल तभी मौजूद होना चाहिए, जब वह मौजूद हो, और उसे किसी अन्य व्यक्ति द्वारा किसी भी समय हटा दिया जा सकता है, तो std :: weak_ptr का उपयोग वस्तु को ट्रैक करने के लिए किया जाता है, और इसे std में बदल दिया जाता है: : साझा स्वामित्व को अस्थायी स्वामित्व मानने के लिए। यदि मूल std :: साझा_ptr इस समय नष्ट हो जाता है, तो वस्तु का जीवनकाल तब तक बढ़ाया जाता है जब तक कि अस्थायी std :: साझा_ptr नष्ट नहीं हो जाता।

इसके अलावा, std :: weak_ptr का उपयोग std के सर्कुलर रेफरेंस को तोड़ने के लिए किया जाता है :: share_ptr।


" परिपत्र संदर्भ तोड़ने के लिए " कैसे?
जिज्ञासु

2

साझा सूचक की एक खामी है: साझा_पॉइंट पैरेंट-चाइल्ड चक्र निर्भरता को संभाल नहीं सकता है। यदि माता-पिता वर्ग साझा पॉइंटर का उपयोग करके चाइल्ड क्लास के ऑब्जेक्ट का उपयोग करता है, तो उसी फाइल में यदि चाइल्ड क्लास पैरेंट क्लास के ऑब्जेक्ट का उपयोग करता है। साझा सूचक सभी वस्तुओं को नष्ट करने में विफल हो जाएगा, यहां तक ​​कि साझा सूचक चक्र निर्भरता परिदृश्य में विध्वंसक को कॉल करने में बिल्कुल भी नहीं है। मूल रूप से साझा किए गए पॉइंटर संदर्भ गणना तंत्र का समर्थन नहीं करते हैं।

यह कमी हम कमजोर_पॉइंटर्स का उपयोग करके दूर कर सकते हैं।


एक कमजोर संदर्भ एक परिपत्र निर्भरता के साथ कैसे निपट सकता है?
जिज्ञासु ११'१६

1
@ चर्चियस, एक बच्चा माता-पिता के लिए एक कमजोर संदर्भ नियुक्त करता है, तब माता-पिता को तब निपटाया जा सकता है जब कोई साझा (मजबूत) संदर्भ न हो। इस प्रकार जब बच्चे के माध्यम से माता-पिता तक पहुंच होती है, तो कमजोर संदर्भ को यह देखने के लिए परीक्षण करना पड़ता है कि क्या माता-पिता अभी भी उपलब्ध हैं। वैकल्पिक रूप से उस अतिरिक्त स्थिति से बचने के लिए, एक वृत्ताकार संदर्भ अनुरेखण तंत्र (या तो मार्क-स्वीप या रिफ़काउंट घटने पर जांच करना, दोनों जिनमें खराब एसिम्प्टोटिक प्रदर्शन है) परिपत्र साझा संदर्भों को तोड़ सकते हैं जब माता-पिता और बच्चे में से केवल एक ही साझा संदर्भ हो। अन्य।
शेल्बी मूर तृतीय

@ShelbyMooreIII " को यह देखने के लिए परीक्षण करना होगा कि क्या माता-पिता अभी भी उपलब्ध हैं " हाँ, और आपको अनुपलब्ध मामले में सही ढंग से प्रतिक्रिया करने में सक्षम होना चाहिए! जो एक वास्तविक (यानी मजबूत) रेफ के साथ नहीं होता है। जिसका अर्थ है कि कमजोर रेफ प्रतिस्थापन में एक बूंद नहीं है: इसके लिए तर्क में बदलाव की आवश्यकता होती है।
२०:२०

2
@curiousguy से आपने यह नहीं पूछा " weak_ptrड्रॉप में प्रतिस्थापन के रूप में प्रोग्राम लॉजिक में कोई बदलाव नहीं होने के साथ एक परिपत्र निर्भरता के साथ एक सौदा कैसे हो सकता है shared_ptr?" :-)
शेल्बी मूर तृतीय

2

जब हम वस्तु का मालिक नहीं होना चाहते हैं:

उदाहरण के लिए:

class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}

उपरोक्त वर्ग में wPtr1 wPtr1 द्वारा इंगित संसाधन का मालिक नहीं है। यदि संसाधन हटा दिया गया है तो wPtr1 की समय सीमा समाप्त हो गई है।

परिपत्र निर्भरता से बचने के लिए:

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

अब अगर हम वर्ग B और A का साझा_प्रात बनाते हैं, तो दोनों सूचक का उपयोग_काउंट दो है।

जब share_ptr ओड स्कोप से बाहर हो जाता है तो काउंट अभी भी 1 ही रहता है और इसलिए A और B ऑब्जेक्ट डिलीट नहीं होता है।

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

उत्पादन:

A()
B()

जैसा कि हम आउटपुट से देख सकते हैं कि ए और बी पॉइंटर कभी डिलीट नहीं होते हैं और इसलिए मेमोरी लीक होती है।

इस तरह के मुद्दे से बचने के लिए, केवल साझा_प्रतिमा के बजाय कक्षा A में weak_ptr का उपयोग करें जो अधिक समझ में आता है।


2

मैं देख रहा हूँ std::weak_ptr<T>एक के रूप में संभाल एक करने के लिए std::shared_ptr<T>: यह मुझे पाने के लिए अनुमति देता है std::shared_ptr<T>अगर यह अभी भी मौजूद है, लेकिन यह अपने जीवन का विस्तार नहीं होगा। इस तरह के दृष्टिकोण उपयोगी होने पर कई परिदृश्य हैं:

// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;

// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.

struct Widget {
    std::weak_ptr< Texture > texture_handle;
    void render() {
        if (auto texture = texture_handle.get(); texture) {
            // do stuff with texture. Warning: `texture`
            // is now extending the lifetime because it
            // is a std::shared_ptr< Texture >.
        } else {
            // gracefully degrade; there's no texture.
        }
    }
};

एक अन्य महत्वपूर्ण परिदृश्य डेटा संरचनाओं में चक्रों को तोड़ना है।

// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > next;
    std::shared_ptr< Node > prev;
};

// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::shared_ptr< Node > next;
    std::weak_ptr< Node > prev;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::weak_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

हर्ब सटर में एक उत्कृष्ट बात है जो डिफ़ॉल्ट रूप से लीक फ्रीडम सुनिश्चित करने के लिए भाषा सुविधाओं (इस मामले में स्मार्ट पॉइंटर्स) का सबसे अच्छा उपयोग बताती है (मतलब: निर्माण द्वारा सभी कुछ क्लिक करता है; आप शायद ही इसे खराब कर सकते हैं)। यह एक घड़ी है।

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