एक अलग 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
उस वस्तु के लिए केवल शेष विशिष्ट हो? अलविदा अलविदा वस्तु, बस जहाँ आप कोशिश करने और इसका उपयोग करने वाले हैं।
तो उस सवाल का जवाब देने के दो तरीके हैं:
अपने पूरे कार्यक्रम के स्रोत की बहुत सावधानी से जांच करें जब तक कि आप सुनिश्चित न हों कि ऑब्जेक्ट फ़ंक्शन बॉडी के दौरान नहीं मरेगा।
संदर्भ के बजाय एक अलग ऑब्जेक्ट होने के लिए पैरामीटर को वापस बदलें।
सामान्य सलाह जो यहां लागू होती है: प्रदर्शन के लिए अपने कोड में जोखिम भरे बदलाव करने की जहमत न उठाएं, जब तक कि आपने अपने उत्पाद को एक यथार्थवादी स्थिति में एक प्रोफाईल में समतल कर दिया हो और निर्णायक रूप से मापा जाए कि आप जो बदलाव करना चाहते हैं वह एक प्रदर्शन के लिए महत्वपूर्ण अंतर।
टिप्पणीकार 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
, इसलिए व्यापार बंद अलग हो सकता है।