C ++ में मेमोरी लीक से बचने के लिए सामान्य दिशानिर्देश [बंद]


130

यह सुनिश्चित करने के लिए कि मैं सी ++ कार्यक्रमों में मेमोरी लीक नहीं करता हूं, कुछ सामान्य युक्तियां क्या हैं? मैं यह कैसे पता लगाऊं कि गतिशील रूप से आवंटित की गई मेमोरी को कौन मुक्त करे?


26
मेरे लिए बहुत रचनात्मक लगता है।
श्योरोब

11
यह रचनात्मक है। और उत्तर तथ्यों, विशेषज्ञता, संदर्भों आदि द्वारा समर्थित होते हैं और उत्थान / उत्तर की संख्या देखते हैं .. !!
समिथा चतुरंगा

जवाबों:


40

मेमोरी को मैन्युअल रूप से प्रबंधित करने के बजाय, जहां लागू हो, स्मार्ट पॉइंटर्स का उपयोग करने का प्रयास करें। बूस्ट लीब , टीआर 1 , और स्मार्ट पॉइंटर्स
पर एक नज़र डालें । इसके अलावा स्मार्ट पॉइंटर्स अब C ++ मानक का एक हिस्सा हैं जिन्हें C ++ 11 कहा जाता है


1
G ++ का उपयोग करने के लिए एक परम जोड़ने की जरूरत है: -std = c ++ 0x
Paweł Szczur

या आप g ++ के साथ संकलित कर सकते हैं झंडा मूल्य -dd = c ++ 11 का उपयोग करके
प्रभाष राठौर

200

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

मत लिखो

Object* x = new Object;

या और भी

shared_ptr<Object> x(new Object);

जब आप बस लिख सकते हैं

Object x;

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

बहुत दिलचस्प बिंदु - मैंने सोचा था कि मेरे पास C ++ मेमोरी प्रबंधन के मुद्दे अन्य भाषाओं की तुलना में बहुत कम क्यों हैं, लेकिन अब मैं देखता हूं: यह वास्तव में स्टैक पर जाने की अनुमति देता है जैसे कि वेनिला सी।
आर्टऑफवर्फ

यदि आप ऑब्जेक्ट एक्स लिखते हैं तो आप क्या करते हैं; और फिर x को फेंकना चाहते हैं? x मुख्य विधि में बनाया गया था।
यमचा

3
@ user1316459 C ++ आपको मक्खी पर भी स्कोप बनाने की अनुमति देता है। आपको बस इतना करना है कि एक्स के जीवनकाल को ब्रेसिज़ के भीतर लपेटें जैसे: {ऑब्जेक्ट एक्स; x.DoSomething; }। अंतिम '}' के बाद, x के विनाशकर्ता को इसमें शामिल किसी भी संसाधन को मुक्त करना कहा जाएगा। यदि x, ही, हीप पर स्मृति को आबंटित किया जाना है, तो मैं इसे एक यूनिक_प्राट में लपेटने का सुझाव देता हूं ताकि इसे आसानी से और उचित रूप से साफ किया जा सके।
डेविड पीटरसन

1
रॉबर्ट: हाँ। रॉस ने यह नहीं कहा "कभी भी लिखो [कोड युक्त नया]", उन्होंने कहा कि "मत लिखो [कि] जब आप बस इसे स्टैक पर रख सकते हैं "। ढेर पर बड़ी वस्तुओं को ज्यादातर स्थितियों में सही कॉल जारी रहेगा, खासकर प्रदर्शन-गहन कोड के लिए।
कोडेटाकु

104

RAII का उपयोग करें

  • कचरा संग्रह को भूल जाओ (बजाय RAII का उपयोग करें)। ध्यान दें कि यहां तक ​​कि कचरा कलेक्टर भी रिसाव कर सकता है, भी (यदि आप जावा / सी # में कुछ संदर्भों को "शून्य" करना भूल जाते हैं), और यह कि कचरा कलेक्टर आपको संसाधनों का निपटान करने में मदद नहीं करेगा (यदि आपके पास कोई ऑब्जेक्ट है जो एक हैंडल हासिल कर लिया है यदि आप जावा में मैन्युअल रूप से नहीं करते हैं, या "# में निपटान" पैटर्न का उपयोग करते हैं तो फ़ाइल, फ़ाइल स्वचालित रूप से मुक्त नहीं होगी।
  • "एक वापसी प्रति फ़ंक्शन" नियम को भूल जाओ । यह लीक से बचने के लिए एक अच्छी सी सलाह है, लेकिन इसके अपवादों (इसके बजाय RAII का उपयोग करें) के उपयोग के कारण यह C ++ में पुराना है।
  • और जबकि "सैंडविच पैटर्न" एक अच्छी सी सलाह है, यह अपवादों के उपयोग (RAII का उपयोग) के कारण C ++ में पुराना है

यह पोस्ट दोहरावदार लगती है, लेकिन C ++ में, जानने के लिए सबसे बुनियादी पैटर्न RAII है

बूस्टर, TR1 या यहां तक ​​कि नीच (लेकिन अक्सर पर्याप्त कुशल) auto_ptr (लेकिन आपको इसकी सीमाओं को जानना चाहिए) दोनों से स्मार्ट पॉइंटर्स का उपयोग करना सीखें।

RAII C ++ में अपवाद सुरक्षा और संसाधन निपटान दोनों का आधार है, और कोई अन्य पैटर्न (सैंडविच, आदि) आपको दोनों (और अधिकांश समय, यह आपको कोई नहीं देगा) देगा।

RAII और गैर RAII कोड की तुलना नीचे देखें:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

RAII के बारे में

संक्षेप में कहने के लिए ( ओगरे भजन 33 से टिप्पणी के बाद ), आरएआईआई तीन अवधारणाओं पर निर्भर करता है:

  • एक बार जब ऑब्जेक्ट का निर्माण किया जाता है, तो यह काम करता है! कंस्ट्रक्टर में संसाधन प्राप्त करें।
  • वस्तु विनाश काफी है! विध्वंसक में मुक्त संसाधन करो।
  • यह सब scopes के बारे में है! स्कोप्ड ऑब्जेक्ट्स (ऊपर दिए गए doRAIIStatic उदाहरण देखें) का निर्माण उनकी घोषणा पर किया जाएगा, और उस क्षण को नष्ट कर दिया जाएगा, जब तक कि दायरे से बाहर नहीं निकल जाता है, चाहे कोई भी निकास (वापसी, ब्रेक, अपवाद, आदि)।

इसका मतलब यह है कि सही C ++ कोड में, अधिकांश ऑब्जेक्ट के साथ निर्माण नहीं किया newजाएगा, और इसके बजाय स्टैक पर घोषित किया जाएगा। और उन लोगों के लिए प्रयोग कर बनाया new, सभी किसी न किसी तरह हो जाएगा scoped (जैसे कि स्मार्ट पॉइंटर से जुड़ा हुआ)।

एक डेवलपर के रूप में, यह वास्तव में बहुत शक्तिशाली है क्योंकि आपको मैन्युअल संसाधन हैंडलिंग (जैसा कि सी में किया गया है, या जावा में कुछ वस्तुओं के लिए जो उस मामले के लिए try/ का गहन उपयोग finallyकरता है) की देखभाल करने की आवश्यकता नहीं होगी ...

संपादित करें (2012-02-12)

"scoped ऑब्जेक्ट ... नष्ट हो जाएगा ... कोई बात नहीं निकास" यह पूरी तरह से सच नहीं है। RAII को धोखा देने के तरीके हैं। किसी भी स्वाद की समाप्ति () सफाई को बायपास करेगी। बाहर निकलें (EXIT_SUCCESS) इस संबंध में एक ऑक्सीमोरोन है।

- विल्हेमटेल

विल्हेमटेल उस बारे में काफी सही है: असाधारण हैं RAII को धोखा देने के लिए तरीके , जो सभी प्रक्रिया को रोकते हैं।

वे असाधारण तरीके हैं क्योंकि C ++ कोड को समाप्त, बाहर निकलने, आदि के साथ जोड़ा नहीं गया है, या अपवादों के मामले में, हम एक अखंड अपवाद चाहते हैं प्रक्रिया को क्रैश करने के लिए और कोर अपनी मेमोरी छवि को डंप करते हैं, और सफाई के बाद नहीं।

लेकिन हमें अभी भी उन मामलों के बारे में पता होना चाहिए, क्योंकि वे शायद ही कभी होते हैं, फिर भी वे हो सकते हैं।

(जो कॉल करता है terminateया exitकैज़ुअल सी ++ कोड में? ... मुझे याद है कि ग्लूट के साथ खेलते समय मुझे उस समस्या से जूझना पड़ता है : यह लाइब्रेरी बहुत सी-ओरिएंटेड है, जहाँ तक सी + + डेवलपर्स के लिए चीजों को मुश्किल बनाने के लिए इसे सक्रिय रूप से डिजाइन करना जैसे कि देखभाल नहीं स्टैक आवंटित डेटा के बारे में , या उनके मुख्य लूप से कभी नहीं लौटने के बारे में "दिलचस्प" निर्णय लेने के बारे में ... मैं उस बारे में कोई टिप्पणी नहीं करूंगा)


टी वर्ग को RAII का उपयोग नहीं करना चाहिए यह सुनिश्चित करने के लिए कि doRAIIStatic () मेमोरी लीक नहीं करता है? उदाहरण के लिए टी पी (); p.doSandwich (); मैं वास्तव में इस बारे में बहुत कुछ नहीं जानता।
डैनियल ओ

@ ऑग्रे Psalm33: टिप्पणी के लिए धन्यवाद। बेशक, आप सही कह रहे हैं। मैंने RAII विकिपीडिया पृष्ठ पर दोनों लिंक जोड़े, और RAII क्या है इसका एक छोटा सारांश।
21:39 पर पेरेसबल

1
@ शिफ्टबिट: वरीयता के क्रम में तीन तरीके: _ _ _ 1. STL कंटेनर के अंदर असली वस्तु रखें। _ _ _ 2. STL कंटेनर के अंदर वस्तुओं के स्मार्ट पॉइंटर्स (शेयर्ड_प्ट्र) लगाएं। _ _ _ 3. एसटीएल कंटेनर के अंदर कच्चे पॉइंटर्स रखें, लेकिन डेटा तक किसी भी पहुंच को नियंत्रित करने के लिए कंटेनर को लपेटें। रैपर यह सुनिश्चित करेगा कि विध्वंसक आवंटित वस्तुओं को मुक्त कर देगा, और रैपर एक्सेसर्स यह सुनिश्चित करेंगे कि कंटेनर को एक्सेस / संशोधित करते समय कुछ भी टूट न जाए।
पियरसबल

1
@Robert: C ++ 03 में, आप एक फ़ंक्शन में doRAIIDynamic का उपयोग करेंगे जो कि बच्चे या माता-पिता के फ़ंक्शन (या वैश्विक दायरे) को स्वामित्व देना चाहिए। या जब आप एक कारखाने के माध्यम से एक पॉलीमॉर्फ ऑब्जेक्ट के लिए एक इंटरफ़ेस प्राप्त कर रहे हैं (स्मार्ट पॉइंटर लौटाते हैं, अगर यह सही ढंग से लिखा गया है)। C ++ 11 में, यह मामला कम है क्योंकि आप अपनी वस्तु को चल-फिर सकते हैं, इसलिए स्टैक पर घोषित किसी वस्तु का स्वामित्व देना आसान है ...
21

2
@Robert: ... ध्यान दें कि स्टैक पर ऑब्जेक्ट घोषित करने का मतलब यह नहीं है कि ऑब्जेक्ट आंतरिक रूप से हीप का उपयोग नहीं करता है (डबल नेगेटिव नोट करें ... :-) ...)। उदाहरण के लिए, छोटे स्ट्रिंग ऑप्टिमाइज़ेशन के साथ लागू किया गया std :: string में "क्लास के स्टैक पर एक बफर होगा" छोटे स्ट्रिंग्स के लिए (~ 15 वर्ण), और बड़े स्ट्रिंग्स के लिए हीप में मेमोरी के लिए एक पॉइंटर का उपयोग करेगा ... लेकिन बाहर से, std :: string अभी भी एक मूल्य प्रकार है जिसे आप (आमतौर पर) स्टैक पर घोषित करते हैं और आप एक पूर्णांक का उपयोग करेंगे (जैसा कि आप विरोध करते हैं: जैसा कि आप एक बहुरूपक वर्ग के लिए एक इंटरफ़ेस का उपयोग करेंगे)।
पियरसबल

25

आप स्मार्ट पॉइंटर्स को देखना चाहेंगे, जैसे कि बूस्ट के स्मार्ट पॉइंटर्स

के बजाय

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

बूस्ट :: share_ptr संदर्भ संख्या शून्य होने के बाद स्वचालित रूप से हटा देगा:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

मेरा अंतिम नोट नोट करें, "जब संदर्भ गणना शून्य है, जो सबसे ठंडा हिस्सा है। इसलिए यदि आपके पास आपके ऑब्जेक्ट के कई उपयोगकर्ता हैं, तो आपको इस बात का ध्यान नहीं रखना होगा कि ऑब्जेक्ट अभी भी उपयोग में है। एक बार कोई भी आपके लिए संदर्भित नहीं होता है। साझा सूचक, यह नष्ट हो जाता है।

यह एक रामबाण दवा नहीं है। हालांकि आप आधार पॉइंटर तक पहुंच सकते हैं, लेकिन जब तक आप यह नहीं कर रहे थे कि आप आश्वस्त नहीं थे, तब तक आप इसे तीसरी पार्टी एपीआई में नहीं भेजना चाहते। बहुत बार, आपके "पोस्टिंग" सामान को किसी अन्य थ्रेड के लिए काम करने के लिए बनाया जा सकता है, जिससे कार्यक्षेत्र समाप्त हो जाता है। यह Win32 में PostThreadMessage के साथ आम है:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

हमेशा की तरह, किसी भी टूल के साथ अपनी सोच की टोपी का उपयोग करें ...



11

अधिकांश मेमोरी लीक ऑब्जेक्ट के स्वामित्व और जीवनकाल के बारे में स्पष्ट नहीं होने का परिणाम हैं।

पहली बात यह है कि जब भी आप कर सकते हैं स्टैक पर आवंटित करें। यह उन अधिकांश मामलों से संबंधित है जहां आपको किसी उद्देश्य के लिए एक ही वस्तु आवंटित करने की आवश्यकता होती है।

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

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

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


10

बाह, आप छोटे बच्चे और आपके नए-नए फालतू कूड़ा उठाने वाले ...

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

create a thing
use that thing
destroy that thing

कभी-कभी व्यापक रूप से विभिन्न स्थानों में बनाना और नष्ट करना आवश्यक होता है; मुझे लगता है कि से बचने के लिए मुश्किल है।

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

कई अन्य बिंदुओं को तब भी परिभाषित किया जाता है जब भी एक इकाई को दूसरे तक पहुंच की आवश्यकता होती है, ताकि वह किरणों को स्कैन कर सके या जो भी हो; ये "बस देख रहे हैं" हैं। 3 डी दृश्य उदाहरण के लिए - एक ऑब्जेक्ट एक बनावट का उपयोग करता है लेकिन खुद का नहीं होता है; अन्य वस्तुएं उसी बनावट का उपयोग कर सकती हैं। किसी वस्तु का विनाश किसी भी बनावट के विनाश का आह्वान नहीं करता है ।

हाँ, यह समय लेने वाला है लेकिन मैं यही करता हूँ। मुझे शायद ही कभी स्मृति लीक या अन्य समस्याएं हैं। लेकिन फिर मैं उच्च-प्रदर्शन वैज्ञानिक, डेटा अधिग्रहण और ग्राफिक्स सॉफ्टवेयर के सीमित क्षेत्र में काम करता हूं। मैं अक्सर बैंकिंग और ईकॉमर्स, ईवेंट-चालित GUI या उच्च नेटवर्क वाली एसिंक्रोनस अराजकता जैसे लेन-देन नहीं करता। हो सकता है कि नए-नए तरीके से वहां फायदा हो!


मैं कुल मिलाकर सहमत हूं। एक एम्बेडेड वातावरण में काम करने से आपके पास तीसरे पक्ष के पुस्तकालयों की विलासिता नहीं हो सकती है।
सिमोन

6
मैं असहमत हूं। "उस चीज़ का उपयोग करें" के भाग में, यदि कोई रिटर्न या अपवाद फेंक दिया जाता है, तो आप डीलक्लॉक को याद करेंगे। प्रदर्शन के लिए, std :: auto_ptr आपके लिए कुछ भी खर्च नहीं करेगा। ऐसा नहीं है कि मैं कभी भी उसी तरह से कोड नहीं करता जो आप करते हैं। यह सिर्फ इतना है कि 100% और 99% सुरक्षित कोड के बीच अंतर है। :-)
पियरसबल

8

बड़ा अच्छा सवाल!

यदि आप c ++ का उपयोग कर रहे हैं और आप रीयल-टाइम CPU-and-memory boud एप्लिकेशन (गेम्स की तरह) विकसित कर रहे हैं, तो आपको अपना मेमोरी मेमोरी मैनेजर लिखने की आवश्यकता है।

मुझे लगता है कि आप जो बेहतर कर सकते हैं वह विभिन्न लेखकों के कुछ दिलचस्प कार्यों को मर्ज करना है, मैं आपको कुछ संकेत दे सकता हूं:

  • फिक्स्ड साइज एलोकेटर की चर्चा हर जगह होती है, हर जगह नेट में

  • अलेक्जेंड्रेस्कु द्वारा 2001 में अपनी संपूर्ण पुस्तक "मॉडर्न सी ++ डिज़ाइन" में स्मॉल ऑब्जेक्ट अलोकेशन प्रस्तुत किया गया था

  • डिमेरिट लजारोव द्वारा लिखित गेम प्रोग्रामिंग जेम 7 (2008) "हाई परफॉर्मेंस हीप एलोकेटर" नाम के एक अद्भुत लेख में एक शानदार उन्नति पाई जा सकती है।

  • संसाधनों की एक महान सूची इस लेख में मिल सकती है

अपने आप से एक नॉब यूजफुल एलोकेटर लिखना शुरू न करें ... पहले खुद को डॉक्युमेंट करें।


5

C ++ में मेमोरी प्रबंधन के साथ लोकप्रिय एक तकनीक RAII है । मूल रूप से आप संसाधन आवंटन को संभालने के लिए कंस्ट्रक्टर / विध्वंसक का उपयोग करते हैं। बेशक अपवाद सुरक्षा के कारण C ++ में कुछ अन्य अप्रिय विवरण हैं, लेकिन मूल विचार बहुत सरल है।

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


5

रिसाव न होने के बारे में पहले से ही बहुत कुछ है, लेकिन अगर आपको लीक पर नज़र रखने में मदद करने के लिए एक उपकरण की आवश्यकता है:


BoundsChecker 404ing है।
टैंकरस्मैश

4

उपयोगकर्ता स्मार्ट संकेत हर जगह आप कर सकते हैं! मेमोरी लीक की पूरी कक्षाएं बस चली जाती हैं।


4

अपनी परियोजना में स्मृति स्वामित्व नियमों को साझा करें और जानें। COM नियमों का उपयोग करना सर्वोत्तम स्थिरता के लिए बनाता है ([में] पैरामीटर फोन करने वाले के स्वामित्व में होते हैं, कैली को कॉपी करना चाहिए; [आउट] कॉलर्स के स्वामित्व वाले हैं, एक संदर्भ रखते हुए कैली को कॉपी बनाना चाहिए; आदि;


4

valgrind रनटाइम में भी आपके प्रोग्राम मेमोरी लीकेज की जाँच करने के लिए एक अच्छा उपकरण है।

यह लिनक्स (एंड्रॉइड सहित) और डार्विन पर सबसे अधिक फ्लेवर पर उपलब्ध है।

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

बेशक यह सलाह किसी अन्य मेमोरी चेक टूल के लिए मान्य है।


3

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


2

यदि आप किसी चीज़ के लिए स्मार्ट पॉइंटर का उपयोग नहीं कर सकते / कर सकते हैं (हालाँकि यह बहुत बड़ा लाल झंडा होना चाहिए), तो अपने कोड में टाइप करें:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

यह स्पष्ट है, लेकिन यह सुनिश्चित करें कि आप किसी भी कोड को दायरे में टाइप करने से पहले उसे टाइप कर लें


2

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

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

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

तर्क सूचियों में गैर-कॉन्स्टेंस संदर्भों का उपयोग न करें। कॉलर कोड को पढ़ते समय यह बहुत स्पष्ट नहीं है कि कैलीले ने पैरामीटर का संदर्भ रखा हो सकता है।

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


2

महत्व के क्रम में युक्तियाँ:

-Tip # 1 अपने विध्वंसक "आभासी" घोषित करने के लिए हमेशा याद रखें।

-Tip # 2 RAII का उपयोग करें

-Tip # 3 बूस्ट के स्मार्ट पॉइंटर्स का उपयोग करें

-Tip # 4 अपने स्वयं के छोटी गाड़ी स्मार्टपॉइंट न लिखें, बूस्ट का उपयोग करें (अभी मैं जिस प्रोजेक्ट पर हूं, मैं उसका उपयोग नहीं कर सकता हूं, और मुझे अपने स्मार्ट पॉइंटर्स को डिबग करने का सामना करना पड़ा है, मैं निश्चित रूप से नहीं लूंगा एक ही मार्ग फिर से, लेकिन फिर फिर से अभी मैं हमारी निर्भरता को बढ़ावा नहीं दे सकता)

-Tip # 5 यदि इसके कुछ आकस्मिक / गैर-प्रदर्शन महत्वपूर्ण (हजारों वस्तुओं के साथ खेल में) काम थोरस्टेन ओटोसन के बूस्टर पॉइंटर कंटेनर में देखें

-Tip # 6 अपनी पसंद के प्लेटफ़ॉर्म के लिए लीक डिटेक्शन हेडर ढूंढें जैसे कि विज़ुअल लीक डिटेक्शन का "vld" हेडर


मुझे एक चाल याद आ रही है, लेकिन एक ही वाक्य में 'खेल' और 'गैर-प्रदर्शन-महत्वपूर्ण' कैसे हो सकता है?
एडम नायलर

खेल पाठ्यक्रम के महत्वपूर्ण परिदृश्य का एक उदाहरण हैं। वहाँ स्पष्ट होने में विफल रहे हैं
राबर्ट गोल्ड

टिप # 1 को केवल तभी लागू किया जाना चाहिए जब कक्षा में कम से कम एक आभासी विधि हो। मैं एक वर्ग पर एक बेकार आभासी विध्वंसक कभी नहीं लगाऊंगा जो कि एक बहुरूपिक विरासत के पेड़ में एक आधार वर्ग के रूप में सेवा करने के लिए नहीं है।
antred

1

यदि आप कर सकते हैं, तो बूस्ट शेयर्ड_प्ट्र और मानक C ++ auto_ptr का उपयोग करें। वे स्वामित्व शब्दार्थ को व्यक्त करते हैं।

जब आप एक auto_ptr वापस करते हैं, तो आप कॉलर को बता रहे हैं कि आप उन्हें स्मृति का स्वामित्व दे रहे हैं।

जब आप एक share_ptr वापस करते हैं, तो आप कॉलर को बता रहे हैं कि आपके पास इसका संदर्भ है और वे स्वामित्व का हिस्सा लेते हैं, लेकिन यह केवल उनकी जिम्मेदारी नहीं है।

ये शब्दार्थ भी मापदंडों पर लागू होते हैं। यदि कॉलर आपको एक auto_ptr पास करता है, तो वे आपको स्वामित्व दे रहे हैं।


1

अन्य लोगों ने पहली बार (स्मार्ट पॉइंटर्स की तरह) मेमोरी लीक से बचने के तरीकों का उल्लेख किया है। लेकिन एक रूपरेखा और मेमोरी-विश्लेषण उपकरण अक्सर आपके पास एक बार मेमोरी समस्याओं को ट्रैक करने का एकमात्र तरीका होता है।

Valgrind memcheck एक उत्कृष्ट निःशुल्क है।


1

केवल MSVC के लिए, प्रत्येक .cpp फ़ाइल के शीर्ष पर निम्न जोड़ें:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

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



1

यदि आप अपनी मेमोरी को मैन्युअल रूप से प्रबंधित करने जा रहे हैं, तो आपके पास दो मामले हैं:

  1. मैंने ऑब्जेक्ट बनाया (शायद अप्रत्यक्ष रूप से, एक फ़ंक्शन को कॉल करके जो एक नई ऑब्जेक्ट आवंटित करता है), मैं इसका उपयोग करता हूं (या एक फ़ंक्शन जिसे मैं इसका उपयोग करता हूं), तो मैं इसे मुक्त करता हूं।
  2. किसी ने मुझे संदर्भ दिया, इसलिए मुझे इसे मुक्त नहीं करना चाहिए।

यदि आपको इनमें से किसी भी नियम को तोड़ने की आवश्यकता है, तो कृपया इसे दस्तावेज़ित करें।

यह सूचक स्वामित्व के बारे में है।


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

0

आप मेमोरी आवंटन कार्यों को रोक सकते हैं और देख सकते हैं कि क्या प्रोग्राम से बाहर निकलने पर कुछ मेमोरी ज़ोन मुक्त नहीं हैं (हालांकि यह सभी अनुप्रयोगों के लिए उपयुक्त नहीं है )।

यह संचालकों के नए और हटाए जाने और अन्य मेमोरी आवंटन कार्यों को प्रतिस्थापित करके संकलन समय पर भी किया जा सकता है।

उदाहरण के लिए इस साइट में जाँच करें [C ++ में मेमोरी आवंटन डिबगिंग] नोट: डिलीट ऑपरेटर के लिए एक ट्रिक है, कुछ इस तरह से:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

आप कुछ चर को फ़ाइल के नाम से संग्रहीत कर सकते हैं और जब ओवरलोड हटाए गए ऑपरेटर को पता चल जाएगा कि वह कौन सी जगह थी, जहां से इसे बुलाया गया था। इस तरह आप अपने प्रोग्राम से हर डिलीट और मॉलोक का पता लगा सकते हैं। मेमोरी चेकिंग सीक्वेंस के अंत में आपको यह रिपोर्ट करने में सक्षम होना चाहिए कि मेमोरी के आवंटित ब्लॉक को of डिलीट ’नहीं किया गया था, इसे फ़ाइल नाम और लाइन नंबर से पहचानना चाहिए जो मुझे लगता है कि आप क्या चाहते हैं।

तुम भी Visual Studio के तहत BoundsChecker की तरह कुछ कोशिश कर सकते हैं जो बहुत ही रोचक और उपयोग करने में आसान है।


0

हम अपने सभी आवंटन कार्यों को एक परत के साथ लपेटते हैं जो सामने एक संक्षिप्त स्ट्रिंग और अंत में एक प्रहरी झंडा लगाता है। इसलिए उदाहरण के लिए आपको "myalloc (pszSomeString, iSize, iAlignment); या नया (" विवरण ", iSize) MyObject () कॉल करना होगा, जो आंतरिक रूप से आपके हेडर और सेंटिनल के लिए निर्दिष्ट स्थान को पर्याप्त स्थान आवंटित करता है। , गैर-डिबग बिल्ड के लिए इसे बाहर टिप्पणी करना मत भूलना! यह ऐसा करने के लिए थोड़ी अधिक स्मृति लेता है, लेकिन लाभ लागत से बहुत दूर हैं।

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


0

C ++ को RAII को ध्यान में रखकर बनाया गया है। स्मृति में C ++ का प्रबंधन करने का वास्तव में कोई बेहतर तरीका नहीं है। लेकिन सावधान रहें कि स्थानीय दायरे पर बहुत बड़ी मात्रा (जैसे बफर ऑब्जेक्ट) आवंटित न करें। यह स्टैक ओवरफ्लो का कारण बन सकता है और, यदि उस चंक का उपयोग करते समय सीमा की जाँच में कोई दोष है, तो आप अन्य चर या रिटर्न पतों को अधिलेखित कर सकते हैं, जो सभी प्रकार के सुरक्षा छेद की ओर जाता है।


0

विभिन्न स्थानों में आवंटित करने और नष्ट करने के बारे में एकमात्र उदाहरण धागा निर्माण (आपके द्वारा पारित पैरामीटर) है। लेकिन इस मामले में भी आसान है। यहाँ एक धागा बनाने का कार्य / विधि है:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

यहां थ्रेड फंक्शन के बजाय

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

बहुत आसान है ना? यदि थ्रेड निर्माण विफल हो जाता है, तो संसाधन auto_ptr द्वारा फ्री होल्ड (डिलीट) हो जाएगा, अन्यथा स्वामित्व थ्रेड को पास कर दिया जाएगा। क्या होगा यदि धागा इतना तेज है कि निर्माण के बाद यह थ्रेड से पहले संसाधन जारी करता है

param.release();

मुख्य कार्य / विधि में कहा जाता है? कुछ भी तो नहीं! क्योंकि हम सौदे को अनदेखा करने के लिए auto_ptr को 'बताएंगे'। क्या C ++ मेमोरी मैनेजमेंट आसान नहीं है? चीयर्स,

ईएमए!


0

स्मृति को उसी तरह प्रबंधित करें जैसे आप अन्य संसाधनों (हैंडल, फाइलें, डीबी कनेक्शन, सॉकेट ...) का प्रबंधन करते हैं। जीसी आपको उनके साथ मदद नहीं करेगा।


-3

किसी भी फ़ंक्शन से सटीक एक वापसी। इस तरह से आप वहां पर डील-डौल कर सकते हैं और कभी नहीं चूक सकते।

अन्यथा गलती करना बहुत आसान है:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.

आपका उत्तर यहाँ उदाहरण कोड से मेल नहीं खाता है? मैं उत्तर "केवल एक वापसी" से सहमत हूं, लेकिन उदाहरण कोड दिखा रहा है कि क्या नहीं करना है।
सिमोन

1
C ++ RAII का बिंदु आपके द्वारा लिखे गए कोड के प्रकार से बचने के लिए है। सी में, यह शायद सही काम है। लेकिन C ++ में, आपका कोड त्रुटिपूर्ण है। उदाहरण के लिए: क्या होगा यदि नया बी () फेंकता है? आप लीक एक
पियरसबल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.