जवाबों:
यह आपको एक वैध shared_ptr
उदाहरण प्राप्त करने में सक्षम बनाता है this
, जब आपके पास सब कुछ होता है this
। इसके बिना, आप एक होने का कोई रास्ता नहीं होता है shared_ptr
करने के लिए this
, जब तक आप पहले से ही एक सदस्य के रूप में एक था। इस उदाहरण को enable_sared_from_this के लिए बढ़ावा देने के दस्तावेज़ से :
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
विधि f()
एक वैध रिटर्न देती है shared_ptr
, भले ही इसका कोई सदस्य उदाहरण न हो। ध्यान दें कि आप बस ऐसा नहीं कर सकते हैं:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
साझा किए गए पॉइंटर कि यह वापस आ गया है, "उचित" एक से एक अलग संदर्भ गणना होगी, और उनमें से एक वस्तु को हटाए जाने पर एक झूलते हुए संदर्भ को खोने और धारण करना होगा।
enable_shared_from_this
C ++ 11 मानक का हिस्सा बन गया है। आप इसे वहां से और साथ ही बूस्ट से भी प्राप्त कर सकते हैं।
std::shared_ptr
std::enable_shared_from_this
std::shared_ptr
लिए पहले से प्रबंधित वस्तु के लिए निर्माण std::shared_ptr
करना आंतरिक रूप से संग्रहीत कमजोर संदर्भ से परामर्श नहीं करेगा और इस प्रकार अपरिभाषित व्यवहार को बढ़ावा देगा।" ( en.cppreference.com/w/cpp/memory/enable_sared_from_this )
shared_ptr<Y> q = p
?
std::make_shared<T>
।
कमजोर बिंदुओं पर डॉ डोब्स लेख से, मुझे लगता है कि यह उदाहरण समझना आसान है (स्रोत: http://drdobbs.com/cpp/184402026 ):
... इस तरह कोड सही ढंग से काम नहीं करेगा:
int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
दोनों में से कोई भी shared_ptr
वस्तु दूसरे के बारे में नहीं जानती है, इसलिए दोनों नष्ट होने पर संसाधन को छोड़ने की कोशिश करेंगे। यह आमतौर पर समस्याओं की ओर जाता है।
इसी प्रकार, यदि किसी सदस्य को किसी shared_ptr
ऑब्जेक्ट की आवश्यकता होती है जो उस वस्तु का मालिक है जिसे उस पर कॉल किया जा रहा है, तो यह केवल मक्खी पर ऑब्जेक्ट नहीं बना सकता है:
struct S
{
shared_ptr<S> dangerous()
{
return shared_ptr<S>(this); // don't do this!
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->dangerous();
return 0;
}
इस कोड में पहले के उदाहरण के समान समस्या है, हालांकि अधिक सूक्ष्म रूप में। जब इसका निर्माण किया जाता है, तो shared_pt
r ऑब्जेक्ट sp1
नए आवंटित संसाधन का मालिक होता है। सदस्य फ़ंक्शन के अंदर का कोड S::dangerous
उस shared_ptr
ऑब्जेक्ट के बारे में नहीं जानता है , इसलिए वह जिस shared_ptr
ऑब्जेक्ट से रिटर्न करता है वह अलग है sp1
। मदद shared_ptr
करने के लिए नई वस्तु की नकल करना sp2
; जब sp2
यह कार्यक्षेत्र से बाहर हो जाता है, तो यह संसाधन को छोड़ देगा, और जब sp1
यह कार्यक्षेत्र से बाहर चला जाएगा, तो यह संसाधन को फिर से जारी करेगा।
इस समस्या से बचने का तरीका वर्ग टेम्पलेट का उपयोग करना है enable_shared_from_this
। टेम्पलेट एक टेम्पलेट प्रकार तर्क लेता है, जो उस वर्ग का नाम है जो प्रबंधित संसाधन को परिभाषित करता है। उस वर्ग को, खासतौर पर, खाके से सार्वजनिक रूप से प्राप्त किया जाना चाहिए; इस तरह:
struct S : enable_shared_from_this<S>
{
shared_ptr<S> not_dangerous()
{
return shared_from_this();
}
};
int main()
{
shared_ptr<S> sp1(new S);
shared_ptr<S> sp2 = sp1->not_dangerous();
return 0;
}
जब आप ऐसा करते हैं, तो ध्यान रखें कि जिस ऑब्जेक्ट पर आप कॉल करते हैं वह ऑब्जेक्ट के shared_from_this
स्वामित्व में होना चाहिए shared_ptr
। यह काम नहीं करेगा:
int main()
{
S *p = new S;
shared_ptr<S> sp2 = p->not_dangerous(); // don't do this
}
shared_ptr<S> sp1(new S);
इसका उपयोग करना पसंद किया जा सकता है shared_ptr<S> sp1 = make_shared<S>();
, उदाहरण के लिए देखें stackoverflow.com/questions/18301511/…
shared_ptr<S> sp2 = p->not_dangerous();
क्योंकि यहाँ नुकसान यह है कि आपको पहली बार कॉल करने से पहले सामान्य तरीके से एक साझा करना चाहिए shared_from_this()
! यह गलत पाने के लिए वास्तव में आसान है! C ++ 17 से पहले यह यूबी कॉल करना है shared_from_this()
इससे पहले कि वास्तव में एक साझा किया गया है सामान्य तरीके से बनाया गया है: auto sptr = std::make_shared<S>();
या shared_ptr<S> sptr(new S());
। C ++ 17 से शुक्र है कि ऐसा करने से फेंक दिया जाएगा।
S* s = new S(); shared_ptr<S> ptr = s->not_dangerous();
<- इसे share_from_this को केवल पहले से साझा की गई वस्तु पर कॉल करने की अनुमति है, अर्थात std द्वारा साझा की गई वस्तु पर: share_ptr <T>। अन्यथा व्यवहार अपरिभाषित है (C ++ 17 तक) std :: bad_weak_ptr फेंक दिया जाता है (डिफ़ॉल्ट-निर्मित कमजोर_तीस से share_ptr निर्माता द्वारा) (C ++ 17 के बाद से)। । तो वास्तविकता यह है कि इसे बुलाया जाना चाहिए always_dangerous()
, क्योंकि आपको ज्ञान की आवश्यकता है कि क्या यह पहले से ही साझा किया गया है या नहीं।
यहाँ एक नट और बोल्ट के दृष्टिकोण से मेरा स्पष्टीकरण है (शीर्ष उत्तर ने मेरे साथ 'क्लिक' नहीं किया)। * ध्यान दें कि यह श्रुति_पट्र और enable_sared_from_this के लिए स्रोत की जाँच करने का परिणाम है जो विज़ुअल स्टूडियो 2012 के साथ आता है। शायद अन्य कंपाइलर enable_sared_from_this को अलग तरह से लागू करते हैं ... *
enable_shared_from_this<T>
एक निजी weak_ptr<T>
उदाहरण जोड़ता है T
जो उदाहरण के लिए ' एक सही संदर्भ गणना ' रखता है T
।
इसलिए, जब आप पहली बार shared_ptr<T>
एक नया T * बनाते हैं , तो वह T * का आंतरिक कमज़ोर_प्रकार 1. के एक रिफकाउंट से आरंभ हो जाता है। नया shared_ptr
मूल रूप से इस पर वापस आ जाता है weak_ptr
।
T
तब, इसके तरीकों में, shared_from_this
एक ही आंतरिक रूप से संग्रहीत संदर्भ गणना परshared_ptr<T>
उस बैक का उदाहरण प्राप्त करने के लिए कॉल कर सकते हैं । इस तरह, आपके पास हमेशा एक जगह होती है, जहाँ पर T*
कई-कई shared_ptr
उदाहरणों के बजाय रेफ-काउंट को संग्रहित किया जाता है, जो एक -दूसरे के बारे में नहीं जानते हैं, और प्रत्येक सोचते हैं कि वे shared_ptr
रेफ-काउंटिंग के प्रभारी हैं T
और इसे तब हटाते हैं जब उनका रिफाइन होता है -काउंट शून्य तक पहुंचता है।
So, when you first create...
क्योंकि यह एक आवश्यकता है (जैसा कि आप कहते हैं कि weak_ptr को तब तक आरंभीकृत नहीं किया जाता है, जब तक आप ऑब्जेक्ट्स पॉइंटर को एक साझा_ crr!) में पास नहीं करते हैं और यह वह जगह है जहाँ चीजें आपके लिए बहुत गलत हो सकती हैं! सावधान नहीं। यदि आप कॉल करने से पहले कोई साझा नहीं shared_from_this
करते हैं तो आप यूबी प्राप्त करते हैं - इसी तरह यदि आप एक से अधिक साझा किए गए हैं तो आप यूबी भी प्राप्त कर सकते हैं। आप किसी भी तरह सुनिश्चित करें कि आप एक shared_ptr बनाने करना है वास्तव में एक बार।
enable_shared_from_this
, बिंदु से शुरू करने के लिए सक्षम होने के बाद से शुरू करने के लिए पूरी तरह से भंगुर shared_ptr<T>
है T*
, लेकिन वास्तव में जब आपको एक संकेतक मिलता है, T* t
तो यह आम तौर पर इसके बारे में कुछ भी मानने के लिए सुरक्षित नहीं है कि यह पहले से ही साझा किया जा रहा है या नहीं, और गलत अनुमान लगाना यूबी है।
ध्यान दें कि एक बढ़ावा :: intrusive_ptr इस समस्या से ग्रस्त नहीं है। यह अक्सर इस मुद्दे के आसपास आने का एक अधिक सुविधाजनक तरीका है।
enable_shared_from_this
आपको एक एपीआई के साथ काम करने की अनुमति देता है जो विशेष रूप से स्वीकार करता है shared_ptr<>
। मेरी राय में, इस तरह की एपीआई आमतौर पर डूइंग इट गलत है (जैसा कि स्टैक में मेमोरी को कुछ अधिक होने देना बेहतर है), लेकिन अगर आप इस तरह के एपीआई के साथ काम करने के लिए मजबूर हैं, तो यह एक अच्छा विकल्प है।
यह c ++ 11 और बाद में बिल्कुल वैसा ही है: यह this
एक साझा पॉइंटर के रूप में वापस जाने की क्षमता को सक्षम करने के लिए है क्योंकि this
आप एक कच्चा पॉइंटर देते हैं।
दूसरे शब्दों में, यह आपको इस तरह कोड को चालू करने की अनुमति देता है
class Node {
public:
Node* getParent const() {
if (m_parent) {
return m_parent;
} else {
return this;
}
}
private:
Node * m_parent = nullptr;
};
इस मामले में:
class Node : std::enable_shared_from_this<Node> {
public:
std::shared_ptr<Node> getParent const() {
std::shared_ptr<Node> parent = m_parent.lock();
if (parent) {
return parent;
} else {
return shared_from_this();
}
}
private:
std::weak_ptr<Node> m_parent;
};
shared_ptr
। आप यह सुनिश्चित करने के लिए इंटरफ़ेस बदलना चाह सकते हैं कि यह मामला है।
std::shared_ptr<Node> getParent const()
, मैं सामान्य रूप से NodePtr getParent const()
इसके बजाय इसे उजागर करूंगा । यदि आपको आंतरिक कच्चे पॉइंटर (सर्वोत्तम उदाहरण: सी लाइब्रेरी से निपटने) तक पहुंच की आवश्यकता है, तो उसके std::shared_ptr<T>::get
लिए है, जिसका मैं उल्लेख करना पसंद करता हूं क्योंकि मैंने इस कच्चे पॉइंटर एक्सेसर का उपयोग कई बार गलत कारण से किया है।
दूसरा तरीका यह है कि किसी weak_ptr<Y> m_stub
सदस्य को इसमें जोड़ें class Y
। फिर लिखें:
shared_ptr<Y> Y::f()
{
return m_stub.lock();
}
उपयोगी तब होता है जब आप उस वर्ग को नहीं बदल सकते जो आप से प्राप्त कर रहे हैं (उदाहरण के लिए अन्य लोगों के पुस्तकालय का विस्तार)। सदस्य को इनिशियलाइज़ करना न भूलें, उदाहरण के लिए m_stub = shared_ptr<Y>(this)
, एक कंस्ट्रक्टर के दौरान भी यह मान्य है।
यह ठीक है अगर वंशानुक्रम पदानुक्रम में इस तरह के अधिक स्टब्स हैं, तो यह ऑब्जेक्ट के विनाश को रोक नहीं पाएगा।
संपादित करें: जैसा कि उपयोगकर्ता nobar द्वारा सही ढंग से बताया गया है, असाइनमेंट समाप्त होने पर कोड Y ऑब्जेक्ट को नष्ट कर देगा और अस्थायी चर नष्ट हो जाएंगे। इसलिए मेरा उत्तर गलत है।
shared_ptr<>
जो अपनी पॉइंटर को नहीं हटाता है, तो यह ओवरकिल है। आप बस यह कह सकते हैं कि एक क्रियात्मक वस्तु return shared_ptr<Y>(this, no_op_deleter);
कहां no_op_deleter
है Y*
और कुछ भी नहीं ले रही है।
m_stub = shared_ptr<Y>(this)
निर्माण करेगा और इससे एक अस्थायी शेयर्ड_एप्ट्र को तुरंत नष्ट कर देगा। जब यह कथन समाप्त this
हो जाएगा , तो हटा दिया जाएगा और बाद के सभी संदर्भ झूलने लगेंगे।
enable_shared_from_this
, तो यह weak_ptr
अपने आप में (ctor द्वारा आबाद) रहता है, shared_ptr
जब आप कॉल करते हैं shared_from_this
। दूसरे शब्दों में, आप enable_shared_from_this
पहले से ही जो प्रदान करते हैं, उसकी नकल कर रहे हैं ।