क्या C ++ में 'नया' और 'डिलीट' हो रहे हैं?


68

मैंने एक प्रश्नोत्तरी पर ठोकर खाई जिसमें विभिन्न आकारों के साथ सरणी घोषणा शामिल थी। पहली बात जो मेरे दिमाग में आई वह यह है कि मुझे newइस तरह से कमांड के साथ डायनेमिक एलोकेशन का उपयोग करना होगा :

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

हालाँकि, मैंने देखा कि समाधान में से एक ने निम्नलिखित मामले की अनुमति दी:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

थोड़ी खोजबीन के बाद मैंने पढ़ा कि g ++ इसे अनुमति देता है, लेकिन इसने मुझे यह सोच कर रखा है कि किन मामलों में गतिशील आवंटन का उपयोग करना आवश्यक है? या यह है कि संकलक इसे गतिशील आवंटन के रूप में अनुवाद करता है।

डिलीट फंक्शन शामिल है। हालाँकि, ध्यान दें कि यहाँ प्रश्न मेमोरी लीक के बारे में नहीं है।


54
दूसरा उदाहरण एक चर-लंबाई सरणी का उपयोग करता है जो कभी भी C ++ का हिस्सा नहीं रहा है। इस मामले के लिए std::vectorइसके बजाय ( std::vector<int> array(N);) का उपयोग करें ।
कुछ प्रोग्रामर

7
आपके प्रश्न का सीधा उत्तर यह होना चाहिए: नहीं, यह पदावनत नहीं हो रहा है। भले ही C ++ के आधुनिक संस्करण स्मृति स्वामित्व प्रबंधन (स्मार्ट पॉइंटर्स) को सरल बनाने के लिए कई सुविधाएँ प्रदान करते हैं, फिर भी वस्तुओं को new OBJसीधे आवंटित करना अभी भी आम बात है ।
पपत्सनी

8
अन्य लोगों के बारे में जो भ्रम में हैं कि लोग मेमोरी लीक के बारे में क्यों बात कर रहे हैं, सवाल को बग को ठीक करने के लिए संपादित किया गया था जो इस प्रश्न के लिए उपयोगी नहीं था
माइक कैरन

4
@ मनोज डायनेमिक और ऑटोमैटिक का उपयोग ढेर और स्टैक के लिए करना पसंद करते हैं। यह दुर्लभ है लेकिन ढेर और ढेर के बिना सी ++ को लागू करना संभव है।
user4581301

1
C ++ में कभी भी कुछ नहीं निकाला गया है और कुछ भी कभी नहीं होगा। इसका मतलब है कि C ++ का क्या हिस्सा है।
जोएलफैन

जवाबों:


114

न तो आप जो स्निपेट दिखाते हैं वह मुहावरेदार है, आधुनिक सी ++ कोड।

newऔर delete(और new[]और delete[]) सी ++ में पदावनत नहीं कर रहे हैं और कभी नहीं होगा। वे अभी भी गतिशील रूप से आवंटित वस्तुओं को त्वरित करने का तरीका हैं। हालाँकि, जैसा कि आपको हमेशा newएक delete(और एक के new[]साथ delete[]) के साथ मेल खाना होता है , उन्हें (पुस्तकालय) कक्षाओं में सबसे अच्छा रखा जाता है जो आपके लिए यह सुनिश्चित करते हैं। देखें कि C ++ प्रोग्रामर को 'नया' का उपयोग क्यों कम करना चाहिए?

आपका पहला स्निपेट एक "नग्न" का उपयोग करता है new[]और फिर delete[]निर्मित सरणी कभी नहीं होता है । ये एक समस्या है। std::vectorसब कुछ आप की जरूरत है यहाँ ठीक है। यह newपर्दे के पीछे के कुछ रूप का उपयोग करेगा (मैं कार्यान्वयन विवरण में डुबकी नहीं लगाऊंगा), लेकिन आप सभी को ध्यान रखना है, यह एक गतिशील सरणी है, लेकिन बेहतर और सुरक्षित है।

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


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

4
यह भी याद रखें कि आप किसी ऐरे को वापस नहीं ले सकते हैं या इसे कहीं और स्टोर नहीं कर सकते हैं, इसलिए वीएलए कभी भी फ़ंक्शन के निष्पादन समय को रेखांकित नहीं करेगा
रुस्लान

16
@StackTracer मेरे ज्ञान का सबसे अच्छा करने के लिए, MSVC VLAs का समर्थन नहीं करता है। और MSVC निश्चित रूप से एक "प्रमुख संकलक" है।
मैक्स लैंगहोफ

2
"आपको हमेशा एक डिलीट के साथ एक नया मिलान करना पड़ता है" - यदि आप साथ काम करते हैं Qt, तो नहीं , क्योंकि इसके बेस क्लासेस में सभी कूड़ा उठाने वाले होते हैं, इसलिए आप इसका उपयोग करते हैं newऔर इसे भूल जाते हैं, ज्यादातर समय। जीयूआई तत्वों के लिए, जब माता-पिता विजेट बंद हो जाते हैं, तो बच्चे दायरे से बाहर हो जाते हैं और कचरा स्वचालित रूप से एकत्र किया जाता है।
vsz

6
@vsz Qt में newभी , प्रत्येक में अभी भी मेल है delete; यह सिर्फ इतना है कि deletes मूल कोड के बजाय पैरेंट विजेट द्वारा किया जाता है new
जजरामी

22

खैर, शुरुआत के लिए, new/ deleteपदावनत नहीं हो रहे हैं।

आपके विशिष्ट मामले में, वे एकमात्र समाधान नहीं हैं, हालांकि। आप जो चुनते हैं वह इस बात पर निर्भर करता है कि आपके "सरणी के साथ कुछ करें" टिप्पणी के तहत क्या छिपा हुआ है।

आपका दूसरा उदाहरण एक गैर-मानक वीएलए विस्तार का उपयोग करता है जो स्टैक पर सरणी को फिट करने की कोशिश करता है। इसकी कुछ सीमाएँ हैं - अर्थात् सीमित आकार और सरणी के दायरे से बाहर जाने के बाद इस मेमोरी का उपयोग करने में असमर्थता। आप इसे हटा नहीं सकते, यह स्टैक अनइंड्स के बाद "गायब" हो जाएगा।

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

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


1
ध्यान दें "... कुछ परतें गहरी"। यदि आप अपने स्वयं के कंटेनरों को लागू करने के लिए थे, तो आपको अभी भी उपयोग करने से बचना चाहिए newऔर delete, बल्कि स्मार्ट पॉइंटर्स का उपयोग करना चाहिए std::unique_pointer
अधिकतम

1
जिसे वास्तव में कहा जाता हैstd::unique_ptr
user253751

2
@ मोम: std::unique_ptrडिफ़ॉल्ट डिस्ट्रक्टर कॉल deleteया delete[], जिसका अर्थ है कि स्वामित्व वाली वस्तु को आवंटित किया जाना चाहिए newया new[]वैसे भी, जो कॉल std::make_uniqueC ++ 14 के बाद से छिपाए गए हैं ।
लॉरेंट ला रिज़्ज़ा

15

आपके दूसरे उदाहरण चर लंबाई सरणियों (वीएलए) का उपयोग करते हैं, जो वास्तव में एक सी 99 ( सी ++ नहीं !) सुविधा है, लेकिन फिर भी जी ++ द्वारा समर्थित है ।

इस उत्तर को भी देखें ।

ध्यान दें कि चर लंबाई सरणियाँ new/ से भिन्न होती हैं deleteऔर उन्हें किसी भी तरह से "अपवित्र" नहीं करती हैं।

यह भी ध्यान रखें कि वीएलएएएस आईएसओ सी ++ नहीं हैं ।


13

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

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

C ++ 14 से आप भी लिख सकते हैं

auto buffer_new = std::make_unique<int[]>(size);

यह भी अच्छा लग रहा है और आवंटन में विफल होने पर स्मृति रिसाव को रोक देगा। C ++ 20 से आप जितना कर सकते हैं करने में सक्षम होना चाहिए

auto a = std::make_shared<int[]>(size);

मेरे लिए यह अभी भी gcc 7.4.0 के साथ लिखने के समय संकलित नहीं है। इन दो उदाहरणों में हम autoबाईं ओर प्रकार की घोषणा के बजाय भी उपयोग करते हैं। सभी मामलों में, हमेशा की तरह सरणी का उपयोग करें:

buffer_old[0] = buffer_new[0] = 17;

स्मृति लीक से newऔर दुर्घटनाओं से दोगुनी हो deleteजाती है कुछ सी ++ कई वर्षों के लिए धराशायी हो गई है, अन्य भाषाओं में स्विच करने के लिए तर्क का "केंद्रीय बिंदु" है। शायद बचना बेहतर है।


आपको इसके unique/shared_ptrपक्ष में कंस्ट्रक्टर्स से बचना चाहिए make_unique/shared, न केवल आपको दो बार (टाइप auto) का निर्माण प्रकार लिखना नहीं है ( लेकिन) आप मेमोरी या संसाधनों को लीक करने का जोखिम नहीं उठाते हैं यदि निर्माण विफल रहता है (यदि आप एक प्रकार का उपयोग कर रहे हैं जो विफल हो सकता है)
साइमन बुकान 3

2
make_unique C ++ 14 से सरणियों के साथ उपलब्ध है, और make_sared C ++ 20 से ही। यह अब भी शायद ही कभी एक डिफ़ॉल्ट सेटिंग है, इसलिए std का प्रस्ताव :: make_sared <int []> (आकार) मेरे लिए कुछ समय से पहले देखा गया था।
ऑड्रियस मेसकॉस्कस

काफी उचित! मैं वास्तव में बहुत उपयोग make_shared<int[]>नहीं करता, जब आप बहुत हमेशा चाहते हैं vector<int>, लेकिन अच्छा है।
साइमन बुकान

अत्यधिक पांडित्य, लेकिन IIRC unique_ptrनिर्माता nothrow है, इसलिए के लिए Tकि कंस्ट्रक्टर्स nothrow है, तो वहाँ के साथ लीक का कोई खतरा नहीं है unique_ptr(new int[size])और shared_ptrहै निम्नलिखित: "एक अपवाद फेंक दिया जाता है, तो हटाएं पी जब टी एक सरणी प्रकार नहीं है कहा जाता है, नष्ट [ ] पी अन्यथा। ", तो आप एक ही प्रभाव है - जोखिम के लिए है unique/shared_ptr(new MyPossiblyAllocatingType[size])
साइमन बुकान

3

नए और हटाए गए पदावनत नहीं हो रहे हैं।

नए ऑपरेटर द्वारा बनाई गई वस्तुओं को संदर्भ द्वारा पारित किया जा सकता है। ऑब्जेक्ट्स को डिलीट का उपयोग करके हटाया जा सकता है।

नया और हटाना भाषा के मूलभूत पहलू हैं। किसी वस्तु की दृढ़ता को नए और हटाए जाने का उपयोग करके प्रबंधित किया जा सकता है। ये निश्चित रूप से पदावनत होने वाले नहीं हैं।

कथन - int array [N] एक एरे को परिभाषित करने का एक तरीका है। एंकल को एन्कोडिंग कोड ब्लॉक के दायरे में इस्तेमाल किया जा सकता है। यह पारित नहीं किया जा सकता है कि किसी वस्तु को दूसरे फ़ंक्शन में कैसे पारित किया जाता है।


2

पहले उदाहरण के delete[]लिए अंत में जरूरत है , या आपके पास मेमोरी लीक होगी।

दूसरा उदाहरण चर सरणी लंबाई का उपयोग करता है जो C ++ द्वारा समर्थित नहीं है; यह केवल सरणी लंबाई के लिए निरंतर अभिव्यक्ति की अनुमति देता है

इस मामले std::vector<>में समाधान के रूप में उपयोग करना उपयोगी है ; एक सरणी पर एक टेम्पलेट कक्षा में आप कर सकते हैं सभी कार्यों को लपेटता है।


3
"सी ++ 11" के साथ आपका क्या मतलब है? मुझे पूरा यकीन है, VLAs कभी भी मानक का हिस्सा नहीं बने।
चर्चिल

c ++ 14 [c ++ 14 मानक] (के मानक पर नज़र isocpp.org/files/papers/N3690.pdf पर पेज 184 पैरा 8.3.4)
टेढ़े उस्तरा

4
यह मानक नहीं है , लेकिन केवल एक ड्राफ्ट और "रनटाइम बाउंड की सरणियों" के बारे में भाग ने इसे मानक में नहीं बनाया है जहां तक ​​मैं बता सकता हूं। cppreference में वीएलए के बारे में उल्लेख नहीं किया गया है।
चर्चिल

1
@zigrazor cppreference.com में मानकों में से प्रत्येक के प्रकाशन से पहले / बाद में निकटतम ड्राफ्ट के लिंक की एक सूची है। प्रकाशित मानक स्वतंत्र रूप से उपलब्ध नहीं हैं, लेकिन ये ड्राफ्ट बहुत करीब होने चाहिए। जैसा कि आप दस्तावेज़ संख्याओं से देख सकते हैं, आपका लिंक किया गया ड्राफ्ट C ++ 14 के लिए कुछ पुराने वर्किंग ड्राफ्ट हैं।
अखरोट

2
@learning_dude यह मानकों द्वारा समर्थित नहीं है । इसका उत्तर (अब) सही है (यद्यपि संक्षिप्त)। यह केवल आपके लिए काम करता है क्योंकि जीसीसी इसे गैर-मानक विस्तार के रूप में अनुमति देता है ।
अखरोट

-4

वाक्य रचना C ++ की तरह दिखता है, लेकिन मुहावरा सादे पुराने Algol60 के समान है। इस तरह कोड ब्लॉक होना आम बात थी:

read n;
begin
    integer array x[1:n];
    ... 
end;

उदाहरण के रूप में लिखा जा सकता है:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

मुझे कभी-कभी वर्तमान भाषाओं में यह याद आती है;)

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