दो व्यापक रूप से उपयोग की जाने वाली मेमोरी आवंटन तकनीकें हैं: स्वचालित आवंटन और गतिशील आवंटन। आमतौर पर, प्रत्येक के लिए स्मृति का एक समान क्षेत्र होता है: स्टैक और ढेर।
ढेर
स्टैक हमेशा मेमोरी को क्रमिक रूप से आवंटित करता है। यह ऐसा कर सकता है क्योंकि आपको मेमोरी को रिवर्स ऑर्डर (फर्स्ट-इन, लास्ट-आउट: FILO) में रिलीज़ करना होगा। यह कई प्रोग्रामिंग भाषाओं में स्थानीय चर के लिए मेमोरी आवंटन तकनीक है। यह बहुत, बहुत तेज़ है क्योंकि इसमें न्यूनतम बहीखाता की आवश्यकता होती है और आवंटित करने के लिए अगला पता निहित है।
C ++ में, इसे स्वचालित भंडारण कहा जाता है क्योंकि भंडारण का स्कोप के अंत में स्वचालित रूप से दावा किया जाता है। जैसे ही वर्तमान कोड ब्लॉक का उपयोग (सीमांकित का उपयोग करना {}
) पूरा हो जाता है, उस ब्लॉक के सभी चर के लिए मेमोरी स्वचालित रूप से एकत्र हो जाती है। यह वह क्षण भी होता है जब संसाधनों को साफ करने के लिए विध्वंसक आक्रमण किए जाते हैं।
ढेर
ढेर एक और अधिक लचीला स्मृति आवंटन मोड के लिए अनुमति देता है। बहीखाता अधिक जटिल है और आवंटन धीमी है। क्योंकि वहाँ कोई अंतर्निहित रिहाई बिंदु है, तो आप स्मृति मैन्युअल जारी करना चाहिए, का उपयोग कर delete
या delete[]
( free
सी में)। हालांकि, एक अंतर्निहित रिलीज बिंदु की अनुपस्थिति ढेर के लचीलेपन की कुंजी है।
गतिशील आवंटन का उपयोग करने के कारण
यहां तक कि अगर ढेर का उपयोग धीमा है और संभावित रूप से मेमोरी लीक या मेमोरी विखंडन की ओर जाता है, तो गतिशील आवंटन के लिए पूरी तरह से अच्छे उपयोग के मामले हैं, क्योंकि यह कम सीमित है।
गतिशील आवंटन का उपयोग करने के दो प्रमुख कारण:
आप नहीं जानते कि संकलन के समय आपको कितनी मेमोरी चाहिए। उदाहरण के लिए, जब एक पाठ फ़ाइल को एक स्ट्रिंग में पढ़ा जाता है, तो आप आमतौर पर यह नहीं जानते कि फ़ाइल का आकार क्या है, इसलिए आप यह तय नहीं कर सकते कि प्रोग्राम चलाने तक कितनी मेमोरी आवंटित की जाए।
आप स्मृति को आवंटित करना चाहते हैं जो वर्तमान ब्लॉक को छोड़ने के बाद बनी रहेगी। उदाहरण के लिए, आप एक फ़ंक्शन लिखना चाहते string readfile(string path)
हैं जो किसी फ़ाइल की सामग्री लौटाता है। इस स्थिति में, भले ही स्टैक संपूर्ण फ़ाइल सामग्री को पकड़ सकता है, आप एक फ़ंक्शन से वापस नहीं आ सकते हैं और आवंटित मेमोरी ब्लॉक को रख सकते हैं।
क्यों गतिशील आवंटन अक्सर अनावश्यक होता है
C ++ में एक विध्वंसक निर्माण होता है जिसे विध्वंसक कहते हैं । यह तंत्र आपको एक चर के जीवनकाल के साथ संसाधन के जीवनकाल को संरेखित करके संसाधनों का प्रबंधन करने की अनुमति देता है। इस तकनीक कहा जाता है आरए II और C ++ के विशिष्ठ स्थान है। यह वस्तुओं में संसाधनों को "लपेटता" है। std::string
एक आदर्श उदाहरण है। यह स्निपेट:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
वास्तव में स्मृति की एक चर राशि आवंटित करता है। std::string
वस्तु आवंटित स्मृति ढेर और उसके नाशक में यह रिलीज का उपयोग कर। इस मामले में, आपको किसी भी संसाधन को मैन्युअल रूप से प्रबंधित करने की आवश्यकता नहीं थी और फिर भी गतिशील मेमोरी आवंटन का लाभ मिला।
विशेष रूप से, इसका अर्थ है कि इस स्निपेट में:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
अनावश्यक डायमेंशनल मेमोरी एलोकेशन है। कार्यक्रम को अधिक टाइपिंग (!) की आवश्यकता होती है और यह मेमोरी से निपटने के लिए भूलने के जोखिम का परिचय देता है। यह कोई स्पष्ट लाभ के साथ ऐसा करता है।
आपको क्यों संभव के रूप में स्वचालित भंडारण का उपयोग करना चाहिए
असल में, आखिरी पैराग्राफ इसे पूरा करता है। स्वत: भंडारण का उपयोग करते हुए जितनी बार संभव अपने कार्यक्रमों में आता है:
- तेजी से टाइप करने के लिए;
- तेज चलने पर;
- स्मृति / संसाधन लीक होने का खतरा कम।
बोनस अंक
संदर्भित प्रश्न में, वहाँ अतिरिक्त चिंताएं हैं। विशेष रूप से, निम्न वर्ग:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
वास्तव में निम्नलिखित की तुलना में उपयोग करने के लिए बहुत अधिक जोखिम भरा है:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
कारण यह है कि std::string
एक कॉपी कंस्ट्रक्टर को ठीक से परिभाषित करता है। निम्नलिखित कार्यक्रम पर विचार करें:
int main ()
{
Line l1;
Line l2 = l1;
}
मूल संस्करण का उपयोग करना, इस कार्यक्रम संभावना है, दुर्घटना के रूप में यह का उपयोग करता होगा delete
दो बार एक ही स्ट्रिंग पर। संशोधित संस्करण का उपयोग करते हुए, प्रत्येक Line
उदाहरण का अपना स्ट्रिंग उदाहरण होगा , प्रत्येक की अपनी स्मृति होगी और दोनों को कार्यक्रम के अंत में जारी किया जाएगा।
अन्य नोट
ऊपर दिए गए सभी कारणों की वजह से RAII का व्यापक उपयोग C ++ में सबसे अच्छा अभ्यास माना जाता है। हालांकि, एक अतिरिक्त लाभ है जो तुरंत स्पष्ट नहीं है। असल में, यह अपने भागों के योग से बेहतर है। पूरा तंत्र रचना करता है । यह तराजू है।
यदि आप Line
क्लास को बिल्डिंग ब्लॉक के रूप में उपयोग करते हैं :
class Table
{
Line borders[4];
};
फिर
int main ()
{
Table table;
}
चार std::string
उदाहरण, चार Line
उदाहरण, एक Table
उदाहरण और सभी स्ट्रिंग सामग्री और सब कुछ स्वचालित रूप से मुक्त किया गया है ।