अंतर यह है कि std::make_shared
एक ढेर-आवंटन का प्रदर्शन करता है, जबकि std::shared_ptr
निर्माणकर्ता को बुलाकर दो कार्य करता है।
ढेर-आवंटन कहाँ होते हैं?
std::shared_ptr
दो संस्थाओं का प्रबंधन करता है:
- नियंत्रण ब्लॉक (मेटा डेटा संग्रहीत करता है, जैसे कि रिफ-काउंट्स, टाइप-एरीड डीलेटर, आदि)
- ऑब्जेक्ट को प्रबंधित किया जा रहा है
std::make_shared
नियंत्रण ब्लॉक और डेटा दोनों के लिए आवश्यक स्थान के लिए एकल हीप-आवंटन लेखांकन करता है। अन्य मामले में, new Obj("foo")
प्रबंधित डेटा के लिए एक ढेर-आवंटन को आमंत्रित std::shared_ptr
करता है और निर्माणकर्ता नियंत्रण ब्लॉक के लिए एक और प्रदर्शन करता है।
अधिक जानकारी के लिए, cppreference पर कार्यान्वयन नोट्स देखें ।
अद्यतन I: अपवाद-सुरक्षा
नोट (2019/08/30) : फ़ंक्शन तर्कों के मूल्यांकन क्रम में बदलाव के कारण C ++ 17 के बाद से यह कोई समस्या नहीं है। विशेष रूप से, फ़ंक्शन के प्रत्येक तर्क को अन्य तर्कों के मूल्यांकन से पहले पूरी तरह से निष्पादित करने की आवश्यकता होती है।
चूंकि ओपी चीजों के अपवाद-सुरक्षा पक्ष के बारे में सोच रहा है, इसलिए मैंने अपना उत्तर अपडेट कर दिया है।
इस उदाहरण पर विचार करें,
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
चूँकि C ++, सबटेक्शंस के मूल्यांकन का मनमाना आदेश देता है, एक संभावित आदेश है:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
अब, मान लीजिए कि हमें चरण 2 पर फेंका गया एक अपवाद मिलता है (उदाहरण के लिए, मेमोरी अपवाद से बाहर, Rhs
कंस्ट्रक्टर ने कुछ अपवाद फेंक दिया)। हम फिर चरण 1 पर आवंटित स्मृति खो देते हैं, क्योंकि कुछ भी इसे साफ करने का मौका नहीं होता। यहाँ समस्या का मूल यह है कि कच्चे पॉइंटर को std::shared_ptr
तुरंत कंस्ट्रक्टर को पास नहीं दिया गया ।
इसे ठीक करने का एक तरीका उन्हें अलग-अलग लाइनों पर करना है ताकि यह मध्यस्थता आदेश न हो सके।
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
इस पाठ्यक्रम को हल करने का पसंदीदा तरीका std::make_shared
इसके बजाय उपयोग करना है।
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
अद्यतन II: का नुकसान std::make_shared
केसी की टिप्पणियों का हवाला देते हुए :
चूँकि वहाँ केवल एक ही आवंटन है, पॉइंटी की मेमोरी को तब तक नहीं हटाया जा सकता है जब तक कि नियंत्रण खंड उपयोग में नहीं है। A weak_ptr
नियंत्रण ब्लॉक को अनिश्चित काल तक जीवित रख सकता है।
क्यों के उदाहरण weak_ptr
नियंत्रण ब्लॉक को जीवित रखते हैं?
यह weak_ptr
निर्धारित करने के लिए कि प्रबंधित वस्तु अभी भी वैध है (उदाहरण के लिए lock
) के लिए एक रास्ता होना चाहिए । वे ऐसा करते हैं shared_ptr
कि प्रबंधित ऑब्जेक्ट के मालिक s की संख्या की जांच करके , जिसे नियंत्रण ब्लॉक में संग्रहीत किया जाता है। इसका नतीजा यह है कि कंट्रोल ब्लॉक तब तक जीवित रहते हैं जब तक कि shared_ptr
गिनती और weak_ptr
गिनती दोनों हिट 0 नहीं हो जाती।
वापस std::make_shared
चूंकि std::make_shared
नियंत्रण ब्लॉक और प्रबंधित ऑब्जेक्ट दोनों के लिए एक ही ढेर-आवंटन होता है, इसलिए नियंत्रण ब्लॉक और प्रबंधित ऑब्जेक्ट के लिए मेमोरी को स्वतंत्र रूप से मुक्त करने का कोई तरीका नहीं है। हमें तब तक इंतजार करना चाहिए जब तक कि हम नियंत्रण ब्लॉक और प्रबंधित ऑब्जेक्ट दोनों को मुक्त नहीं कर सकते हैं, जो तब तक होता है जब तक कि कोई shared_ptr
एस या weak_ptr
एस जीवित नहीं होता है।
मान लीजिए कि हमने इसके बजाय नियंत्रण ब्लॉक और प्रबंधित ऑब्जेक्ट के माध्यम से new
और shared_ptr
निर्माणकर्ता के लिए दो ढेर-आवंटन किए । फिर हम मेमोरी को प्रबंधित ऑब्जेक्ट (शायद पहले) के लिए मुक्त करते हैं जब कोई shared_ptr
जीवित नहीं होते हैं , और नियंत्रण ब्लॉक (शायद बाद में) के लिए मेमोरी को मुक्त कर देते हैं जब कोई weak_ptr
जीवित नहीं होता है।