2009-03-18 13 views
22

के सक्षम_shared_from_this को कैसे सक्षम करें मेरे पास सरल आधार और व्युत्पन्न कक्षा है जो मैं चाहता हूं कि दोनों shared_from_this() हों।दोनों माता-पिता और derived

यह सरल उपाय: "googling" के बाद

shared_ptr<foo> ptr(shared_ptr<foo>(new bar1)); 
function<void()> f=ptr->get_callback(); 
f(); 

तो मैं समाधान निम्नलिखित पाया है:

class foo : public enable_shared_from_this<foo> { 
    void foo_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&foo::foo_do_it,shared_from_this()); 
    } 
    virtual ~foo() {}; 
}; 

class bar1 : public foo , public enable_shared_from_this<bar1> { 
    using enable_shared_from_this<bar1>::shared_from_this; 
    void bar1_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&bar1::bar1_do_it,shared_from_this()); 
    } 
}; 

निम्नलिखित कोड में अपवाद tr1::bad_weak_ptr कारण

class bar2 : public foo { 
    void bar2_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
    shared_ptr<bar2> shared_from_this() 
    { 
     return boost::static_pointer_cast<bar2>(foo::shared_from_this()); 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&bar2::bar2_do_it,shared_from_this()); 
    } 
}; 

और अब यह काम करता है।

क्या माता-पिता और बच्चे दोनों के लिए enable_shared_from_this पर कोई बेहतर और अधिक समृद्ध और सही तरीका है?

धन्यवाद

उत्तर

10

क्षमा करें, लेकिन ऐसा नहीं है।

समस्या यह है कि shared_ptr<foo> और shared_ptr<bar1> विभिन्न प्रकार हैं। मुझे हुड के नीचे जो कुछ भी हो रहा है, उसे समझ में नहीं आता है, लेकिन सोचता है कि जब निर्माता कन्वर्टर लौटाता है और shared_ptr<foo> पर असाइन किया जाता है, तो आंतरिक weak_ptr<bar1> देखता है कि कुछ भी इसके लिए इंगित नहीं कर रहा है (क्योंकि केवल shared_ptr<bar1> काउंटर में वृद्धि करेगा) और खुद को रीसेट करता है। जब आप को get_callback पर कॉल करते हैं, तो आपको अपवाद मिलता है क्योंकि आंतरिक weak_ptr कुछ भी इंगित नहीं कर रहा है।

अनिवार्य रूप से, enable_shared_from_this केवल पदानुक्रम में एक वर्ग से पारदर्शी रूप से काम करने लगता है। यदि आप implementing it manually आज़माते हैं, तो समस्या स्पष्ट होनी चाहिए।

+2

मैं इसे स्वयं को लागू करने की कोशिश की है, और यह वास्तव में उस मुश्किल नहीं है। 'My :: SmartPointer 'में, जांचें कि टी' My :: enableSmartFromThis' से लिया गया है या नहीं। यदि ऐसा है, तो ढेर पर एक संदर्भ काउंटर आवंटित न करें, लेकिन 'My :: enableSmartFromThis' के सदस्यों का उपयोग करें। अब 'मेरा :: GetSmartFromThis (यह)' मामूली हो जाता है, यह '' 'को' My :: enableSmartFromThis * 'में परिवर्तित कर सकता है और मौजूदा संदर्भ गणना पा सकता है। आप यह भी जांच सकते हैं कि क्या आप बेस से व्युत्पन्न में कनवर्ट करने का प्रयास कर रहे हैं, जब केवल व्युत्पन्न 'My :: enableSmartFromThis' से प्राप्त होता है। – MSalters

22

ओपी समाधान बेस क्लास पर निम्न परिभाषित करके अधिक सुविधाजनक बनाया जा सकता है।

protected: 
    template <typename Derived> 
    std::shared_ptr<Derived> shared_from_base() 
    { 
     return std::static_pointer_cast<Derived>(shared_from_this()); 
    } 
+1

यह बहुत सुंदर है। +1 – sehe

2

ऐसा ही एक समाधान @evoskuil है कि व्युत्पन्न वर्ग में बॉयलरप्लेट कम कर देता है आप एक shared_from_this() समारोह लागू करना चाहते हैं चाहिए।

संपादित करें: क्लीनर समाधान। कक्षा के बाहर के कार्यों का उपयोग कर। यह और विकल्प भी प्रदान करता है और कक्षाओं के उपयोग के लिए एक पैटर्न प्रदान करता है, जिनके इंटरफ़ेस को संशोधित नहीं किया जा सकता है लेकिन enable_shared_from_this से प्राप्त होता है। यह पैटर्न shared_from(that) है।

नोट: auto का उपयोग यहां वापसी प्रकारों के लिए आपके कंपाइलर की आयु पर निर्भर करेगा।

template <typename Base> 
inline std::shared_ptr<Base> 
shared_from_base(std::enable_shared_from_this<Base>* base) 
{ 
    return base->shared_from_this(); 
} 
template <typename Base> 
inline std::shared_ptr<const Base> 
shared_from_base(std::enable_shared_from_this<Base> const* base) 
{ 
    return base->shared_from_this(); 
} 
template <typename That> 
inline std::shared_ptr<That> 
shared_from(That* that) 
{ 
    return std::static_pointer_cast<That>(shared_from_base(that)); 
} 

कार्रवाई में उदाहरण::

struct base : public std::enable_shared_from_this<base> {}; 
struct derived : public base 
{ 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
    // Can also provide a version for const: 
    auto shared_from_this() const { 
     return shared_from(this); 
    } 
    // Note that it is also possible to use shared_from(...) from 
    // outside the class, e.g. 
    // auto sp = shared_from(that); 
}; 
template <typename X> 
struct derived_x : public derived 
{ 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
}; 

संकलन परीक्षण:

int main() 
{ 
    auto pbase = std::make_shared<base>(); 
    auto pderived = std::make_shared<derived>(); 
    auto pderived_x = std::make_shared<derived_x<int> >(); 

    auto const& const_pderived = *pderived; 
    const_pderived.shared_from_this(); 

    std::shared_ptr<base> test1 = pbase->shared_from_this(); 
    std::shared_ptr<derived> test2 = pderived->shared_from_this(); 
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this(); 

    return 0; 
} 

यह कुछ बाहरी परत कार्यों कि एक पुस्तकालय शीर्षक में रखा जा सकता है की आवश्यकता है

https://onlinegdb.com/SJWM5CYIG

पहले समाधान - 'सामान्य' वर्गों और टेम्पलेट वर्गों के लिए आवश्यक कार्यान्वयन के बीच विशेष रूप से गैर एकरूपता - इस आधार वर्ग जो कुछ समस्या थी में कार्य रखा।
इसके अतिरिक्त बेस क्लास में कार्यान्वयन को नए वर्ग पदानुक्रमों के लिए दोहराया जाना चाहिए जो कि DRY नहीं है। इसके अलावा बेस क्लास फ़ंक्शन किसी भिन्न ऑब्जेक्ट से बेस क्लास पॉइंटर की आपूर्ति करके दुरुपयोग की संभावना से ग्रस्त था। ऊपर की नई योजना पूरी तरह से बचाती है और रनटाइम जोर (...) चेक जाता है।

पुरानी कार्यान्वयन:

#include <cassert> 
#include <memory> 

class base : public std::enable_shared_from_this<base> 
{ 
protected: 
    template <typename T> 
    std::shared_ptr<T> shared_from(T* derived) { 
     assert(this == derived); 
     return std::static_pointer_cast<T>(shared_from_this()); 
    } 
}; 

class derived : public base 
{ 
public: 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
}; 

template <typename X> 
class derived_x : public derived 
{ 
public: 
    auto shared_from_this() { 
     return this->template shared_from(this); 
    } 
}; 

int main() 
{ 
    auto pbase = std::make_shared<base>(); 
    auto pderived = std::make_shared<derived>(); 
    auto pderived_x = std::make_shared<derived_x<int> >(); 

    std::shared_ptr<base> test1 = pbase->shared_from_this(); 
    std::shared_ptr<derived> test2 = pderived->shared_from_this(); 
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this(); 

    return 0; 
} 
+0

आपने 'derived_x' में' टेम्पलेट' कीवर्ड का उपयोग क्यों किया? – Myon

+1

@Myon ऐसा इसलिए है क्योंकि share_from बेस क्लास में टेम्पलेट सदस्य फ़ंक्शन है और इसे टेम्पलेट क्लास के भीतर से कॉल किया जा रहा है। यहां 'इस' या 'टेम्पलेट' कीवर्ड के बिना यह अनुपालन कंपाइलरों पर संकलन करने में विफल रहेगा। एमएसवीसी का पुराना संस्करण शायद इस जुर्माना को संकलित करेगा। इसके बिना संकलक '<' प्रतीक को 'कम से कम' ऑपरेटर के रूप में व्याख्या करेगा। वर्तमान ड्राफ्ट सी ++ मानक दस्तावेज़ में सेक्शन 17.2.4 (पृष्ठ 312) देखें: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf, – Pete

+0

@ मैयान वास्तव में आया ऐसा करने का एक बेहतर तरीका है जो बदसूरत टेम्पलेट वर्ग को गैर-समानता से पूरी तरह से बचाता है और इसके कई अन्य सकारात्मक दुष्प्रभाव भी हैं - अद्यतन उत्तर देखें। – Pete

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