2015-03-25 8 views
5

यहाँ इसने कई shared_ptr "परिवार" बनाने कुछ उदाहरण कोड (ऑनलाइन here) है:अप्रत्याशित व्यवहार जब एक ही वस्तु

#include <memory> 

struct Foo : public std::enable_shared_from_this<Foo> {}; 

void example() 
{ 
    auto sharedFoo = std::make_shared<Foo>(); 
    std::shared_ptr<Foo> nonDeletingSharedFoo(sharedFoo.get(), [](void*){}); 
    nonDeletingSharedFoo.reset(); 
    sharedFoo->shared_from_this(); // throws std::bad_weak_ptr 
} 

क्या मैं (कई compilers के तहत) को देखने के यह है कि जब nonDeletingSharedFoo रीसेट, है weak_ptr आंतरिक रूप से enable_shared_from_this द्वारा उपयोग किया जाता है, इसलिए shared_from_this पर आने वाली कॉल विफल हो जाती है।

मैं nonDeletingSharedFoo उम्मीद sharedFoo से एक पूरी तरह से अलग रेफरी गिनती के लिए के बाद से यह एक कच्चे सूचक से निर्माण किया गया था, लेकिन स्पष्ट रूप से यह अभी भी Foo वस्तु के आंतरिक weak_ptr के कमजोर गिनती प्रभावित कर रहा है। मुझे लगता है कि ऐसा इसलिए है क्योंकि shared_ptr कन्स्ट्रक्टर और/या विनाशक कुछ खास करते हैं जब इंगित करने के लिए प्रकार enable_shared_from_this लागू करता है।

तो क्या यह कोड मानक का उल्लंघन करता है? क्या कोई समाधान है, या क्या लागू करने वाले ऑब्जेक्ट पर एकाधिक shared_ptr "परिवार" होना संभव नहीं है?

+2

मुझे इस नोट को cppreference [प्रलेखन] (http://en.cppreference.com/w/cpp/memory/enable_shared_from_this) में 'enable_shared_from_this' के लिए मिला:" किसी ऑब्जेक्ट के लिए std :: shared_ptr का निर्माण करना पहले से ही किसी अन्य std :: shared_ptr द्वारा प्रबंधित किया गया आंतरिक रूप से संग्रहीत कमजोर संदर्भ से परामर्श नहीं करेगा और इस प्रकार अपरिभाषित व्यवहार का कारण बन जाएगा। " हालांकि, मुझे इसे वापस करने के लिए मानक में कुछ भी नहीं मिला, इसलिए मैं अब भी सोच रहा हूं कि यह वास्तव में अमान्य कोड है या सिर्फ एक ऐसा मामला है जो हर किसी को 'enable_shared_from_this' लागू करने के तरीके के कारण विफल रहता है। – dlf

उत्तर

2

आप एक ग्रे क्षेत्र यहाँ पर हैं: enable_shared_from_this आम तौर पर shared_ptr कंस्ट्रक्टर्स है कि एक वस्तु enable_shared_from_this से प्राप्त करने के लिए एक कच्चे सूचक का स्वामित्व लेने के होने से कार्यान्वित किया जाता है एक weak_ptr ऑब्जेक्ट के अंदर निहित निर्धारित किया है। इस प्रकार बाद में shared_from_this() पर कॉल करने के लिए कुछ है। जब आप sharedFoo पर "अभिभावक" मूल weak_ptr मान ओवरराइट किया जा रहा है ताकि अंत में shared_from_this पर कॉल करने पर एक कालबाह्य मूल्य हो।

यह संभव है कि इस व्यवहार को मानक द्वारा प्रतिबंधित किया गया हो, लेकिन मुझे लगता है कि यह इस बात की अधिक संभावना है कि इसकी अनुमति है और स्वामित्व के अर्थशास्त्र इस स्वीकार्य रूप से विशिष्ट कोने मामले में थोड़ा कम नहीं हैं। मानक नोट करता है कि "shared_ptr रचनाकार जो अद्वितीय पॉइंटर्स बनाते हैं, enable_shared_from_this आधार की उपस्थिति का पता लगा सकते हैं और नए shared_ptr को अपने __weak_this सदस्य को असाइन कर सकते हैं।" ([Util.smartptr.enab]/11)। इस तथ्य के बावजूद कि नोट गैर-मानक हैं, मुझे लगता है कि यह मानक के इरादे से बात करता है।

आपको लगता है कि स्वामित्व साझा नहीं करता है वास्तव में एक खाली shared_ptr बनाने लेकिन फिर भी द्वारा समस्या से बच सकते हैं sharedFoo में बताते हैं:

template<class Y> shared_ptr(const shared_ptr<Y>& r, T* p) noexcept; 
:

std::shared_ptr<Foo> nonDeletingSharedFoo(std::shared_ptr<Foo>(), sharedFoo.get()); 

यह "अलियासिंग" निर्माता का लाभ लेता है

जो shared_ptr बनाता है जो r के साथ स्वामित्व साझा करता है, इस मामले में एक खाली डिफ़ॉल्ट-निर्मित shared_ptr, औरपर अंक 10। नतीजा एक खाली (गैर-स्वामित्व वाला) shared_ptr है जो उसी वस्तु पर sharedFoo के रूप में इंगित करता है।

आप यह सुनिश्चित करने के लिए ज़िम्मेदार हैं कि इस तरह के एक गैर-मालिक सूचक को संदर्भित समाप्ति के जीवनकाल के बाद कभी भी संदर्भित नहीं किया जाता है। डिज़ाइन को साफ करना बेहतर होगा ताकि आप या तो स्वामित्व साझा कर सकें, या "गैर-स्वामित्व वाले साझा_प्टर" हैक के बजाय कच्चे सूचक का उपयोग करें।

+0

ऐसा लगता है कि चीजों को कवर करना (इसके लायक होने के लिए, नकली 'shared_ptr की केवल एक प्रमुख रिफैक्टरिंग के माध्यम से अंतर को दूर करने के लिए आवश्यक था और अंतिम डिजाइन में मौजूद नहीं होगा)। – dlf

0

आप sharedFoo (sharedFoo.get()) से प्राप्त का उपयोग करते हैं, तो आपको पता है कि shared_ptr के अंतर्गत होता है मिलता है। तो nonDeletingSharedFoo को shared_ptr तक पहुंच नहीं है और जब आप nonDeletingSharedFoo रीसेट करते हैं, तो आप पता स्मृति जारी करते हैं। तो की वस्तु साझा की गई अब मौजूद नहीं है।

+1

'nonDeletingSharedFoo' हालांकि वस्तु को हटा नहीं देता है। यह अभी भी स्मृति में मौजूद होना चाहिए। – lcs

संबंधित मुद्दे