मुझे C++ में नए कीवर्ड का उपयोग कब करना चाहिए?




pointers reference (8)

मैं थोड़ी देर के लिए सी ++ का उपयोग कर रहा हूं, और मैं नए कीवर्ड के बारे में सोच रहा हूं। बस, क्या मुझे इसका इस्तेमाल करना चाहिए, या नहीं?

1) नए कीवर्ड के साथ ...

MyClass* myClass = new MyClass();
myClass->MyField = "Hello world!";

2) नए कीवर्ड के बिना ...

MyClass myClass;
myClass.MyField = "Hello world!";

एक कार्यान्वयन परिप्रेक्ष्य से, वे अलग नहीं लगते हैं (लेकिन मुझे यकीन है कि वे हैं) ... हालांकि, मेरी प्राथमिक भाषा सी # है, और निश्चित रूप से पहली विधि है जिसका उपयोग मैं करता हूं।

कठिनाई यह प्रतीत होती है कि विधि 1 std C ++ कक्षाओं के साथ उपयोग करना कठिन है।

मुझे किस विधि का उपयोग करना चाहिए?

अद्यतन 1:

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

अद्यतन 2:

मेरे एक दोस्त ने हाल ही में मुझे बताया कि new कीवर्ड का उपयोग करने के लिए एक सरल नियम है; हर बार जब आप new टाइप new , तो delete टाइप करें।

Foobar *foobar = new Foobar();
delete foobar; // TODO: Move this to the right place.

यह स्मृति रिसाव को रोकने में मदद करता है, क्योंकि आपको हमेशा कहीं हटाना पड़ता है (यानी जब आप इसे किसी विनाशक या अन्यथा चिपकाते हैं)।


मुझे किस विधि का उपयोग करना चाहिए?

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

फ्री-स्टोर प्रबंधन का उपयोग करने के बोझ को कम करने के लिए लोगों ने auto_ptr और unique_ptr जैसे सामानों का आविष्कार किया है। मैं दृढ़ता से अनुशंसा करता हूं कि आप इन पर नज़र डालें। वे आपके टाइपिंग मुद्दों में भी मदद कर सकते हैं ;-)


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


दोनों के बीच एक महत्वपूर्ण अंतर है।

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

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

(और वह वहीं है जहां सी # स्टॉप की कोई समानता है)

अब, स्टैक पर आवंटित कुछ भी "स्वचालित" भंडारण अवधि है (आप वास्तव में एक वैरिएबल को auto रूप में घोषित कर सकते हैं, लेकिन यह डिफ़ॉल्ट है यदि कोई अन्य संग्रहण प्रकार निर्दिष्ट नहीं है, तो कीवर्ड वास्तव में अभ्यास में उपयोग नहीं किया जाता है, लेकिन यह वह जगह है यह इससे आता है)

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

void foo() {
  bar b;
  bar* b2 = new bar();
}

यह फ़ंक्शन विचार करने के लायक तीन मान बनाता है:

लाइन 1 पर, यह स्टैक (स्वचालित अवधि) पर एक प्रकार की प्रकार bar घोषित करता है।

लाइन 2 पर, यह स्टैक (स्वचालित अवधि) पर एक bar पॉइंटर b2 घोषित करता है, और ढेर पर एक bar ऑब्जेक्ट आवंटित करने के लिए नया कॉल करता है। (गतिशील अवधि)

जब समारोह वापस आता है, तो निम्नलिखित होगा: सबसे पहले, b2 गुंजाइश से बाहर हो जाता है (विनाश का आदेश हमेशा निर्माण के क्रम के विपरीत होता है)। लेकिन b2 सिर्फ एक सूचक है, इसलिए कुछ नहीं होता है, जो स्मृति वह कब्जा कर लेती है वह बस मुक्त होती है। और महत्वपूर्ण बात यह है कि यह स्मृति जो इंगित करती है (ढेर पर bar उदाहरण) स्पर्श नहीं होता है। केवल सूचक को मुक्त किया जाता है, क्योंकि केवल सूचक के पास स्वचालित अवधि होती है। दूसरा, b गुंजाइश से बाहर चला जाता है, इसलिए इसकी अवधि स्वचालित है, इसके विनाशक को बुलाया जाता है, और स्मृति मुक्त हो जाती है।

और ढेर पर bar उदाहरण? यह शायद अभी भी वहाँ है। इसे हटाने के लिए कोई भी परेशान नहीं है, इसलिए हमने स्मृति लीक की है।

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

और यह काफी सटीक है कि अधिकांश सी ++ कोड कैसे काम करता है। उदाहरण के लिए मानक लाइब्रेरी के std::vector को देखें। यह आमतौर पर ढेर पर आवंटित किया जाता है, लेकिन गतिशील रूप से आकार और आकार बदल सकता है। और यह आवश्यक रूप से ढेर पर आंतरिक रूप से स्मृति आवंटित करके करता है। कक्षा का उपयोगकर्ता इसे कभी नहीं देखता है, इसलिए स्मृति को लीक करने का कोई मौका नहीं है, या जो भी आपने आवंटित किया है उसे साफ़ करना भूल गया है।

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

एक सामान्य नियम के रूप में, अपने उच्च स्तर कोड से सीधे नए / हटाएं का उपयोग न करें। हमेशा इसे उस कक्षा में लपेटें जो आपके लिए स्मृति का प्रबंधन कर सके, और यह सुनिश्चित करेगा कि यह फिर से मुक्त हो जाए। (हां, इस नियम के अपवाद हो सकते हैं। विशेष रूप से, स्मार्ट पॉइंटर्स के लिए आपको सीधे new कॉल करने की आवश्यकता होती है, और पॉइंटर को अपने कन्स्ट्रक्टर को पास करने की आवश्यकता होती है, जो तब लेता है और सुनिश्चित करता है कि delete सही ढंग से कहा जाता है। लेकिन यह अभी भी एक बहुत ही महत्वपूर्ण नियम है अंगूठे का)


यदि आप सी ++ में लिख रहे हैं तो आप शायद प्रदर्शन के लिए लिख रहे हैं। नए और नि: शुल्क स्टोर का उपयोग करना स्टैक (विशेष रूप से धागे का उपयोग करते समय) का उपयोग करने से बहुत धीमा है, इसलिए इसकी आवश्यकता होने पर ही इसका उपयोग करें।

जैसा कि अन्य ने कहा है, आपको नई वस्तु की आवश्यकता होती है जब आपकी ऑब्जेक्ट को फ़ंक्शन या ऑब्जेक्ट स्कोप के बाहर रहने की आवश्यकता होती है, ऑब्जेक्ट वास्तव में बड़ा होता है या जब आप संकलन समय पर किसी सरणी के आकार को नहीं जानते हैं।

साथ ही, हटाने का उपयोग करने से बचने का प्रयास करें। इसके बजाय अपने स्मार्ट को एक पॉइंटर में लपेटें। स्मार्ट पॉइंटर कॉल आपके लिए हटा दें।

ऐसे कुछ मामले हैं जहां एक स्मार्ट सूचक स्मार्ट नहीं है। एसटीएल कंटेनर के अंदर std :: auto_ptr <> को कभी भी स्टोर न करें। कंटेनर के अंदर कॉपी ऑपरेशन की वजह से यह सूचक जल्द ही हटा देगा। एक और मामला तब होता है जब आपके पास ऑब्जेक्ट्स के पॉइंटर्स का वास्तव में बड़ा एसटीएल कंटेनर होता है। boost :: shared_ptr <> में गति ओवरहेड का एक टन होगा क्योंकि यह संदर्भ संख्याओं को ऊपर और नीचे दबाता है। उस मामले में जाने का बेहतर तरीका एसटीएल कंटेनर को किसी अन्य ऑब्जेक्ट में रखना है और उस ऑब्जेक्ट को एक विनाशक देना है जो कंटेनर में प्रत्येक पॉइंटर पर डिलीट करेगा।


संक्षिप्त जवाब यह है कि "नया" कीवर्ड अविश्वसनीय रूप से महत्वपूर्ण है जब आप इसका उपयोग करते हैं तो ऑब्जेक्ट डेटा ढेर के रूप में ढेर पर संग्रहीत होता है, जो सबसे महत्वपूर्ण है!


संक्षिप्त जवाब यह है: यदि आप सी ++ में शुरुआत कर रहे हैं, तो आपको कभी भी new का उपयोग नहीं करना चाहिए या खुद को delete देना चाहिए। इसके बजाए, आपको स्मार्ट पॉइंटर्स जैसे std::unique_ptr (या कम अक्सर, std::shared_ptr ) का उपयोग करना चाहिए। इस तरह, आपको मेमोरी लीक के बारे में ज्यादा चिंता करने की ज़रूरत नहीं है। और यहां तक ​​कि यदि आप अधिक उन्नत होते हैं, तो सबसे अच्छा अभ्यास आमतौर पर आप जिस new तरीके से उपयोग कर रहे हैं उसे अनुकूलित कर सकते हैं और एक छोटी कक्षा (जैसे एक कस्टम स्मार्ट पॉइंटर) में delete सकते हैं जो जीवन चक्र के मुद्दों को ऑब्जेक्ट करने के लिए समर्पित है।

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



विधि 1 ( new उपयोग करके)

  • ऑब्जेक्ट के लिए फ्री स्टोर पर मेमोरी आवंटित करता है (यह अक्सर ढेर के समान ही होता है)
  • आपको बाद में अपनी ऑब्जेक्ट को स्पष्ट रूप से delete आवश्यकता है। (यदि आप इसे हटा नहीं देते हैं, तो आप मेमोरी रिसाव बना सकते हैं)
  • जब तक आप इसे delete नहीं देते तब तक स्मृति आवंटित रहता है। (यानी आप एक ऑब्जेक्ट return कर सकते return जिसे आपने new का उपयोग करके बनाया है)
  • प्रश्न में उदाहरण स्मृति को रिसाव करेगा जब तक कि सूचक delete जाता है; और इसे हमेशा हटाया जाना चाहिए , भले ही कौन सा नियंत्रण पथ लिया जाता है, या अपवाद फेंक दिया जाता है।

विधि 2 ( new उपयोग नहीं कर रहा है)

  • स्टैक पर ऑब्जेक्ट के लिए मेमोरी आवंटित करता है (जहां सभी स्थानीय चर जाते हैं) आमतौर पर ढेर के लिए कम स्मृति उपलब्ध होती है; यदि आप बहुत सारी वस्तुओं को आवंटित करते हैं, तो आप स्टैक ओवरफ़्लो को जोखिम देते हैं।
  • आपको बाद में इसे delete आवश्यकता नहीं होगी।
  • स्कोप से बाहर होने पर स्मृति अब आवंटित नहीं की जाती है। (यानी आपको स्टैक पर ऑब्जेक्ट में पॉइंटर return नहीं करना चाहिए)

जहां तक ​​उपयोग करने के लिए एक; आप उपरोक्त बाधाओं को देखते हुए, उस विधि को चुनते हैं जो आपके लिए सबसे अच्छा काम करता है।

कुछ आसान मामले:

  • यदि आप delete करने के बारे में चिंता नहीं करना चाहते हैं, (और मेमोरी लीक का कारण बनने की क्षमता) तो आपको new उपयोग नहीं करना चाहिए।
  • यदि आप किसी फ़ंक्शन से अपनी ऑब्जेक्ट में पॉइंटर वापस करना चाहते हैं, तो आपको new उपयोग करना होगा






keyword