संदर्भ या मूल्य के आधार पर स्मार्ट पॉइंटर्स (शेयर्ड_प्ट्र) कैसे लौटाएं?


94

मान लीजिए कि मेरे पास एक विधि है जिसमें एक रिटर्न है shared_ptr

संदर्भ या मूल्य द्वारा इसे वापस करने के संभावित लाभ और कमियां क्या हैं?

दो संभावित सुराग:

  • प्रारंभिक वस्तु विनाश। यदि मैं shared_ptr(कॉन्स्टेबल) संदर्भ वापस करता हूं , तो रेफरेंस काउंटर को इंक्रीमेंट नहीं किया जाता है, इसलिए मैं ऑब्जेक्ट को डिलीट करने का जोखिम उठाता हूं जब वह किसी अन्य संदर्भ (जैसे एक और धागा) में दायरे से बाहर हो जाता है। क्या ये सही है? क्या होगा यदि पर्यावरण एकल-थ्रेडेड है, क्या यह स्थिति भी हो सकती है?
  • लागत। दर्रा-दर-मूल्य निश्चित रूप से मुक्त नहीं है। जब भी संभव हो, क्या इसे टालना उचित है?

सभी का धन्यवाद।

जवाबों:


114

मूल्य से स्मार्ट संकेत लौटें।

जैसा कि आपने कहा है, यदि आप इसे संदर्भ द्वारा वापस करते हैं, तो आप संदर्भ गणना को ठीक से नहीं बढ़ाएंगे, जिससे अनुचित समय पर कुछ हटाने का जोखिम खुल जाता है। अकेले संदर्भ के द्वारा वापस नहीं जाने के लिए पर्याप्त कारण होना चाहिए। इंटरफेस मजबूत होना चाहिए।

मूल्य चिंता आजकल वैल्यू ऑप्टिमाइज़ेशन (आरवीओ) वापस करने के लिए धन्यवाद है , इसलिए आप वेतन वृद्धि-वृद्धि क्रम या आधुनिक कंपाइलरों में ऐसा कुछ नहीं करेंगे। तो वापसी के लिए सबसे अच्छा तरीका shared_ptrहै बस मूल्य द्वारा वापस जाना है:

shared_ptr<T> Foo()
{
    return shared_ptr<T>(/* acquire something */);
};

यह आधुनिक C ++ कंपाइलर के लिए एक मृत-स्पष्ट RVO अवसर है। मुझे पता है कि विजुअल C ++ कंपाइलर RVO को तब भी लागू करते हैं, जब सभी अनुकूलन बंद हो जाते हैं। और C ++ 11 के चाल शब्दार्थ के साथ, यह चिंता और भी कम प्रासंगिक है। (लेकिन यह सुनिश्चित करने का एकमात्र तरीका प्रोफ़ाइल और प्रयोग है।)

यदि आप अभी भी आश्वस्त नहीं हैं, तो डेव अब्राहम के पास एक लेख है जो मूल्य द्वारा लौटने के लिए एक तर्क देता है। मैं यहां एक स्निपेट को पुन: पेश करता हूं; मैं अत्यधिक सलाह देता हूं कि आप पूरा लेख पढ़ें:

ईमानदार रहें: निम्नलिखित कोड आपको कैसा महसूस कराता है?

std::vector<std::string> get_names();
...
std::vector<std::string> const names = get_names();

सच कहूँ, भले ही मुझे बेहतर पता होना चाहिए, यह मुझे परेशान करता है। सिद्धांत रूप में, जब get_names() रिटर्न मिलता है, तो हमें एक vectorकी नकल करनी होती है string। फिर, जब हम शुरू करते हैं names, तो हमें इसे फिर से कॉपी करने की आवश्यकता होती है , और हमें पहली प्रति को नष्ट करने की आवश्यकता होती है। यदि stringवेक्टर में N s हैं , तो प्रत्येक प्रतिलिपि को N + 1 मेमोरी आबंटन और कैश-अनफ़ेयर डेटा एक्सेस की एक पूरी श्रृंखला की आवश्यकता हो सकती है> जैसा कि स्ट्रिंग सामग्री की प्रतिलिपि बनाई गई है।

इस तरह की चिंता का सामना करने के बजाय, मैं अक्सर अनावश्यक प्रतियों से बचने के लिए पास-बाय-संदर्भ पर वापस गिर गया हूं:

get_names(std::vector<std::string>& out_param );
...
std::vector<std::string> names;
get_names( names );

दुर्भाग्य से, यह दृष्टिकोण आदर्श से बहुत दूर है।

  • कोड में 150% की वृद्धि हुई
  • हमें छोड़ना पड़ा है constक्योंकि हम नाम बदल रहे हैं।
  • जैसा कि कार्यात्मक प्रोग्रामर हमें याद दिलाना पसंद करते हैं, म्यूटेशन कोड को रेफ़रेंशियल ट्रांसपेरेंसी और इक्विशनल रीज़निंग को कम करके कारण को और अधिक जटिल बनाता है।
  • अब हमारे पास नामों के लिए सख्त मूल्य शब्दार्थ नहीं हैं।

लेकिन क्या दक्षता हासिल करने के लिए इस तरह से हमारे कोड को गड़बड़ाना वास्तव में आवश्यक है? सौभाग्य से, जवाब नहीं निकला (और विशेष रूप से अगर आप C ++ 0x का उपयोग कर रहे हैं) नहीं।


मुझे नहीं पता कि मैं कहूंगा कि आरवीओ प्रश्न को गलत बनाता है क्योंकि संदर्भ से लौटने पर निश्चित रूप से आरवीओ असंभव हो जाता है।
एडवर्ड स्ट्रेंज

@CrazyEddie: सच है, यही एक कारण है कि मैं अनुशंसा करता हूं कि ओपी मूल्य से वापसी करें।
सिलिको में

क्या आरवीओ नियम, मानक द्वारा अनुमति देता है, मानक द्वारा गारंटीकृत सिंक्रनाइज़ेशन / होने से पहले के नियमों के बारे में नियमों को ट्रम्प करता है?
edA-qa मोर्ट-ओर-वाई

1
@ eda-qa mort-ora-y: RVO को स्पष्ट रूप से अनुमति दी जाती है, भले ही इसके दुष्प्रभाव हों। उदाहरण के लिए, यदि आपके पास cout << "Hello World!";डिफ़ॉल्ट और कॉपी कंस्ट्रक्टर में एक स्टेटमेंट है , तो आप दो Hello World!एस नहीं देखेंगे जब आरवीओ प्रभाव में होगा। हालांकि, यह ठीक से डिज़ाइन किए गए स्मार्ट पॉइंटर्स, यहां तक ​​कि wrt सिंक्रोनाइज़ेशन के लिए भी समस्या नहीं होनी चाहिए।
सिलिको में

23

किसी भी स्मार्ट पॉइंटर के बारे में (न सिर्फ साझा किया गया), मुझे नहीं लगता कि किसी एक के संदर्भ को वापस करना कभी भी स्वीकार्य है, और मैं उन्हें संदर्भ या रॉ पॉइंटर द्वारा पास करने में बहुत संकोच करूंगा। क्यों? क्योंकि आप निश्चित नहीं हो सकते कि यह बाद में संदर्भ के माध्यम से उथला-प्रति नहीं होगा। आपका पहला बिंदु इस कारण को परिभाषित करता है कि यह एक चिंता क्यों होनी चाहिए। एकल-थ्रेडेड वातावरण में भी ऐसा हो सकता है। आपको अपने कार्यक्रमों में खराब कॉपी शब्दार्थ डालने के लिए डेटा के समवर्ती उपयोग की आवश्यकता नहीं है। जब आप इसे बंद कर देते हैं, तो आप वास्तव में यह नियंत्रित नहीं करते हैं कि आपके उपयोगकर्ता सूचक के साथ क्या करते हैं, इसलिए अपने एपीआई उपयोगकर्ताओं को स्वयं को लटका देने के लिए पर्याप्त रस्सी देने के दुरुपयोग को प्रोत्साहित न करें।

दूसरे, यदि संभव हो तो अपने स्मार्ट पॉइंटर के कार्यान्वयन को देखें। निर्माण और विनाश नगण्य के करीब होना चाहिए। यदि यह ओवरहेड स्वीकार्य नहीं है, तो स्मार्ट पॉइंटर का उपयोग न करें! लेकिन इससे परे, आपको अपने द्वारा प्राप्त की गई संगणकीय वास्तुकला की भी जांच करनी होगी, क्योंकि सूचक के उपयोग को ट्रैक करने वाले तंत्र के लिए पारस्परिक रूप से अनन्य पहुंच आपको साझा करने के लिए साझा किए गए ऑब्जेक्ट के मात्र निर्माण से अधिक धीमा करने वाली है।

संपादित करें, 3 साल बाद: C ++ में अधिक आधुनिक सुविधाओं के आगमन के साथ, मैं उन मामलों को और अधिक स्वीकार करने के लिए अपना जवाब दूंगा जब आपने केवल एक लंबो लिखा है जो कॉलिंग फ़ंक्शन के दायरे से बाहर कभी नहीं रहता है, और नहीं कहीं और नकल की। यहां, यदि आप साझा सूचक को कॉपी करने के बहुत कम ओवरहेड को बचाना चाहते हैं, तो यह उचित और सुरक्षित होगा। क्यों? क्योंकि आप इस बात की गारंटी दे सकते हैं कि संदर्भ का गलत इस्तेमाल कभी नहीं होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.