2012-05-17 7 views
61

मान लें कि मेरे पास एक विधि है जिसमें shared_ptr लौटाता है।संदर्भ या मूल्य से स्मार्ट पॉइंटर्स (shared_ptr) को वापस कैसे करें?

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

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

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

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

उत्तर

80

मूल्य से स्मार्ट पॉइंटर्स लौटें।

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

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

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

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

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

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

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

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

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

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

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

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

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

+0

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

+0

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

+0

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

17

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

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

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

संबंधित मुद्दे