C ++ 17 में std :: make_unique का उपयोग क्यों करें?


96

जहां तक ​​मैं समझता हूं, C ++ 14 ने पेश किया std::make_uniqueक्योंकि पैरामीटर मूल्यांकन क्रम निर्दिष्ट नहीं होने के कारण, यह असुरक्षित था:

f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A

(स्पष्टीकरण: यदि मूल्यांकन पहले कच्चे पॉइंटर के लिए मेमोरी आवंटित करता है, तो कॉल g()और एक अपवाद std::unique_ptrनिर्माण से पहले फेंक दिया जाता है , फिर मेमोरी लीक हो जाती है।)

कॉलिंग std::make_uniqueकॉल ऑर्डर को बाधित करने का एक तरीका था, इस प्रकार यह चीजें सुरक्षित बनाती हैं:

f(std::make_unique<MyClass>(param), g());             // Syntax B

तब से, सी ++ 17 मूल्यांकन आदेश स्पष्ट किया है, भी सिंटेक्स एक सुरक्षित बनाने, इसलिए यहाँ है मेरे सवाल: अभी भी उपयोग करने के लिए एक कारण नहीं है std::make_uniqueसे अधिक std::unique_ptrसी ++ 17 में निर्माता की? क्या आप कुछ उदाहरण दे सकते हैं?

अब तक, एकमात्र कारण जिसकी मैं कल्पना कर सकता हूं, वह MyClassकेवल एक बार टाइप करने की अनुमति देता है (यह मानते हुए कि आपको बहुरूपता पर भरोसा करने की आवश्यकता नहीं है std::unique_ptr<Base>(new Derived(param)))। हालांकि, यह एक बहुत ही कमजोर कारण की तरह लगता है, खासकर जब std::make_uniqueएक विध्वंसक को निर्दिष्ट करने की अनुमति नहीं देता है जबकि std::unique_ptrनिर्माता करता है।

और बस स्पष्ट होने के लिए, मैं std::make_uniqueमानक पुस्तकालय से हटाने के पक्ष में वकालत नहीं कर रहा हूं (इसे कम से कम पिछड़े अनुकूलता के लिए समझ में आता है), लेकिन यह सोचकर कि क्या अभी भी ऐसी परिस्थितियां हैं जिनमें इसे दृढ़ता से पसंद किया जाता हैstd::unique_ptr


4
हालांकि, यह एक बहुत कमजोर कारण की तरह लगता है -> यह एक कमजोर कारण क्यों है? यह प्रभावी रूप से कोड दोहराव को कम करता है। डिलेटर के लिए, आप कितनी बार कस्टम डीलेटर का उपयोग कर रहे हैं std::unique_ptr? इसके खिलाफ कोई तर्क नहीं हैmake_unique
llllllllll

2
मैं कहता हूं कि यह एक कमजोर कारण है क्योंकि अगर std::make_uniqueपहली जगह में नहीं था , तो मुझे नहीं लगता कि एसटीएल में इसे जोड़ने के लिए पर्याप्त कारण होगा, खासकर जब यह एक सिंटैक्स है जो कंस्ट्रक्टर का उपयोग करने की तुलना में कम अभिव्यंजक है, अधिक नहीं
अनन्त

1
यदि आपके पास एक प्रोग्राम है, जो c ++ 14 में बनाया गया है, तो make_unique का उपयोग करके, आप नहीं चाहते कि फ़ंक्शन स्टाल से हटा दिया जाए। या यदि आप चाहते हैं कि यह पीछे की ओर संगत हो।
सर्ज

2
@ सर्ज यह एक अच्छा बिंदु है, लेकिन यह मेरे सवाल के उद्देश्य के अलावा थोड़ा सा है। मैं इसे और स्पष्ट करने के संपादित कर देंगे
अनन्त

1
@ अनन्त कृपया C ++ मानक पुस्तकालय को STL के रूप में संदर्भित करना बंद करें क्योंकि यह गलत है और भ्रम पैदा करता है। देखें stackoverflow.com/questions/5205491/...
Marandil

जवाबों:


73

आप सही हैं कि मुख्य कारण को हटा दिया गया था। अभी भी नए दिशानिर्देशों का उपयोग नहीं किया गया है और यह कम टाइपिंग कारण हैं (टाइप दोहराना नहीं है या शब्द का उपयोग नहीं करना है new)। बेशक वे मजबूत तर्क नहीं हैं, लेकिन मैं वास्तव newमें अपने कोड में नहीं देखना पसंद करता हूं।

इसके अलावा स्थिरता के बारे में मत भूलना। आपको पूरी तरह से उपयोग करना चाहिए make_sharedताकि make_uniqueप्राकृतिक का उपयोग करना और पैटर्न फिट हो। यह तब (या रिवर्स) में बदलने के std::make_unique<MyClass>(param)लिए तुच्छ है, std::make_shared<MyClass>(param)जहां सिंटैक्स ए को फिर से लिखने की अधिक आवश्यकता होती है।


40
@reggaeguitar अगर मुझे देखा जाए कि newमुझे रुकने और सोचने की ज़रूरत है: यह पॉइंटर कब तक रहने वाला है? क्या मैंने इसे सही तरीके से संभाला है? यदि कोई अपवाद है, तो क्या सब कुछ सही ढंग से साफ हो गया है? मैं अपने आप से उन सवालों को नहीं पूछना चाहता हूं और इस पर अपना समय बर्बाद कर रहा हूं और अगर मैं इसका उपयोग newनहीं करता हूं, तो मुझे उन सवालों को पूछने की जरूरत नहीं है।
नाथनऑलिवर

5
कल्पना कीजिए कि आप अपनी परियोजना के सभी स्रोत फ़ाइलों पर एक grep करते हैं और एक भी नहीं पाते हैं new। यह अद्भुत नहीं होगा?
सेबेस्टियन मच

5
"नया उपयोग न करें" का मुख्य लाभ यह दिशानिर्देश देता है कि यह सरल है, इसलिए यह आपके द्वारा काम किए जा सकने वाले कम अनुभवी डेवलपर्स को देने के लिए एक आसान दिशानिर्देश है। मुझे पहली बार एहसास नहीं हुआ था, लेकिन उस का मूल्य और अपने आप में है
शाश्वत

@NathanOliver आपको वास्तव में पूरी तरह से उपयोग करने की आवश्यकता नहीं है std::make_shared- एक ऐसे मामले की कल्पना करें जहां आवंटित वस्तु बड़ी है और इसमें बहुत std::weak_ptr-से संकेत दिए गए हैं: अंतिम साझा होते ही वस्तु को हटा देना बेहतर होगा। सूचक नष्ट हो गया है और बस एक छोटे से साझा क्षेत्र के साथ रहता है।
देव नल

1
@NathanOliver आप नहीं करेंगे। मैं जिस चीज के बारे में बात कर रहा हूं, वह है std::make_shared stackoverflow.com/a/20895705/8414561 का नुकसान जहां ऑब्जेक्ट को स्टोर करने के लिए उपयोग की जाने वाली मेमोरी को अंतिम std::weak_ptrचले जाने तक मुक्त नहीं किया जा सकता है (भले ही ऑल std::shared_ptr-सिस इसे इंगित करता हो (और) फलस्वरूप वस्तु) पहले ही नष्ट हो चुकी है।
देव नल

50

make_uniqueअलग Tसे T[]और T[N], unique_ptr(new ...)ऐसा नहीं करता।

आप एक पॉइंटर को पास करके आसानी से अपरिभाषित व्यवहार प्राप्त कर सकते हैं जो कि new[]एड के लिए था unique_ptr<T>, या उस पॉइंटर को पास करके जो newएड में था unique_ptr<T[]>


यह बदतर है: यह न केवल करता है, यह फ्लैट-आउट करने में असमर्थ है।
डिडुप्लिकेटर

21

कारण डुप्लिकेट के बिना कम कोड है। तुलना

f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());

आप बचाते हैं MyClass, newऔर ब्रेसिज़। यह केवल एक वर्ण में अधिक लागत मेकअप के साथ तुलना में ptr


2
ठीक है, जैसा कि मैंने सवाल में कहा था, मैं इसे केवल एक उल्लेख के साथ कम टाइपिंग देख सकता हूं MyClass, लेकिन मैं सोच रहा था कि क्या इसका उपयोग करने का एक मजबूत कारण था
अनन्त

2
कई मामलों में कटौती गाइड <MyClass>पहले संस्करण में भाग को खत्म करने में मदद करेगा ।
एनटी

9
यह पहले से ही अन्य उत्तरों के लिए टिप्पणियों में कहा गया है, लेकिन c ++ 17 ने निर्माणकर्ताओं के लिए टेम्पलेट प्रकार की कटौती की शुरुआत की, जबकि std::unique_ptrयह अस्वीकृत है। इसे अलग करना है std::unique_ptr<T>औरstd::unique_ptr<T[]>
अनन्त

19

newआजीवन शुद्धता के लिए हर उपयोग को अतिरिक्त सावधानीपूर्वक अंकेक्षित किया जाना चाहिए; क्या यह नष्ट हो जाता है? सिर्फ एक बार?

make_uniqueउन अतिरिक्त विशेषताओं के लिए हर उपयोग नहीं करता है; जब तक मालिक के पास "सही" जीवनकाल होता है, तब तक यह अद्वितीय संकेतक को "सही" बनाता है।

अब, यह सच है कि unique_ptr<Foo>(new Foo())सभी तरीकों से 1 के समान है make_unique<Foo>(); इसके लिए बस एक सरल आवश्यकता है " newउन्हें ऑडिट करने के सभी उपयोगों के लिए अपना स्रोत कोड तैयार करें"।


1 वास्तव में सामान्य मामले में झूठ है। सही अग्रेषण सही नहीं है {}, डिफ़ॉल्ट init, सरणियाँ सभी अपवाद हैं।


तकनीकी रूप से काफी समान unique_ptr<Foo>(new Foo)नहीं है ... बाद वाला करता है लेकिन अन्यथा, हाँ। make_unique<Foo>()new Foo()
बैरी

@ असली, अतिभारित ऑपरेटर नया संभव है।
यक - एडम नेवरामोंट

@dedup क्या Foul C ++ 17 जादू टोना है?
यक्क - एडम नेवरामॉन्ट

2
@Deduplicator जबकि c ++ 17 ने कंस्ट्रक्टर्स के लिए टेम्प्लेट टाइप डिडक्शन की शुरुआत की है, इस मामले में std::unique_ptrयह अस्वीकृत है। यदि अलग करना है std::unique_ptr<T>औरstd::unique_ptr<T[]>
अनन्त

@ यक-आदमनेवरुमोंट का मतलब नई ओवरलोडिंग से नहीं था, मेरा मतलब सिर्फ डिफॉल्ट-इनिट बनाम वैल्यू-इनिट था।
बैरी

0

तब से, C ++ 17 ने मूल्यांकन आदेश को स्पष्ट कर दिया है, जिससे सिंटैक्स ए सुरक्षित भी हो गया है

यह वास्तव में पर्याप्त नहीं है। सुरक्षा की गारंटी के रूप में हाल ही में पेश किए गए तकनीकी खण्ड पर भरोसा करना बहुत मजबूत अभ्यास नहीं है:

  • कोई इस कोड को C ++ 14 में संकलित कर सकता है।
  • आप newअपने उदाहरण को कॉपी-पेस्ट करके कच्चे कहीं और के उपयोग को प्रोत्साहित करेंगे ।
  • जैसा कि एसएम का सुझाव है, चूंकि कोड दोहराव है, एक प्रकार दूसरे को बदले बिना बदला जा सकता है।
  • कुछ प्रकार की स्वचालित IDE रीफ़ैक्टरिंग शायद newकहीं और स्थानांतरित हो सकती है (ठीक है, दी गई है, उस की बहुत संभावना नहीं है)।

आम तौर पर, मानक में मामूली या अस्पष्ट तकनीकी खामियों को देखते हुए, आपके कोड के लिए भाषा / लेयरिंग का सहारा लिए बिना उचित / मजबूत / स्पष्ट रूप से मान्य होना एक अच्छा विचार है ।

(यह मूल रूप से वही तर्क है जो मैंने टपल विनाश के आदेश के बारे में यहां कहा था ।)


-1

शून्य फ़ंक्शन पर विचार करें (std :: unique_ptr (new A ()), std :: unique_ptr (new B ())) {...}

मान लीजिए कि नया ए () सफल होता है, लेकिन नया बी () एक अपवाद फेंकता है: आप इसे अपने कार्यक्रम के सामान्य निष्पादन को फिर से शुरू करने के लिए पकड़ते हैं। दुर्भाग्य से, C ++ मानक को उस ऑब्जेक्ट ए को नष्ट होने की आवश्यकता नहीं है और इसकी मेमोरी डीलॉलेटेड है: मेमोरी चुपचाप लीक होती है और इसे साफ करने का कोई तरीका नहीं है। A और B को std :: make_uniques में लपेटने से आपको यकीन है कि रिसाव नहीं होगा:

शून्य फ़ंक्शन (std :: make_unique (), std :: make_unique ()) {...} यहां बिंदु यह है कि std :: make_unique और std :: make_unique अब अस्थायी वस्तुएं हैं, और अस्थायी वस्तुओं की सफाई सही ढंग से निर्दिष्ट की गई है सी ++ मानक: उनके विध्वंसक ट्रिगर हो जाएंगे और स्मृति मुक्त हो जाएगी। इसलिए यदि आप, हमेशा std :: make_unique और std :: make_sared का उपयोग करके ऑब्जेक्ट आवंटित करना पसंद कर सकते हैं।


4
लेखक ने स्पष्ट रूप से प्रश्न में निर्दिष्ट किया कि C ++ 17 लीक में अब और नहीं होगा। "तब से, C ++ 17 ने मूल्यांकन आदेश को स्पष्ट कर दिया है, सिंटैक्स ए को भी सुरक्षित बना दिया है, इसलिए यहां मेरा प्रश्न है: (...)"। आपने उसके सवाल का जवाब नहीं दिया।
R2RT
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.