अंतर यह है कि 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जीवित नहीं होता है।