क्या इसे हटाने के बाद किसी पॉइंटर को NULL करना अच्छा है?


150

मैं कह कर शुरू करूंगा, स्मार्ट पॉइंटर्स का उपयोग करें और आपको इस बारे में कभी चिंता नहीं करनी चाहिए।

निम्नलिखित कोड में क्या समस्याएं हैं?

Foo * p = new Foo;
// (use p)
delete p;
p = NULL;

यह एक जवाब और एक अन्य प्रश्न के लिए टिप्पणियों द्वारा छिड़ गया था । नील बटरवर्थ की एक टिप्पणी ने कुछ उभार लाए:

NULL को डिलीट करने के लिए पॉइंटर्स सेट करना C ++ में सार्वभौमिक अच्छा अभ्यास नहीं है। ऐसे समय होते हैं जब यह करना अच्छी बात है, और ऐसे समय जब यह व्यर्थ है और त्रुटियों को छिपा सकता है।

वहाँ बहुत सारी परिस्थितियाँ हैं जहाँ यह मदद नहीं करेगा। लेकिन मेरे अनुभव में, यह चोट नहीं पहुँचा सकता। कोई मुझे प्रबुद्ध करे।


6
@Andre: तकनीकी रूप से, यह अपरिभाषित है। ऐसा होने की संभावना है कि आप पहले की तरह ही मेमोरी एक्सेस करते हैं, लेकिन इसका उपयोग अब कुछ और हो सकता है। यदि आप मेमोरी को दो बार हटाते हैं, तो यह आपके प्रोग्राम निष्पादन को हार्ड-टू-फाइंड तरीके से खराब करने की संभावना है। यह deleteएक शून्य सूचक के लिए सुरक्षित है , हालांकि, जो एक संकेतक को शून्य करने का एक कारण अच्छा हो सकता है।
डेविड थॉर्नले

5
@ एंड्रे पेना, यह अपरिभाषित है। अक्सर यह दोहराए जाने योग्य भी नहीं होता है। आप डीबगिंग के दौरान त्रुटि को अधिक दृश्यमान बनाने के लिए पॉइंटर को सेट करते हैं, और शायद इसे और अधिक दोहराने योग्य बनाते हैं।
मार्क रैनसम

3
@ आंद्रे: कोई नहीं जानता। यह अपरिभाषित व्यवहार है। यह एक एक्सेस उल्लंघन के साथ क्रैश हो सकता है, या यह बाकी एप्लिकेशन द्वारा उपयोग की जाने वाली मेमोरी को ओवरराइट कर सकता है। भाषा मानक क्या होता है इसकी कोई गारंटी नहीं देता है, और ऐसा होने पर आप अपने आवेदन पर भरोसा नहीं कर सकते। यह परमाणु मिसाइलों को निकाल सकता था या आपके हार्डड्राइव को स्वरूपित कर सकता था। यह आपके ऐप की मेमोरी को दूषित कर सकता है, या यह राक्षसों को आपकी नाक से बाहर निकाल सकता है। सभी दांव बंद हैं।
जलफ

16
उड़ान राक्षसों एक बग नहीं, एक विशेषता है।
Jball

3
यह प्रश्न डुप्लिकेट नहीं है क्योंकि दूसरा प्रश्न C के बारे में है और यह C ++ के बारे में है। बहुत सारे उत्तर स्मार्ट पॉइंटर्स जैसी चीजों पर टिका है, जो C ++ में उपलब्ध नहीं हैं।
एड्रियन मैकार्थी

जवाबों:


86

0 (जो कि मानक C ++ में "null" है, एक पॉइंटर सेट करना, C से NULL डिफाइन कुछ अलग है) डबल डिलीट होने पर क्रैश से बचा जाता है।

निम्नलिखित को धयान मे रखते हुए:

Foo* foo = 0; // Sets the pointer to 0 (C++ NULL)
delete foo; // Won't do anything

जहाँ तक:

Foo* foo = new Foo();
delete foo; // Deletes the object
delete foo; // Undefined behavior 

दूसरे शब्दों में, यदि आप हटाए गए बिंदुओं को 0 पर सेट नहीं करते हैं, तो यदि आप डबल डिलीट कर रहे हैं, तो आप मुसीबत में पड़ जाएंगे। डिलीट करने के बाद 0 पर पॉइंटर्स सेट करने का एक तर्क यह होगा कि ऐसा करने से सिर्फ मास्क ही डबल डिलीट हो जाते हैं और उन्हें अनहेल्दी छोड़ देता है।

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

अंत में, ऑब्जेक्ट आवंटन के प्रबंधन के बारे में एक विचारधारा, मेरा सुझाव है कि आप अपनी आवश्यकताओं के आधार पर, std::unique_ptrसख्त स्वामित्व / एकल स्वामित्व के लिए, std::shared_ptrसाझा स्वामित्व या किसी अन्य स्मार्ट पॉइंटर कार्यान्वयन के लिए एक नज़र डालें ।


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

28
यहां समस्या यह है कि आपके पास एक डबल डिलीट है। पॉइंटर बनाना NULL सिर्फ उस तथ्य को छिपाता है जो इसे सही नहीं करता है या इसे किसी भी सुरक्षित बनाता है। Imagaine एक mainainer एक साल बाद वापस आ रहा है और फू को हटाते हुए देख रहा है। अब वह मानता है कि वह पॉइंटर का फिर से उपयोग कर सकता है दुर्भाग्य से वह दूसरे डिलीट को मिस कर सकता है (यह एक ही फंक्शन में भी नहीं हो सकता है) और अब पॉइंटर का दोबारा इस्तेमाल करने पर दूसरा डिलीट हो जाता है। दूसरी डिलीट के बाद कोई भी एक्सेस अब एक बड़ी समस्या है।
मार्टिन यॉर्क

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

AFAIK, std :: auto_ptr को आगामी c ++ मानक में रखा गया है
rafak

मैं यह नहीं कहूंगा कि यह गलत है, यह ऐसा लगता है जैसे विचार अभी चला गया है। बल्कि, इसके साथ प्रतिस्थापित किया जा रहा है unique_ptr, जो कि वह auto_ptrकरने की कोशिश करता है, जिसमें चाल शब्दार्थ है।
GManNickG

55

आपके द्वारा हटाए जाने के बाद NULL को संकेत देना निश्चित रूप से चोट नहीं पहुंचा सकता है, लेकिन यह अक्सर एक अधिक मौलिक समस्या पर एक बैंड-सहायता का एक सा है: आप पहली जगह में एक पॉइंटर का उपयोग क्यों कर रहे हैं? मैं दो विशिष्ट कारण देख सकता हूं:

  • आप बस ढेर पर आवंटित कुछ चाहते थे। जिस स्थिति में इसे RAII ऑब्जेक्ट में लपेटना अधिक सुरक्षित और क्लीनर होता। RAII ऑब्जेक्ट का दायरा तब समाप्त करें जब आपको ऑब्जेक्ट की आवश्यकता नहीं है। यह कैसे std::vectorकाम करता है, और यह गलती से इंगित मेमोरी को चारों ओर छोड़ने की समस्या को हल करता है। कोई संकेत नहीं हैं।
  • या शायद आप कुछ जटिल साझा स्वामित्व शब्दार्थ चाहते थे। पॉइंटर से लौटाया गया newहो सकता है कि deleteजैसा कहा जाता है वैसा ही न हो । कई वस्तुओं ने इस दौरान एक साथ वस्तु का उपयोग किया होगा। उस मामले में, एक साझा सूचक या ऐसा ही कुछ बेहतर होता।

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


15
तो आप यह तर्क दे रहे हैं कि पहली जगह में कच्चा पॉइंटर नहीं होना चाहिए था, और कुछ भी जिसमें कहा गया था कि पॉइंटर को "अच्छा अभ्यास" शब्द के साथ आशीर्वाद नहीं दिया जाना चाहिए? काफी उचित।
मार्क रैनसम

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

3
लेकिन तत्काल प्रश्न का उत्तर देने के लिए, नहीं, मैं यह नहीं देखता कि बिंदुओं को शून्य करने के लिए सेटिंग कभी भी त्रुटियों का कारण बन सकती है
जल्फ

7
मैं असहमत हूं - ऐसे मामले हैं जब एक पॉइंटर का उपयोग करना अच्छा होता है। उदाहरण के लिए, स्टैक पर 2 चर हैं और आप उनमें से किसी एक को चुनना चाहते हैं। या आप एक फ़ंक्शन के लिए एक वैकल्पिक चर पास करना चाहते हैं। मैं कहूंगा, आपको कभी भी कच्चे सूचक का उपयोग नहीं करना चाहिए new
आरएलबोंड १9

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

43

मुझे और भी बेहतर अभ्यास मिला है: जहाँ संभव हो, चर का दायरा समाप्त करें!

{
    Foo* pFoo = new Foo;
    // use pFoo
    delete pFoo;
}

17
हां, RAII आपका मित्र है। इसे एक कक्षा में लपेटें और यह और भी सरल हो जाता है। या एसटीएल का उपयोग करके स्मृति को अपने आप को संभालना नहीं है!
ब्रायन

24
हां वास्तव में, यह सबसे अच्छा विकल्प है। हालांकि इस सवाल का जवाब नहीं है।
मार्क रैनसम

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

2
यह सोचने के लिए आओ, यदि आप ऐसा कर सकते हैं, तो आप सिर्फ ढेर को क्यों नहीं भूलेंगे और अपनी सभी मेमोरी को स्टैक से हटा देंगे?
सैन जैसिंटो

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

31

मैं हमेशा ऑब्जेक्ट को हटाने के बाद NULL(अब nullptr) एक पॉइंटर सेट करता हूं ।

  1. यह फ़्रीड मेमोरी के लिए कई संदर्भों को पकड़ने में मदद कर सकता है (अपने प्लेटफ़ॉर्म दोषों को एक शून्य सूचक के डेरेफ़ पर मानते हुए)।

  2. यह सभी संदर्भों को मेमोरी को फ्री होल्ड करने के लिए नहीं पकड़ेगा, उदाहरण के लिए, आपके पास पॉइंटर की प्रतियाँ हैं। लेकिन कुछ भी नहीं से बेहतर है।

  3. यह एक डबल-डिलीट मास्क करेगा, लेकिन मुझे लगता है कि वे पहले से मुक्त मेमोरी तक पहुंच से कम आम हैं।

  4. कई मामलों में कंपाइलर इसे दूर अनुकूलित करने जा रहा है। तो यह तर्क कि यह अनावश्यक है, मुझे राजी नहीं करता है।

  5. यदि आप पहले से ही RAII का उपयोग कर रहे हैं, तो deleteआपके कोड में कई s शुरू नहीं होते हैं, इसलिए यह तर्क कि अतिरिक्त असाइनमेंट अव्यवस्था का कारण है, मुझे राजी नहीं करता है।

  6. यह अक्सर सुविधाजनक होता है, जब डिबगिंग होता है, तो बासी सूचक के बजाय अशक्त मान देखने के लिए।

  7. यदि यह अभी भी आपको परेशान करता है, तो इसके बजाय एक स्मार्ट पॉइंटर या एक संदर्भ का उपयोग करें।

जब संसाधन मुक्त होता है (जो आमतौर पर केवल एक RAII रैपर के विनाशकर्ता में होता है जो संसाधन को एनकैप्सुलेट करने के लिए लिखा जाता है) मैंने अन्य प्रकार के संसाधन हैंडल को भी सेट किया है।

मैंने एक बड़े (9 मिलियन स्टेटमेंट) वाणिज्यिक उत्पाद (मुख्य रूप से C) में काम किया। एक बिंदु पर, हमने मैक्रो मैजिक का उपयोग किया, जब मेमोरी को मुक्त कर दिया गया था। इसने तुरंत बहुत सारे गुप्त कीड़े उजागर किए जो तुरंत ठीक कर दिए गए थे। जहां तक ​​मुझे याद है, हमारे पास कभी भी एक डबल-फ्री बग नहीं था।

अद्यतन: Microsoft का मानना ​​है कि यह सुरक्षा के लिए एक अच्छा अभ्यास है और अपनी SDL नीतियों में अभ्यास की सिफारिश करता है। यदि आप / SDL विकल्प के साथ संकलित करते हैं, तो जाहिर है MSVC ++ 11 हटाए गए सूचक को स्वचालित रूप से (कई परिस्थितियों में) समाप्त कर देगा ।


12

सबसे पहले, इस पर और निकटता से संबंधित विषयों पर बहुत सारे मौजूदा प्रश्न हैं, उदाहरण के लिए NULL को सूचक सेट क्यों नहीं हटाता है?

आपके कोड में, वह समस्या जो (उपयोग p) पर जाती है। उदाहरण के लिए, यदि आपके पास कहीं ऐसा कोड है:

Foo * p2 = p;

फिर N को पूरा करने के लिए p को सेट करना बहुत कम है, क्योंकि आपको अभी भी चिंता करने के लिए सूचक P2 है।

यह कहना नहीं है कि NULL को पॉइंटर सेट करना हमेशा व्यर्थ है। उदाहरण के लिए, यदि p एक संसाधन के लिए इंगित करने वाला एक सदस्य चर था, जिसका जीवनकाल p युक्त वर्ग के समान नहीं था, तो N को p सेट करना संसाधन की उपस्थिति या अनुपस्थिति को इंगित करने का एक उपयोगी तरीका हो सकता है।


1
मैं मानता हूं कि ऐसे समय होते हैं जब यह मदद नहीं करेगा, लेकिन आपको लग रहा था कि यह सक्रिय रूप से हानिकारक हो सकता है। क्या वह आपकी मंशा थी, या मैंने इसे गलत पढ़ा?
मार्क रैनसम

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

2
@ लोगों के बहुत सारे लोग आपसे असहमत लगते हैं। और क्या कोई प्रतिलिपि निश्चित रूप से प्रासंगिक है यदि आप मूल को हटाने के बाद प्रतिलिपि का उपयोग करने का प्रयास करते हैं।

3
Franci, वहाँ एक अंतर है। आप व्यंजन साफ ​​करते हैं क्योंकि आप उन्हें फिर से उपयोग करते हैं। इसे हटाने के बाद आपको सूचक की आवश्यकता नहीं है। यह आखिरी चीज होनी चाहिए जो आप करते हैं। पूरी तरह से स्थिति से बचने के लिए बेहतर अभ्यास है।
GMANNICKG

1
आप एक चर का फिर से उपयोग कर सकते हैं, लेकिन फिर यह रक्षात्मक प्रोग्रामिंग का मामला नहीं है; यह है कि आपने हाथ में समस्या का समाधान कैसे बनाया है। ओपी चर्चा कर रहा है कि क्या यह रक्षात्मक शैली ऐसी चीज है जिसके लिए हमें प्रयास करना चाहिए, न कि हम कभी भी एक संकेतक को शून्य करने के लिए सेट करेंगे। और आदर्श रूप से, आपके प्रश्न के लिए, हाँ! आप उन्हें हटाने के बाद संकेत का उपयोग न करें!
GManNickG

7

यदि के बाद अधिक कोड है delete, हाँ। जब पॉइंटर को एक कंस्ट्रक्टर में या विधि या फ़ंक्शन के अंत में हटा दिया जाता है, नहीं।

इस दृष्टांत का उद्देश्य रन-टाइम के दौरान प्रोग्रामर को याद दिलाना है कि ऑब्जेक्ट पहले ही हटा दिया गया है।

एक और बेहतर अभ्यास स्मार्ट पॉइंटर्स (साझा या स्कोप) का उपयोग करना है जो स्वचालित रूप से अपने लक्ष्य वस्तुओं को हटाते हैं।


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

3

जैसा कि दूसरों ने कहा है, delete ptr; ptr = 0;राक्षसों को आपकी नाक से बाहर निकलने का कारण नहीं बनना है। हालाँकि, यह ptrध्वज के रूप में उपयोग को प्रोत्साहित करता है । कोड deleteको पॉइंटर करने और पॉइंटर को सेट करने के लिए लेट हो जाता है NULL। अगला कदम पॉइंटर if (arg == NULL) return;के आकस्मिक उपयोग से बचाने के लिए आपके कोड के माध्यम से तितर बितर करना है NULL। समस्या तब होती है जब NULLएक वस्तु या कार्यक्रम की स्थिति के लिए चेक आपके प्राथमिक साधन बन जाते हैं।

मुझे यकीन है कि एक झंडे के रूप में एक पॉइंटर का उपयोग करने के बारे में एक कोड गंध है, लेकिन मुझे एक नहीं मिला है।


9
एक ध्वज के रूप में एक सूचक का उपयोग करने में कुछ भी गलत नहीं है। यदि आप एक पॉइंटर का उपयोग कर रहे हैं, और NULLएक वैध मूल्य नहीं है, तो आपको संभवतः एक संदर्भ का उपयोग करना चाहिए।
एड्रियन मैक्कार्थी

2

मैं आपके प्रश्न को थोड़ा बदल दूंगा:

क्या आप एक असिंचित सूचक का उपयोग करेंगे? आप जानते हैं, एक जिसे आपने NULL को सेट नहीं किया था या वह जिस मेमोरी को इंगित करता है उसे आवंटित करने के लिए?

वहाँ दो परिदृश्य हैं जहाँ सूचक को NULL में सेट किया जा सकता है:

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

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


(ए) "यह तर्क देने जैसा लगता है कि आपको बग को ठीक नहीं करना चाहिए" NULL को पॉइंटर सेट करना बग नहीं है। (बी) "लेकिन इसे NULL में सेट करना वास्तव में उसी बग का कारण होगा" नंबर को NULL में सेट करना डबल डिलीट करता है । (C) सारांश: NULL के लिए सेटिंग डबल डिलीट को छुपाता है, लेकिन बासी संदर्भों को उजागर करता है। NULL के लिए सेटिंग बासी संदर्भों को छिपा नहीं सकती है, लेकिन डबल डिलीट को उजागर करती है। दोनों पक्ष सहमत हैं कि वास्तविक समस्या बासी-संदर्भ और डबल-डिलीट को ठीक करना है।
मूंग बतख

2

यदि आपके पास कोई अन्य बाधा नहीं है जो आपको इसे हटाने के बाद NULL को पॉइंटर सेट करने या न करने के लिए मजबूर करती है (ऐसा एक बाधा नील बटरवर्थ द्वारा उल्लेख किया गया था ), तो मेरी व्यक्तिगत प्राथमिकता इसे छोड़ना है।

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

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


2

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

यदि आप वास्तव में इसका मतलब यह है, यह स्रोत में स्पष्ट करने के लिए बेहतर है कि बढ़ावा :: वैकल्पिक जैसे कुछ का उपयोग कर

optional<Foo*> p (new Foo);
// (use p.get(), but must test p for truth first!...)
delete p.get();
p = optional<Foo*>();

लेकिन अगर आप वास्तव में लोगों को जानना चाहते हैं कि सूचक "खराब हो गया है", तो मैं 100% समझौते में उन लोगों के साथ पिच करूंगा, जो सबसे अच्छी बात यह कहते हैं कि यह दायरे से बाहर हो गया है। फिर आप संकलक का उपयोग कर रहे हैं रनटाइम पर खराब dereferences की संभावना को रोकने के लिए।

वह सभी सी ++ स्नानागार में बच्चा है, उसे बाहर नहीं फेंकना चाहिए। :)


2

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

असाइन करने के खिलाफ कई तर्क 0देते हैं कि यह बग को छिपा सकता है या नियंत्रण प्रवाह को जटिल कर सकता है। मौलिक रूप से, यह या तो एक अपस्ट्रीम एरर है (न कि आपकी गलती (बुरी सजा के लिए खेद है) या प्रोग्रामर की ओर से कोई अन्य त्रुटि - शायद यह भी एक संकेत है कि प्रोग्राम फ्लो बहुत जटिल हो गया है।

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

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

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

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

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


1

जो आपने पहले ही अपने प्रश्न में डाल दिया है, मुझे उसका विस्तार करने दें।

आपके द्वारा अपने प्रश्न को बुलेट-पॉइंट फॉर्म में रखा गया है:


NULL को डिलीट करने के लिए पॉइंटर्स सेट करना C ++ में सार्वभौमिक अच्छा अभ्यास नहीं है। ऐसे समय होते हैं जब:

  • यह एक अच्छी बात है
  • और बार जब यह व्यर्थ है और त्रुटियों को छिपा सकता है।

हालाँकि, ऐसा कोई समय नहीं है जब यह खराब हो ! आप इसे स्पष्ट रूप से शून्य करने से अधिक बग का परिचय नहीं देंगे , आप मेमोरी को लीक नहीं करेंगे , आप अपरिभाषित व्यवहार का कारण नहीं बनेंगे

इसलिए, यदि संदेह है, तो इसे शून्य करें।

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


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

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

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

@ मायकेएम मैं पूरी तरह से सहमत हूं, इस बारे में मेरे विचार पिछले ~ 6.5 वर्षों में काफी हद तक बदल गए हैं
कार्सन मायर्स

एक प्रोग्रामर होने के संदर्भ में, हम सभी 6-7 साल पहले किसी और के थे :) मुझे यकीन भी नहीं है कि मैं आज C / C ++ प्रश्न का उत्तर देने की हिम्मत कर रहा हूं :)
लास वी। कार्लसन

1

हाँ।

एकमात्र "नुकसान" यह कर सकता है कि आपके कार्यक्रम में अक्षमता (एक अनावश्यक स्टोर ऑपरेशन) शुरू करना है - लेकिन यह ओवरहेड ज्यादातर मामलों में स्मृति के ब्लॉक को आवंटित करने और मुक्त करने की लागत के संबंध में नगण्य होगा।

आप यह मत करो, तो आप होगा कुछ बुरा सूचक derefernce कीड़े एक दिन की है।

मैं हमेशा हटाने के लिए मैक्रो का उपयोग करता हूं:

#define SAFEDELETE(ptr) { delete(ptr); ptr = NULL; }

(और एक सरणी के लिए समान, मुफ्त (), हैंडल जारी करना)

आप "सेल्फ डिलीट" तरीके भी लिख सकते हैं जो कॉलिंग कोड के पॉइंटर का संदर्भ लेते हैं, इसलिए वे कॉल कोड के पॉइंटर को NULL को बाध्य करते हैं। उदाहरण के लिए, कई वस्तुओं का उप योग हटाने के लिए:

static void TreeItem::DeleteSubtree(TreeItem *&rootObject)
{
    if (rootObject == NULL)
        return;

    rootObject->UnlinkFromParent();

    for (int i = 0; i < numChildren)
       DeleteSubtree(rootObject->child[i]);

    delete rootObject;
    rootObject = NULL;
}

संपादित करें

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

ऐसे कई तरीके भी हैं जिन्हें आप ऊपर लागू कर सकते हैं - मैं सिर्फ लोगों को एक सूचक को हटाने के लिए मजबूर करने के विचार को चित्रित करने की कोशिश कर रहा हूं, यदि वे किसी ऑब्जेक्ट को हटाते हैं, बल्कि उनके लिए एक साधन प्रदान करने की बजाय मेमोरी को खाली करने के लिए नहीं करते हैं जो कॉल करने वाले के सूचक को पूरा नहीं करता है ।

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


2
मैक्रों एक बुरा विचार हैं, खासकर जब वे सामान्य कार्यों की तरह दिखते हैं। यदि आप ऐसा करना चाहते हैं, तो एक टेम्प्लेटेड फ़ंक्शन का उपयोग करें।

2
वाह ... मैंने कभी भी सूचक को anObject->Delete(anObject)अमान्य करने जैसा कुछ नहीं देखा है anObject। यह सिर्फ भयावह है। आपको इसके लिए एक स्टैटिक तरीका तैयार करना चाहिए ताकि आप TreeItem::Delete(anObject)बहुत कम से कम करने को मजबूर हों ।
डी। शॉवले १

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

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

1

"ऐसे समय होते हैं जब यह करना अच्छी बात होती है, और ऐसे समय जब यह व्यर्थ होता है और त्रुटियों को छिपा सकता है"

मैं दो समस्याएं देख सकता हूं: यह सरल कोड:

delete myObj;
myobj = 0

बहु-वातावरण में फॉर-लाइनर बन जाता है:

lock(myObjMutex); 
delete myObj;
myobj = 0
unlock(myObjMutex);

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

अपवाद-उपयोग कोड में इस तकनीक पर एक और खतरा निर्भर करता है:

try{  
   delete myObj; //exception in destructor
   myObj=0
}
catch
{
   //myObj=0; <- possibly resource-leak
}

if (myObj)
  // use myObj <--undefined behaviour

ऐसे कोड में या तो आप संसाधन-रिसाव उत्पन्न करते हैं और समस्या को स्थगित कर देते हैं या प्रक्रिया क्रैश हो जाती है।

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


मैं यह देखने में विफल रहता हूं कि 4-लाइनर 3-लाइनर की तुलना में काफी अधिक जटिल है (किसी को वैसे भी lock_guards का उपयोग करना चाहिए) और यदि आपका विध्वंसक फेंकता है तो आप किसी भी तरह से परेशानी में हैं।
माइकएम

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

0

चिंता करने के लिए हमेशा डैंगलिंग पॉइंटर्स होते हैं।


1
क्या "इस" के बजाय "डैंगलिंग पॉइंटर्स" डालना इतना कठिन होगा? :) कम से कम यह आपके उत्तर को कुछ पदार्थ देता है।
GManNickG

4
यह W3C QA टिप का विषय भी है, "लिंक टेक्स्ट के रूप में 'यहां क्लिक करें' का उपयोग न करें: w3.org/QA/Tips/noClickHere
outis

0

यदि आप पुन: उपयोग करने से पहले पॉइंटर को फिर से चालू करने जा रहे हैं (इसे डीरेफरेंस कर रहे हैं, इसे किसी फंक्शन में पास करना आदि), तो पॉइंटर NULL बनाना केवल एक अतिरिक्त ऑपरेशन है। हालाँकि, यदि आप सुनिश्चित नहीं हैं कि यह फिर से उपयोग किए जाने से पहले इसे दोबारा प्राप्त किया जाएगा या नहीं, तो इसे NULL पर सेट करना एक अच्छा विचार है।

जैसा कि कई लोगों ने कहा है, बेशक स्मार्ट पॉइंटर्स का उपयोग करना बहुत आसान है।

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


0

मैं NULL के लिए एक पॉइंटर सेट करने की कल्पना कर सकता हूं इसे हटाने के बाद यह दुर्लभ मामलों में उपयोगी है जहां एक एकल फ़ंक्शन (या ऑब्जेक्ट) में इसका पुन: उपयोग करने का एक वैध परिदृश्य है। अन्यथा इसका कोई मतलब नहीं है - एक पॉइंटर को किसी सार्थक वस्तु को इंगित करने की आवश्यकता है जब तक कि वह मौजूद है - अवधि।


0

यदि कोड आपके एप्लिकेशन के सबसे प्रदर्शन-महत्वपूर्ण भाग से संबंधित नहीं है, तो इसे सरल रखें और एक साझा करें_ का उपयोग करें:

shared_ptr<Foo> p(new Foo);
//No more need to call delete

यह संदर्भ गणना करता है और थ्रेड-सुरक्षित है। आप इसे tr1 (std :: tr1 namespace, #include <memory>) में पा सकते हैं या यदि आपका कंपाइलर इसे प्रदान नहीं करता है, तो इसे बूस्ट से प्राप्त करें।

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