सी ++ - एसटीडी के संदर्भों को साझा करना :: साझा_प्रति या बढ़ावा :: साझा_प्रति


115

अगर मेरे पास एक ऐसा कार्य है shared_ptr, जिसके साथ काम करने की आवश्यकता है , तो क्या यह इसे संदर्भ देने के लिए अधिक कुशल नहीं होगा (इसलिए shared_ptrऑब्जेक्ट को कॉपी करने से बचने के लिए )? संभावित बुरे दुष्प्रभाव क्या हैं? मैं दो संभावित मामलों की कल्पना करता हूं:

1) फ़ंक्शन के अंदर एक प्रति तर्क से बना है, जैसे कि

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) फ़ंक्शन के अंदर तर्क का उपयोग किया जाता है, जैसे अंदर

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

मैं boost::shared_ptr<foo>संदर्भ के बजाय मूल्य से गुजरने का एक अच्छा कारण दोनों मामलों में नहीं देख सकता । मूल्य से गुजरना केवल "अस्थायी रूप से" प्रतिलिपि के कारण संदर्भ गणना को बढ़ाता है, और फिर फ़ंक्शन के दायरे से बाहर निकलने पर इसे घटाता है। क्या मैं कुछ देख रहा हूँ?

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


मुझे नहीं पता कि आप अपने प्रश्न के टैग को संशोधित कर सकते हैं, लेकिन कृपया वहां एक बढ़ावा टैग जोड़ने का प्रयास करें। मैंने इस प्रश्न की तलाश करने की कोशिश की, लेकिन मुझे कोई भी नहीं मिला क्योंकि मैंने बूस्ट और स्मार्ट-पॉइंटर टैग की तलाश की थी। इसलिए मैंने अपना प्रश्न मेरे स्वयं के प्रश्न को लिखने के बाद पाया
एडिसन गुस्तावो मुएंज़

जवाबों:


113

एक अलग shared_ptrउदाहरण की बात यह shared_ptrहै कि (जहाँ तक संभव हो) गारंटी देना है कि जब तक यह गुंजाइश है, तब तक वह वस्तु जो अभी भी मौजूद है, क्योंकि इसका संदर्भ गणना कम से कम 1 होगी।

Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
    // sp points to an object that cannot be destroyed during this function
}

तो एक संदर्भ का उपयोग करके shared_ptr, आप उस गारंटी को अक्षम करते हैं। तो अपने दूसरे मामले में:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}

आपको कैसे पता चलेगा कि sp->do_something()एक शून्य सूचक के कारण उड़ा नहीं होगा?

यह सब निर्भर करता है कि कोड के उन '...' अनुभागों में क्या है। क्या होगा यदि आप पहली बार '...' के दौरान कुछ कहते हैं, shared_ptrजिसका एक ही वस्तु को साफ़ करने का साइड-इफ़ेक्ट (कोड के दूसरे भाग में) है ? और क्या होगा अगर यह shared_ptrउस वस्तु के लिए केवल शेष विशिष्ट हो? अलविदा अलविदा वस्तु, बस जहाँ आप कोशिश करने और इसका उपयोग करने वाले हैं।

तो उस सवाल का जवाब देने के दो तरीके हैं:

  1. अपने पूरे कार्यक्रम के स्रोत की बहुत सावधानी से जांच करें जब तक कि आप सुनिश्चित न हों कि ऑब्जेक्ट फ़ंक्शन बॉडी के दौरान नहीं मरेगा।

  2. संदर्भ के बजाय एक अलग ऑब्जेक्ट होने के लिए पैरामीटर को वापस बदलें।

सामान्य सलाह जो यहां लागू होती है: प्रदर्शन के लिए अपने कोड में जोखिम भरे बदलाव करने की जहमत न उठाएं, जब तक कि आपने अपने उत्पाद को एक यथार्थवादी स्थिति में एक प्रोफाईल में समतल कर दिया हो और निर्णायक रूप से मापा जाए कि आप जो बदलाव करना चाहते हैं वह एक प्रदर्शन के लिए महत्वपूर्ण अंतर।

टिप्पणीकार JQ के लिए अद्यतन करें

यहाँ एक उदाहरण है। यह जानबूझकर सरल है, इसलिए गलती स्पष्ट होगी। वास्तविक उदाहरणों में, गलती इतनी स्पष्ट नहीं है क्योंकि यह वास्तविक विवरण की परतों में छिपी हुई है।

हमारे पास एक फ़ंक्शन है जो कहीं न कहीं एक संदेश भेजेगा। यह एक बड़ा संदेश हो सकता है इसके बजाय इसका उपयोग करने की std::stringसंभावना नकल हो जाती है क्योंकि यह कई स्थानों पर पारित हो जाता है, हम shared_ptrएक स्ट्रिंग का उपयोग करते हैं :

void send_message(std::shared_ptr<std::string> msg)
{
    std::cout << (*msg.get()) << std::endl;
}

(हम सिर्फ इस उदाहरण के लिए कंसोल पर "भेज" देते हैं)।

अब हम पिछले संदेश को याद रखने के लिए एक सुविधा जोड़ना चाहते हैं। हम निम्नलिखित व्यवहार चाहते हैं: एक चर मौजूद होना चाहिए जिसमें सबसे हाल ही में भेजा गया संदेश है, लेकिन जब कोई संदेश वर्तमान में भेजा जा रहा है, तो कोई पिछला संदेश नहीं होना चाहिए (भेजने से पहले चर को रीसेट किया जाना चाहिए)। इसलिए हम नए चर की घोषणा करते हैं:

std::shared_ptr<std::string> previous_message;

फिर हम निर्दिष्ट किए गए नियमों के अनुसार अपने कार्य में संशोधन करते हैं:

void send_message(std::shared_ptr<std::string> msg)
{
    previous_message = 0;
    std::cout << *msg << std::endl;
    previous_message = msg;
}

इसलिए, इससे पहले कि हम भेजना शुरू करें हम वर्तमान पिछले संदेश को छोड़ दें, और फिर भेजने के पूरा होने के बाद हम नए पिछले संदेश को संग्रहीत कर सकते हैं। सब अच्छा। यहां कुछ परीक्षण कोड दिए गए हैं:

send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);

और उम्मीद के मुताबिक, यह Hi!दो बार प्रिंट करता है ।

अब श्री मेंटेनर आता है, जो कोड को देखता है और सोचता है: अरे, यह पैरामीटर send_messageएक है shared_ptr:

void send_message(std::shared_ptr<std::string> msg)

जाहिर है कि इसे बदला जा सकता है:

void send_message(const std::shared_ptr<std::string> &msg)

प्रदर्शन में वृद्धि के बारे में सोचो यह लाएगा! (कोई बात नहीं कि हम किसी चैनल पर आम तौर पर एक बड़ा संदेश भेजने वाले हैं, इसलिए प्रदर्शन में वृद्धि बहुत कम होगी, क्योंकि यह असहनीय है)।

लेकिन असली समस्या यह है कि अब परीक्षण कोड अपरिभाषित व्यवहार प्रदर्शित करेगा (दृश्य C ++ 2010 डिबग बिल्ड में, यह क्रैश हो जाता है)।

श्री मेंटेनर इससे आश्चर्यचकित हैं, लेकिन send_messageहो रही समस्या को रोकने के प्रयास में रक्षात्मक जाँच जोड़ता है :

void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

लेकिन निश्चित रूप से यह अभी भी आगे बढ़ता है और दुर्घटनाग्रस्त हो जाता है, क्योंकि msgजब send_messageकहा जाता है तो कभी भी अशक्त नहीं होता है।

जैसा कि मैं कहता हूं, सभी कोड एक तुच्छ उदाहरण में एक साथ इतने करीब हैं, गलती को ढूंढना आसान है। लेकिन परिवर्तनशील वस्तुओं है कि एक दूसरे के संकेत पकड़ के बीच और अधिक जटिल रिश्तों के साथ वास्तविक कार्यक्रम, में, यह करने के लिए आसान है करना गलती का पता लगाने के लिए आवश्यक परीक्षण मामलों के निर्माण के लिए गलती, और हार्ड।

आसान समाधान, जहाँ आप चाहते हैं कि एक फ़ंक्शन पूरे विश्व में shared_ptrनिरंतर अशक्त रहने में सक्षम हो, यह फ़ंक्शन shared_ptrमौजूदा के संदर्भ पर निर्भर होने के बजाय, अपने स्वयं के सत्य को आवंटित करने के लिए है shared_ptr

नकारात्मक पक्ष यह है कि नकल एक shared_ptrस्वतंत्र नहीं है: यहां तक ​​कि "लॉक-फ्री" कार्यान्वयन को थ्रेडिंग गारंटी का सम्मान करने के लिए एक इंटरलॉक किए गए ऑपरेशन का उपयोग करना होगा। तो ऐसी परिस्थितियाँ हो सकती हैं जहाँ एक कार्यक्रम को एक shared_ptrमें बदलकर महत्वपूर्ण रूप से विकसित किया जा सकता है shared_ptr &। लेकिन यह एक ऐसा बदलाव नहीं है जिसे सभी कार्यक्रमों के लिए सुरक्षित रूप से बनाया जा सकता है। यह कार्यक्रम के तार्किक अर्थ को बदल देता है।

ध्यान दें कि यदि हम std::stringइसके बजाय std::shared_ptr<std::string>और इसके बजाय इसका उपयोग करते हैं तो एक समान बग उत्पन्न होगा :

previous_message = 0;

संदेश को साफ करने के लिए, हमने कहा:

previous_message.clear();

तब लक्षण अपरिभाषित व्यवहार के बजाय एक खाली संदेश का आकस्मिक भेजना होगा। एक बहुत बड़ी स्ट्रिंग की एक अतिरिक्त प्रतिलिपि की लागत एक कॉपी करने की लागत की तुलना में बहुत अधिक महत्वपूर्ण हो सकती है shared_ptr, इसलिए व्यापार बंद अलग हो सकता है।


16
शेयर्ड_प्टर जो पहले ही पास हो चुका है, कॉल साइट पर एक दायरे में रहता है। आप एक विस्तृत परिदृश्य बनाने में सक्षम हो सकते हैं, जहां इस प्रश्न का कोड झूलने वाले सूचक के कारण उड़ जाएगा, लेकिन फिर मुझे लगता है कि आपको संदर्भ पैरामीटर से बड़ी समस्याएं हैं!
मैग्नस हॉफ

10
यह एक सदस्य में संग्रहीत किया जा सकता है। आप उस सदस्य को साफ़ करने के लिए कुछ कह सकते हैं। Smart_ptr का पूरा बिंदु यह है कि कॉल स्टैक के आसपास घोंसले वाले पदानुक्रम या स्कोप में जीवनकाल को समन्वयित करने से बचें, इसीलिए यह मानना ​​सबसे अच्छा है कि ऐसे कार्यक्रमों में जीवनकाल ऐसा नहीं करते हैं।
डैनियल इयरविकर

7
हालांकि यह वास्तव में मेरा दृष्टिकोण नहीं है! अगर आपको लगता है कि मैं जो कह रहा हूं वह मेरे कोड के साथ करने के लिए कुछ विशिष्ट है, तो आप मुझे समझ नहीं पाए होंगे। मैं कारण के एक अपरिहार्य निहितार्थ के बारे में बात कर रहा हूँ साझा किए गए_प्रति प्रथम स्थान पर मौजूद हैं: कई ऑब्जेक्ट जीवन काल केवल फ़ंक्शन कॉल से संबंधित नहीं हैं।
डैनियल इयरविकर

8
@DanielEarwicker आपके सभी बिंदुओं से पूरी तरह सहमत है और विपक्ष के स्तर से हैरान है। ऐसा कुछ जो आपकी चिंताओं को और भी अधिक प्रासंगिक बनाता है, जब यह शामिल हो जाता है तो एक वस्तु की वैधता के बारे में गारंटी बहुत अधिक महत्वपूर्ण हो जाती है। अच्छा उत्तर।
रेडमैन

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

115

मैंने खुद को सबसे ज्यादा मत वाले जवाब से असहमति जताई, इसलिए मैं विशेषज्ञ मतदाताओं की तलाश में गया और यहां वे हैं। से http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

हर्ब सटर: "जब आप साझा करें

स्कॉट मेयर्स: "जब आप इसे मान से पास करते हैं, या इसे संदर्भ से पास करते हैं, तो इसे साझा करने के लिए साझा करने के बारे में कुछ विशेष नहीं है। ठीक उसी विश्लेषण का उपयोग करें जो आप किसी अन्य उपयोगकर्ता परिभाषित प्रकार के लिए उपयोग करते हैं। लोगों को यह धारणा लगती है कि share_ptr किसी तरह हल करती है। सभी प्रबंधन की समस्याएं, और क्योंकि यह छोटा है, यह आवश्यक है कि मूल्य से पारित करना सस्ता है। इसे कॉपी करना होगा, और इसके साथ एक लागत जुड़ी है ... इसे मूल्य से पारित करना महंगा है, इसलिए अगर मैं दूर हो सकता हूं यह मेरे कार्यक्रम में उचित शब्दार्थ के साथ है, मैं इसे कॉन्स्ट या संदर्भ के संदर्भ में पास करने जा रहा हूं "

हर्ब सटर: "हमेशा उन्हें कॉन्स्टेंस के संदर्भ में पास करें, और कभी-कभार शायद इसलिए क्योंकि आप जानते हैं कि आपने जो कहा है वह उस चीज़ को संशोधित कर सकता है जिसे आपको एक संदर्भ मिला, हो सकता है कि आप मूल्य से गुजरें ... यदि आप उन्हें मापदंडों के रूप में कॉपी करते हैं, तो ओह मेरी अच्छाई आपको लगभग कभी भी उस संदर्भ गणना को टक्कर देने की आवश्यकता नहीं है क्योंकि यह वैसे भी जीवित है, और आपको इसे संदर्भ से गुजरना चाहिए, इसलिए कृपया इसे "

अद्यतन: यहाँ पर जड़ी बूटी का विस्तार हुआ है: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ , हालांकि कहानी का नैतिक यह है कि आपको पास नहीं होना चाहिए share_ptrs बिल्कुल "जब तक आप स्मार्ट पॉइंटर का उपयोग या हेरफेर नहीं करना चाहते हैं, जैसे कि स्वामित्व साझा करना या स्थानांतरित करना।"


8
अच्छा लगा! एसओ पर पारंपरिक ज्ञान का सार्वजनिक रूप से खंडन करते हुए इस विषय पर दो अग्रणी विशेषज्ञों को देखना बहुत अच्छा है।
स्टीफन टॉल्क्सडॉर्फ

3
"जब आप इसे मूल्य से पास करते हैं, या इसे संदर्भ से पास करते हैं, तो यह साझा करने के बारे में कुछ खास नहीं है" - मैं वास्तव में इसके लिए सहमत नहीं हूं। यह विशेष है। व्यक्तिगत रूप से मैं इसे सुरक्षित रूप से खेलना चाहूंगा, और थोड़ा सा प्रदर्शन हिट करूंगा। अगर कोड का कोई विशेष क्षेत्र है जिसे मुझे अनुकूलित करने की आवश्यकता है, तो मुझे यकीन है कि मैं const रेफरी द्वारा शेयर्ड_ptr पास के प्रदर्शन लाभों को देखूंगा।
जेसन

3
यह भी ध्यान रखना दिलचस्प है कि, जबकि shared_ptrएस के अति प्रयोग पर सहमति थी, पास-दर-मूल्य-बनाम-संदर्भ मुद्दे के बारे में कोई समझौता नहीं था।
निकोल बोलस

2
स्कॉट मेयर्स: "तो अगर मैं अपने कार्यक्रम में उचित शब्दार्थ के साथ इसे दूर कर सकता हूं ..." अर्थात मेरे जवाब का बिल्कुल भी विरोध नहीं करता है, जो बताता है कि क्या मापदंडों को बदलने से const &शब्दार्थ को प्रभावित करना आसान होगा सरल कार्यक्रम।
डैनियल ईयरविकर

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

22

मैं इस अभ्यास के खिलाफ सलाह दूंगा जब तक कि आप और आपके साथ काम करने वाले अन्य प्रोग्रामर वास्तव में नहीं जानते कि आप सभी क्या कर रहे हैं।

सबसे पहले, आपको पता नहीं है कि आपकी कक्षा में इंटरफ़ेस कैसे विकसित हो सकता है और आप अन्य प्रोग्रामर को बुरे काम करने से रोकना चाहते हैं। साझाकरण को संदर्भ द्वारा साझा करना कुछ ऐसा नहीं है जिसे एक प्रोग्रामर को देखने की उम्मीद करनी चाहिए, क्योंकि यह मुहावरेदार नहीं है, और इससे इसे गलत तरीके से उपयोग करना आसान हो जाता है। प्रोग्राम डिफेंसिवली: इंटरफ़ेस को गलत तरीके से उपयोग करना कठिन बना। संदर्भ से गुजर बस बाद में समस्याओं को आमंत्रित करने जा रहा है।

दूसरा, तब तक ऑप्टिमाइज़ न करें जब तक आपको पता न हो कि यह विशेष वर्ग एक समस्या है। पहले प्रोफ़ाइल, और फिर यदि आपके कार्यक्रम को वास्तव में संदर्भ द्वारा पारित करके दिए गए बढ़ावा की आवश्यकता है, तो हो सकता है। अन्यथा, छोटे सामान (यानी अतिरिक्त एन निर्देशों को मान से पास करने में पसीना नहीं आता है) इसके बजाय डिजाइन, डेटा संरचनाओं, एल्गोरिदम और दीर्घकालिक स्थिरता के बारे में चिंता करें।


हालाँकि, litb का उत्तर तकनीकी रूप से सही है, लेकिन प्रोग्रामर्स के "आलस्य" (मैं बहुत आलसी भी हूँ!) को कभी कम न समझें। लिटलेनग का उत्तर बेहतर है, कि एक साझा_प्रति का संदर्भ अप्रत्याशित होगा, और संभवतः (शायद) एक अनावश्यक अनुकूलन है जो भविष्य के रखरखाव को अधिक चुनौतीपूर्ण बनाता है।
netJff

18

हां, एक संदर्भ लेना वहां ठीक है। आप साझा की गई विधि को स्वामित्व देने का इरादा नहीं रखते हैं; यह केवल इसके साथ काम करना चाहता है। आप पहले मामले के लिए भी एक संदर्भ ले सकते हैं, क्योंकि आप इसे वैसे भी कॉपी करते हैं। लेकिन पहले मामले के लिए, यह स्वामित्व लेता है। इस ट्रिक को अभी भी केवल एक बार कॉपी करना है:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

जब आप इसे वापस लौटाते हैं (तो आपको संदर्भ नहीं देना चाहिए) भी कॉपी करना चाहिए। क्योंकि आपकी कक्षा को यह पता नहीं है कि ग्राहक इसके साथ क्या कर रहा है (यह एक पॉइंटर को स्टोर कर सकता है और फिर बड़ा धमाका होता है)। यदि यह बाद में पता चला कि यह एक अड़चन है (पहली प्रोफ़ाइल!), तो आप अभी भी एक संदर्भ वापस कर सकते हैं।


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


11

यह पास करने के लिए समझदार shared_ptrहै const&। यह परेशानी का कारण नहीं बनेगा (अप्रत्याशित स्थिति को छोड़कर, जिसे shared_ptrफ़ंक्शन कॉल के दौरान हटा दिया गया है, जैसा कि इयरविकर द्वारा विस्तृत किया गया है) और यदि आप इनमें से बहुत सारे पास करते हैं तो यह तेजी से होगा। याद है; डिफ़ॉल्ट boost::shared_ptrथ्रेड सुरक्षित है, इसलिए इसे कॉपी करने पर थ्रेड सुरक्षित वेतन वृद्धि शामिल है।

const&केवल के बजाय उपयोग करने का प्रयास करें &, क्योंकि अस्थायी वस्तुओं को गैर-कॉन्स्टेंस संदर्भ द्वारा पारित नहीं किया जा सकता है। (भले ही MSVC में एक भाषा विस्तार आपको इसे वैसे भी करने की अनुमति देता है)


3
हां, मैं हमेशा कॉन्स्ट रेफरेंस का उपयोग करता हूं, मैं इसे अपने उदाहरण में रखना भूल गया। वैसे भी, MSVC एक बग के लिए नॉन-कॉस्ट-रेफरेंस को नॉन-कास्ट रेफरेंस से बाँधने की अनुमति देता है, लेकिन क्योंकि डिफ़ॉल्ट रूप से इसके पास "C / C ++ -> लैंग्वेज -> डिसएबल लैंग्वेज एक्सटेंशन" सेट "NO" है। इसे सक्षम करें और यह उन्हें संकलित नहीं करेगा ...
abigagli

abigagli: गंभीरता से? मिठाई! मैं इसे काम पर लागू करूंगा, कल पहली बात;)
मैग्नस हॉफ

10

दूसरे मामले में, ऐसा करना सरल है:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

आप इसे कॉल कर सकते हैं

only_work_with_sp(*sp);

3
यदि आप पॉइंटर रेफरेंस का उपयोग करने के लिए कन्वेंशन को अपनाते हैं, तो आपको पॉइंटर की कॉपी लेने की आवश्यकता नहीं है, यह आपके इरादे को भी दस्तावेज करने का काम करता है। यह आपको एक कॉन्स्टेंट रेफरेंस का उपयोग करने का मौका भी देता है।
मार्क रैनसम

हां, मैं वस्तुओं के संदर्भ के उपयोग पर सहमत हूं कि यह व्यक्त करने के लिए कि फ़ंक्शन उस ऑब्जेक्ट के बारे में कुछ भी "याद" नहीं कर रहा है। आम तौर पर मैं सूचक औपचारिक तर्कों का उपयोग करता हूं यदि फ़ंक्शन ऑब्जेक्ट का "ट्रैकिंग" है
abigagli

3

मैं "सादे" संदर्भ से बचना चाहूंगा जब तक कि फ़ंक्शन विस्फोटक रूप से सूचक को संशोधित नहीं कर सकता।

एक const &उदाहरण के लिए आगे अनुकूलन सक्षम करने के लिए, कुछ स्थितियों दूर इनलाइन करने की तरह - जब छोटे कार्यों बुला एक समझदार सूक्ष्म अनुकूलन हो सकता है। इसके अलावा, वेतन वृद्धि / वृद्धि - चूंकि यह थ्रेड सुरक्षित है - एक सिंक्रनाइज़ेशन बिंदु है। मैं यह उम्मीद नहीं करूंगा कि अधिकांश परिदृश्यों में एक बड़ा अंतर होगा, हालांकि।

आम तौर पर, आपको सरल शैली का उपयोग करना चाहिए जब तक कि आपके पास कारण न हो। फिर, या तो const &लगातार उपयोग करें , या टिप्पणी जोड़ें कि यदि आप इसे केवल कुछ स्थानों पर उपयोग करते हैं।


3

मैं कॉन्स्ट रेफरेंस द्वारा साझा पॉइंटर पास करने की वकालत करूंगा - एक शब्दार्थ जो कि पॉइंटर के साथ पास किया जा रहा है वह पॉइंटर नहीं है, जो डेवलपर्स के लिए एक साफ मुहावरा है।

एकमात्र थ्रेड कई थ्रेड प्रोग्रामों में है जो साझा पॉइंटर द्वारा इंगित की जा रही वस्तु दूसरे धागे में नष्ट हो जाती है। तो यह कहना सुरक्षित है कि साझा पॉइंटर के कास्ट रेफरेंस का उपयोग सिंगल थ्रेडेड प्रोग्राम में सुरक्षित है।

नॉन-कास्ट संदर्भ द्वारा साझा पॉइंटर को पास करना कभी-कभी खतरनाक होता है - इसका कारण है स्वैप और रीसेट फ़ंक्शंस फ़ंक्शन को अंदर ले जा सकते हैं ताकि ऑब्जेक्ट को नष्ट करने के लिए जो फ़ंक्शन रिटर्न के बाद अभी भी मान्य माना जाता है।

यह समय से पहले के अनुकूलन के बारे में नहीं है, मुझे लगता है - यह सीपीयू चक्रों के अनावश्यक अपशिष्ट से बचने के बारे में है जब आप स्पष्ट हैं कि आप क्या करना चाहते हैं और कोडिंग मुहावरे को आपके साथी डेवलपर्स द्वारा दृढ़ता से अपनाया गया है।

बस मेरे 2 सेंट :-)


1
डेविड श्वार्ट्ज द्वारा ऊपर दी गई टिप्पणी देखें "... मैंने एक बहुत ही गंभीर बग का पीछा किया जो एक साझा सूचक के संदर्भ में पारित होने के कारण था। कोड एक वस्तु के राज्य परिवर्तन को संभाल रहा था और जब उसने देखा कि वस्तु की स्थिति बदल गई थी, इसने इसे पिछली स्थिति में वस्तुओं के संग्रह से हटा दिया और इसे नए राज्य में वस्तुओं के संग्रह में स्थानांतरित कर दिया। हटाए गए ऑपरेशन ने ऑब्जेक्ट को अंतिम साझा पॉइंटर को नष्ट कर दिया। सदस्य फ़ंक्शन को साझा पॉइंटर के संदर्भ में बुलाया गया था। संग्रह में। बूम ... "
जेसन हैरिसन

3

ऐसा लगता है कि यहां सभी पेशेवरों और विपक्षों को वास्तव में साझा किए गए किसी भी प्रकार के लिए सामान्यीकृत नहीं किया जा सकता है। मेरी राय में, आपको संदर्भ, कास्ट संदर्भ और मूल्य से गुजरने के शब्दार्थ को जानना चाहिए और इसे सही ढंग से उपयोग करना चाहिए। लेकिन जब तक आपको नहीं लगता कि सभी संदर्भ खराब हैं, तब तक संदर्भ के साथ साझा करने में कुछ भी गलत नहीं है।

उदाहरण पर वापस जाने के लिए:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

आपको कैसे पता चलेगा कि sp.do_something()झूलने वाले पॉइंटर के कारण झटका नहीं लगेगा?

सच्चाई यह है कि, साझा करें या नहीं, कास्ट करें या नहीं, यह तब हो सकता है जब आपके पास कोई डिज़ाइन दोष हो, जैसे कि प्रत्यक्ष या अप्रत्यक्ष रूप से spथ्रेड्स के बीच के स्वामित्व को साझा करना delete this, एक ऑब्जेक्ट को मिस करना , जो आपके पास एक परिपत्र स्वामित्व या अन्य स्वामित्व त्रुटियां हैं।


2

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

उदाहरण के लिए, यह कोड एक त्रुटि उत्पन्न करेगा, लेकिन यदि आप बदलते हैं test()तो यह काम करेगा ताकि साझा सूचक संदर्भ से पारित न हो।

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}

1

मुझे लगता है कि आप समय से पहले अनुकूलन से परिचित हैं और यह या तो शैक्षणिक उद्देश्यों के लिए पूछ रहे हैं या क्योंकि आपने कुछ पूर्व-मौजूदा कोड को अलग-थलग कर दिया है जो कि अंडर-परफॉर्म कर रहा है।

संदर्भ से गुजरना ठीक है

कॉन्स्ट रेफरेंस द्वारा पास करना बेहतर है, और आमतौर पर इसका उपयोग किया जा सकता है, क्योंकि यह इंगित की गई ऑब्जेक्ट पर कॉन्स्टेंस को बाध्य नहीं करता है।

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

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

आपको अपने स्मार्ट पॉइंटर को एक संदर्भ के रूप में संग्रहीत करने से हमेशा बचना चाहिए । आपका Class::take_copy_of_sp(&sp)उदाहरण उसके लिए सही उपयोग दिखाता है।


1
"संदर्भ का उपयोग करने के कारण आपको पॉइंटर खोने का खतरा नहीं है। यह संदर्भ इस बात का सबूत है कि आपके पास स्टैक में पहले स्मार्ट पॉइंटर की एक प्रति है" या डेटा सदस्य ...?
डैनियल इयरविकर

बढ़ावा देने के जादू पर विचार करें: धागा और बढ़ावा :: रेफ: बढ़ावा :: समारोह <int> functionPointer = बढ़ावा :: बाँध (doSomething, बढ़ावा :: रेफरी (sharePtrInstance)); m_workerThread = नया बूस्ट :: थ्रेड (फ़ंक्शनपॉइंट); ... sharePtrInstance हटाएं
जेसन हैरिसन

1

यह मानते हुए कि हम कांस्ट की शुद्धता से चिंतित नहीं हैं (या इससे अधिक, आप का मतलब है कि फ़ंक्शंस को संशोधित किया जा सकता है या डेटा के स्वामित्व को साझा करने में सक्षम होना चाहिए), एक उत्तीर्ण करना :: मूल्य द्वारा साझा किया गया # साझा_ संदर्भ के रूप में इसे पारित करने की तुलना में सुरक्षित है हम मूल बढ़ावा की अनुमति देते हैं :: इसे नियंत्रित करने के लिए साझा किया गया है। निम्नलिखित कोड के परिणामों पर विचार करें ...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

ऊपर दिए गए उदाहरण से हम देखते हैं कि साझाकरण को संदर्भ द्वारा साझा करने से FooTakesReference को मूल सूचक को रीसेट करने की अनुमति मिलती है, जो इसे डेटा को नष्ट करते हुए गिनती का उपयोग 0 तक कम कर देता है। FooTakesValue , हालांकि, मूल पॉइंटर को रीसेट नहीं कर सकता है, गारंटी है कि shareB का डेटा अभी भी उपयोग करने योग्य है। जब कोई अन्य डेवलपर अनिवार्य रूप से साथ आता है और शेयर्स के नाजुक अस्तित्व पर अराजकता फैलाने का प्रयास करता है, तो अराजकता फैल जाती है। हालाँकि, भाग्यशाली शेयर्ड डेवलपर, घर जल्दी जाता है क्योंकि उसकी दुनिया में सब ठीक है।

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


1

सैंडी ने लिखा: "ऐसा लगता है कि यहां सभी पेशेवरों और विपक्षों को वास्तव में किसी भी प्रकार के संदर्भ में सामान्यीकृत किया जा सकता है, न कि केवल साझा किया गया।

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

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

कोड जो अतीत में मौजूद है वह लेखक है, या यहां तक ​​कि यह लेखक की स्मृति है, जिसे व्यवहार के बारे में अंतर्निहित धारणाओं की आवश्यकता होती है, वस्तु जीवनकाल के बारे में विशेष व्यवहार में, स्पष्ट, संक्षिप्त, पठनीय प्रलेखन की आवश्यकता होती है, और फिर कई ग्राहक इसे वैसे भी नहीं पढ़ेंगे! सादगी लगभग हमेशा दक्षता को बढ़ाती है, और कुशल होने के लिए लगभग हमेशा अन्य तरीके हैं। यदि आपको वास्तव में अपने संदर्भ-गिनती-ऑब्जेक्ट्स (और बराबर ऑपरेटर) की प्रतिलिपि बनाने वालों द्वारा गहरी प्रतिलिपि बनाने से बचने के लिए संदर्भों द्वारा मानों को पास करने की आवश्यकता है, तो शायद आपको गहरी-प्रतिलिपि किए गए डेटा को संदर्भ गणना किए गए बिंदु होने वाले तरीकों पर विचार करना चाहिए जल्दी से नकल की। (बेशक, यह सिर्फ एक डिजाइन परिदृश्य है जो आपकी स्थिति पर लागू नहीं हो सकता है)।


1

मैं एक परियोजना में काम करता था कि सिद्धांत मूल्य द्वारा स्मार्ट पॉइंटर्स पास करने के बारे में बहुत मजबूत था। जब मुझे कुछ प्रदर्शन विश्लेषण करने के लिए कहा गया - मैंने पाया कि स्मार्ट पॉइंटर्स के संदर्भ काउंटरों की वृद्धि और क्षय के लिए आवेदन उपयोग किए गए प्रोसेसर समय के 4-6% के बीच खर्च करता है।

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

यदि आप एक संदर्भ के साथ जाने का निर्णय लेते हैं, तो कॉन्स्ट रेफरेंस का उपयोग करने का मुख्य कारण यह है कि जब आप इंटरफ़ेस में उपयोग किए जाने वाले वर्ग को इनहेरिट करने के लिए साझा किए गए पॉइंटर को पास करने की आवश्यकता हो, तो इसका मतलब यह हो सकता है।


0

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


0
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. मान से पास करें:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
  2. संदर्भ से गुजरें:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
  3. सूचक द्वारा पास:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }

0

हर कोड का टुकड़ा कुछ न कुछ होना चाहिए। यदि आप अनुप्रयोग में हर जगह एक साझा पॉइंटर पास करते हैं, तो इसका मतलब है "मैं इस बारे में अनिश्चित हूं कि अन्यत्र क्या हो रहा है, इसलिए मैं अपनी सुरक्षा का पक्ष लेता हूं "। यह वह नहीं है जिसे मैं अन्य प्रोग्रामरों के लिए एक अच्छा विश्वास संकेत कहता हूं जो कोड से परामर्श कर सकते थे।

वैसे भी, यदि किसी फ़ंक्शन को एक कॉस्ट रेफरेंस मिलता है और आप "अनिश्चित" हैं, तो भी आप पॉइंटर के एक मजबूत संदर्भ को जोड़ने के लिए, फ़ंक्शन के प्रमुख पर साझा पॉइंटर की एक प्रति बना सकते हैं। यह डिजाइन के बारे में संकेत के रूप में भी देखा जा सकता है ("सूचक को कहीं और संशोधित किया जा सकता है")।

तो हाँ, IMO, डिफ़ॉल्ट " कॉन्स्टेंट संदर्भ द्वारा पास " होना चाहिए ।

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