C ++ में RAII और स्मार्ट पॉइंटर्स


193

C ++ के साथ अभ्यास में, RAII क्या है , स्मार्ट पॉइंटर्स क्या हैं , ये प्रोग्राम में कैसे लागू किए जाते हैं और स्मार्ट पॉइंटर्स के साथ RAII का उपयोग करने के क्या फायदे हैं?

जवाबों:


317

RAII का एक सरल (और शायद अधिक उपयोग किया जाने वाला) उदाहरण एक फ़ाइल वर्ग है। RAII के बिना, कोड कुछ इस तरह दिख सकता है:

File file("/path/to/file");
// Do stuff with file
file.close();

दूसरे शब्दों में, हमें यह सुनिश्चित करना चाहिए कि जब हम इसके साथ समाप्त कर लें, तो हम फ़ाइल को बंद कर दें। इसकी दो कमियां हैं - सबसे पहले, जहाँ भी हम फ़ाइल का उपयोग करते हैं, हमें फ़ाइल :: close () कहा जाता है - यदि हम ऐसा करना भूल जाते हैं, तो हमें उस फ़ाइल पर अधिक समय तक रोकना पड़ता है, जब हमें उसकी आवश्यकता होती है। दूसरी समस्या यह है कि यदि हम फ़ाइल को बंद करने से पहले एक अपवाद को फेंक देते हैं तो क्या होगा?

जावा अंत में क्लॉज का उपयोग करके दूसरी समस्या हल करता है:

try {
    File file = new File("/path/to/file");
    // Do stuff with file
} finally {
    file.close();
}

या जावा 7 के बाद से, एक कोशिश के साथ संसाधन बयान:

try (File file = new File("/path/to/file")) {
   // Do stuff with file
}

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

File file("/path/to/file");
// Do stuff with file
// No need to close it - destructor will do that for us

यह जावा में नहीं किया जा सकता है क्योंकि ऑब्जेक्ट नष्ट होने की कोई गारंटी नहीं है, इसलिए हम गारंटी नहीं दे सकते कि फाइल जैसे संसाधन को कब मुक्त किया जाएगा।

स्मार्ट पॉइंटर्स पर - बहुत समय, हम बस स्टैक पर ऑब्जेक्ट बनाते हैं। उदाहरण के लिए (और दूसरे उत्तर से एक उदाहरण चुराना):

void foo() {
    std::string str;
    // Do cool things to or using str
}

यह ठीक काम करता है - लेकिन क्या होगा अगर हम str लौटना चाहते हैं? हम इसे लिख सकते हैं:

std::string foo() {
    std::string str;
    // Do cool things to or using str
    return str;
}

तो, इसमें गलत क्या है? खैर, वापसी का प्रकार एसटीडी :: स्ट्रिंग है - तो इसका मतलब है कि हम मूल्य से लौट रहे हैं। इसका मतलब यह है कि हम str कॉपी करते हैं और वास्तव में कॉपी वापस करते हैं। यह महंगा हो सकता है, और हम इसे कॉपी करने की लागत से बचना चाहते हैं। इसलिए, हम संदर्भ या सूचक द्वारा लौटने के विचार के साथ आ सकते हैं।

std::string* foo() {
    std::string str;
    // Do cool things to or using str
    return &str;
}

दुर्भाग्य से, यह कोड काम नहीं करता है। हम स्ट्राइक पर एक पॉइंटर लौटा रहे हैं - लेकिन स्टैक पर स्ट्रेट बनाया गया था, इसलिए हम फू () से बाहर निकलते ही हटा दिए जाते हैं। दूसरे शब्दों में, जब तक कॉलर को पॉइंटर मिलता है, तब तक यह बेकार होता है (और यकीनन बेकार से बदतर होता है क्योंकि इसे इस्तेमाल करने से हर तरह की फनी त्रुटियाँ हो सकती हैं)

तो, समाधान क्या है? हम नए का उपयोग कर ढेर पर बना सकते हैं - इस तरह, जब फू () पूरा हो गया है, तो तार नष्ट नहीं होगा।

std::string* foo() {
    std::string* str = new std::string();
    // Do cool things to or using str
    return str;
}

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

यह वह जगह है जहाँ स्मार्ट पॉइंटर्स आते हैं। निम्नलिखित उदाहरण शेयर्ड_एप्ट्र का उपयोग करता है - मेरा सुझाव है कि आप विभिन्न प्रकार के स्मार्ट पॉइंटर्स को देखें जो आप वास्तव में उपयोग करना चाहते हैं।

shared_ptr<std::string> foo() {
    shared_ptr<std::string> str = new std::string();
    // Do cool things to or using str
    return str;
}

अब, SHAR_ptr संदर्भों की संख्या को str में गिना जाएगा। उदाहरण के लिए

shared_ptr<std::string> str = foo();
shared_ptr<std::string> str2 = str;

अब एक ही तार के दो संदर्भ हैं। एक बार str के लिए कोई शेष संदर्भ नहीं है, इसे हटा दिया जाएगा। जैसे, अब आपको इसे हटाने की चिंता नहीं करनी होगी।

त्वरित संपादन: जैसा कि कुछ टिप्पणियों में बताया गया है, यह उदाहरण दो कारणों के लिए बिल्कुल सही नहीं है। सबसे पहले, स्ट्रिंग्स के कार्यान्वयन के कारण, एक स्ट्रिंग की नकल करना सस्ता हो जाता है। दूसरे, जिसे रिटर्न वैल्यू ऑप्टिमाइज़ेशन के नाम से जाना जाता है, उसकी वजह से मूल्य वापस करना महंगा नहीं हो सकता क्योंकि कंपाइलर चीजों को गति देने के लिए कुछ चतुराई कर सकता है।

तो, चलिए हमारे फ़ाइल वर्ग का उपयोग करके एक अलग उदाहरण देखें।

मान लें कि हम किसी फ़ाइल को लॉग के रूप में उपयोग करना चाहते हैं। इसका मतलब है कि हम अपनी फ़ाइल को केवल मोड में खोलना चाहते हैं:

File file("/path/to/file", File::append);
// The exact semantics of this aren't really important,
// just that we've got a file to be used as a log

अब, अन्य वस्तुओं के जोड़े के लिए लॉग के रूप में हमारी फाइल सेट करें:

void setLog(const Foo & foo, const Bar & bar) {
    File file("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

दुर्भाग्य से, यह उदाहरण बुरी तरह से समाप्त होता है - जैसे ही यह विधि समाप्त होती है, फ़ाइल बंद हो जाएगी, जिसका अर्थ है कि फू और बार में अब एक अमान्य लॉग फ़ाइल है। हम ढेर पर फ़ाइल का निर्माण कर सकते हैं, और foo और bar दोनों को फाइल करने के लिए एक पॉइंटर पास कर सकते हैं:

void setLog(const Foo & foo, const Bar & bar) {
    File* file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

लेकिन फिर फाइल हटाने के लिए कौन जिम्मेदार है? यदि न तो फ़ाइल को हटाएं, तो हमारे पास स्मृति और संसाधन रिसाव दोनों हैं। हमें नहीं पता कि फू या बार पहले फाइल के साथ खत्म होगा या नहीं, इसलिए हम यह उम्मीद भी नहीं कर सकते हैं कि वह फाइल को खुद डिलीट कर दे। उदाहरण के लिए, अगर f फ़ाइल को हटाने से पहले बार के साथ समाप्त हो गया है, तो बार में अब एक अमान्य पॉइंटर है।

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

void setLog(const Foo & foo, const Bar & bar) {
    shared_ptr<File> file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

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


7
यह ध्यान दिया जाना चाहिए कि कई स्ट्रिंग कार्यान्वयन एक संदर्भ गिने पॉइंटर के संदर्भ में कार्यान्वित किए जाते हैं। ये कॉपी-ऑन-राइट सेमेंटिक्स वास्तव में सस्ती कीमत द्वारा एक स्ट्रिंग लौटाते हैं।

7
यहां तक ​​कि उन लोगों के लिए भी, जो कई कंपाइलर NRV ऑप्टिमाइज़ेशन को लागू करते हैं जो ओवरहेड की देखभाल करेंगे। सामान्य तौर पर, मुझे शेयर्ड_पार्टर शायद ही कभी उपयोगी लगे - बस आरएआईआई के साथ रहें और साझा स्वामित्व से बचें।
निमन्जा ट्राइफुनोविक

27
वास्तव में स्मार्ट पॉइंटर्स का उपयोग करने के लिए एक स्ट्रिंग वापस करना एक अच्छा कारण नहीं है। रिटर्न वैल्यू ऑप्टिमाइज़ेशन आसानी से रिटर्न को ऑप्टिमाइज़ कर सकता है, और c ++ 1x मूव शब्दार्थ एक कॉपी को पूरी तरह से खत्म कर देगा (जब सही तरीके से उपयोग किया जाता है)। उदाहरण के बदले कुछ वास्तविक विश्व उदाहरण दिखाएं (उदाहरण के लिए जब हम एक ही संसाधन साझा करते हैं) :)
जोहान्स स्काउब -

1
मुझे लगता है कि आपका निष्कर्ष इस बारे में जल्दी है कि जावा ऐसा क्यों नहीं कर सकता है। जावा या C # में इस सीमा का वर्णन करने का सबसे आसान तरीका है क्योंकि स्टैक पर आवंटित करने का कोई तरीका नहीं है। C # एक विशेष कीवर्ड के माध्यम से स्टैक आवंटन की अनुमति देता है, लेकिन आप सुरक्षित प्रकार खो देते हैं।
ApplePieIsGood

4
@Nemanja Trifunovic: इस संदर्भ में RAII का मतलब है कि आप कॉपियों को वापस कर रहे हैं / स्टैक पर ऑब्जेक्ट बना रहे हैं? यदि आपके पास उन वस्तुओं की वापसी नहीं होती है / जिन्हें स्वीकार किया जाता है, तो उन वस्तुओं को स्वीकार / स्वीकार कर सकते हैं जिन्हें उपवर्ग में रखा जा सकता है। फिर आपको ऑब्जेक्ट को टुकड़ा करने से बचने के लिए एक पॉइंटर का उपयोग करना होगा, और मैं तर्क दूंगा कि स्मार्ट पॉइंटर अक्सर उन मामलों में कच्चे से बेहतर होता है।
फ्रैंक ओस्टरफेल्ड

141

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

बस एक उदाहरण यह SBRM के बिना कर रहा है:

void o_really() {
     resource * r = allocate_resource();
     try {
         // something, which could throw. ...
     } catch(...) {
         deallocate_resource(r);
         throw;
     }
     if(...) { return; } // oops, forgot to deallocate
     deallocate_resource(r);
}

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

struct resource_holder {
    resource_holder() {
        r = allocate_resource();
    }
    ~resource_holder() {
        deallocate_resource(r);
    }
    resource * r;
};

void o_really() {
     resource_holder r;
     // something, which could throw. ...
     if(...) { return; }
}

यह अच्छा है यदि आपको अपनी स्वयं की कक्षाएं मिली हैं जो केवल संसाधनों को आवंटित / डील करने के उद्देश्य से नहीं हैं। उनके काम करवाने के लिए आवंटन केवल एक अतिरिक्त चिंता होगी। लेकिन जैसे ही आप संसाधनों को आवंटित / डील करना चाहते हैं, वैसे ही ऊपर वाला बेकार हो जाता है। आपको हर तरह के संसाधन के लिए एक रैपिंग क्लास लिखना होगा। इसे कम करने के लिए, स्मार्ट पॉइंटर्स आपको उस प्रक्रिया को स्वचालित करने की अनुमति देते हैं:

shared_ptr<Entry> create_entry(Parameters p) {
    shared_ptr<Entry> e(Entry::createEntry(p), &Entry::freeEntry);
    return e;
}

आम तौर पर, स्मार्ट पॉइंटर्स नए / डिलीट के आस-पास पतले रैपर होते हैं जो कॉल करने के लिए बस तब होते हैं deleteजब वे जिस संसाधन के दायरे से बाहर जाते हैं। कुछ स्मार्ट पॉइंटर्स, जैसे कि share_ptr आपको उन्हें एक तथाकथित डीलेटर बताने की अनुमति देता है, जिसका उपयोग इसके बजाय किया जाता है delete। उदाहरण के लिए, आपको विंडो हैंडल, नियमित अभिव्यक्ति संसाधनों और अन्य मनमाने सामानों को प्रबंधित करने की अनुमति देता है, जब तक कि आप सही डिलेटर के बारे में साझा नहीं करते।

विभिन्न उद्देश्यों के लिए अलग-अलग स्मार्ट पॉइंटर्स हैं:

unique_ptr

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

कोड:

unique_ptr<plot_src> p(new plot_src); // now, p owns
unique_ptr<plot_src> u(move(p)); // now, u owns, p owns nothing.
unique_ptr<plot_src> v(u); // error, trying to copy u

vector<unique_ptr<plot_src>> pv; 
pv.emplace_back(new plot_src); 
pv.emplace_back(new plot_src);

Auto_ptr के विपरीत, unique_ptr को एक कंटेनर में रखा जा सकता है, क्योंकि कंटेनर गैर-प्रतिलिपि योग्य (लेकिन चल) प्रकार, स्ट्रीम और unique_ptr की तरह रखने में सक्षम होंगे।

scoped_ptr

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

कोड:

void do_something() {
    scoped_ptr<pipe> sp(new pipe);
    // do something here...
} // when going out of scope, sp will delete the pointer automatically. 

shared_ptr

साझा स्वामित्व के लिए है। इसके लिए, यह प्रतिलिपि और चल दोनों है। एकाधिक स्मार्ट पॉइंटर इंस्टेंस एक ही संसाधन के मालिक हो सकते हैं। जैसे ही आखिरी स्मार्ट पॉइंटर मालिक के पास संसाधन के दायरे से बाहर हो जाता है, संसाधन को मुक्त कर दिया जाएगा। मेरी परियोजनाओं में से कुछ वास्तविक दुनिया उदाहरण:

कोड:

shared_ptr<plot_src> p(new plot_src(&fx));
plot1->add(p)->setColor("#00FF00");
plot2->add(p)->setColor("#FF0000");
// if p now goes out of scope, the src won't be freed, as both plot1 and 
// plot2 both still have references. 

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


जहाँ तक मुझे पता है कि गैर-कॉपी करने योग्य वस्तुएं stl कंटेनरों में उपयोग करना अच्छा नहीं है क्योंकि वे मूल्य शब्दार्थों पर भरोसा करते हैं - यदि आप उस कंटेनर को क्रमबद्ध करना चाहते हैं तो क्या होता है? सॉर्ट तत्वों को कॉपी करता है ...
fmuecke

सी ++ 0x कंटेनरों को बदल दिया जाएगा ताकि यह केवल-प्रकार के प्रकारों का सम्मान करे unique_ptr, और sortइसी तरह बदला भी जाएगा।
जोहान्स स्काउब -

क्या आपको याद है कि आपने पहली बार SBRM शब्द कहाँ सुना था? जेम्स इसे ट्रैक करने की कोशिश कर रहा है।
GManNickG

मुझे इनका उपयोग करने के लिए कौन से हेडर या लाइब्रेरीज़ को शामिल करना चाहिए? इस पर कोई और रीडिंग?
atoMerz

यहाँ एक सलाह: अगर @ सीएलबी द्वारा C ++ प्रश्न का उत्तर है, तो यह सही उत्तर है (कोई बात नहीं वोट या उत्तर "सही" के रूप में चिह्नित किया गया ...)
fnl

32

अवधारणा में आधार और कारण सरल हैं।

RAII यह सुनिश्चित करने के लिए डिज़ाइन प्रतिमान है कि चर अपने कंस्ट्रक्टरों में सभी आवश्यक आरंभीकरण को संभालते हैं और सभी को उनके तंतुओं में सफाई की आवश्यकता होती है। यह एक ही कदम के लिए सभी इनिशियलाइज़ेशन और क्लीनअप को कम करता है।

C ++ को RAII की आवश्यकता नहीं है, लेकिन यह तेजी से स्वीकार किया जाता है कि RAII विधियों का उपयोग करने से अधिक मजबूत कोड का उत्पादन होगा।

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

इन तंत्रों के लिए सभी आरंभीकरण और सफाई को बांधने से, आपको यह सुनिश्चित किया जाता है कि C ++ आपके लिए भी इस काम का ध्यान रखेगा।

C ++ में RAII के बारे में बात करना आमतौर पर स्मार्ट पॉइंटर्स की चर्चा की ओर जाता है, क्योंकि सफाई के लिए पॉइंटर्स विशेष रूप से नाजुक होते हैं। मॉलोक या नए से अधिग्रहित ढेर-आवंटित मेमोरी का प्रबंधन करते समय, यह आमतौर पर प्रोग्रामर की जिम्मेदारी होती है कि पॉइंटर को नष्ट करने से पहले उस मेमोरी को मुक्त या हटा दें। स्मार्ट संकेत आरएआईआई दर्शन का उपयोग यह सुनिश्चित करने के लिए करेंगे कि ढेर आवंटित वस्तुएं किसी भी समय नष्ट हो जाएं क्योंकि सूचक चर नष्ट हो जाता है।


इसके अलावा - पॉइंटर्स RAII का सबसे सामान्य अनुप्रयोग है - आप संभवतः किसी अन्य संसाधन की तुलना में हजारों गुना अधिक पॉइंटर्स आवंटित करेंगे।
ग्रहण

8

स्मार्ट पॉइंटर RAII की भिन्नता है। RAII का अर्थ है संसाधन का अधिग्रहण इनिशियलाइज़ेशन। स्मार्ट पॉइंटर उपयोग से पहले एक संसाधन (मेमोरी) प्राप्त करता है और फिर इसे एक विध्वंसक में स्वचालित रूप से फेंक देता है। दो चीजें होती हैं:

  1. हम इसका उपयोग करने से पहले हमेशा मेमोरी आवंटित करते हैं, हमेशा तब भी जब हम इसे महसूस नहीं करते हैं - स्मार्ट पॉइंटर के साथ एक और तरीका करना मुश्किल है। यदि ऐसा नहीं हो रहा था, तो आप NULL मेमोरी तक पहुंचने का प्रयास करेंगे, जिसके परिणामस्वरूप दुर्घटना (बहुत दर्दनाक) होगी।
  2. त्रुटि होने पर भी हम स्मृति मुक्त करते हैं। कोई स्मृति लटकी नहीं है।

उदाहरण के लिए, एक और उदाहरण नेटवर्क सॉकेट RAII है। इस मामले में:

  1. हम नेटवर्क सॉकेट को खोलने से पहले उसका उपयोग करते हैं, हमेशा, तब भी जब हम ऐसा महसूस नहीं करते हैं - यह RAII के साथ एक और तरीका करना मुश्किल है। यदि आप RAII के बिना ऐसा करने का प्रयास करते हैं, तो आप MSN कनेक्शन के लिए खाली सॉकेट खोल सकते हैं। फिर "आज रात ऐसा करने दें" जैसे संदेश हस्तांतरित नहीं हो सकते हैं, उपयोगकर्ता लेट नहीं होंगे, और आपको निकाल दिए जाने का जोखिम हो सकता है।
  2. त्रुटि होने पर भी हम नेटवर्क सॉकेट बंद कर देते हैं । कोई भी सॉकेट लटका हुआ नहीं है क्योंकि इससे प्रतिक्रिया संदेश "प्रेषक को नीचे से बीमार होने" पर रोक सकता है।

अब, जैसा कि आप देख सकते हैं, RAII ज्यादातर मामलों में एक बहुत ही उपयोगी उपकरण है क्योंकि यह लोगों को ढलने में मदद करता है।

C ++ स्मार्ट पॉइंटर्स के स्रोत नेट पर लाखों में हैं, जिनमें मेरे ऊपर प्रतिक्रियाएं भी शामिल हैं।


2

Boost में Boost.Interprocess की साझा मेमोरी के लिए इनमें से कई शामिल हैं । यह स्मृति प्रबंधन को बहुत सरल करता है, विशेष रूप से सिरदर्द-उत्पीड़क स्थितियों में जैसे कि जब आपके पास एक ही डेटा संरचना को साझा करने वाली 5 प्रक्रियाएं होती हैं: जब हर कोई स्मृति के साथ काम करता है, तो आप चाहते हैं कि यह स्वचालित रूप से मुक्त हो जाए और वहां बैठने की कोशिश न करें। जो deleteस्मृति के एक टुकड़े पर कॉल करने के लिए ज़िम्मेदार होना चाहिए , ऐसा न हो कि आप स्मृति रिसाव के साथ समाप्त हो जाएं, या एक संकेतक जो दो बार गलती से मुक्त हो जाए और पूरे ढेर को दूषित कर सके।


0
शून्य फू ()
{
   std :: string bar;
   //
   // यहाँ और अधिक कोड
   //
}

कोई भी बात नहीं है, क्योंकि फू () फ़ंक्शन के दायरे को पीछे छोड़ दिया गया है, बार को ठीक से हटाया जा रहा है।

आंतरिक रूप से std :: string कार्यान्वयन अक्सर संदर्भ गिने बिंदुओं का उपयोग करते हैं। इसलिए आंतरिक स्ट्रिंग को केवल तब कॉपी करने की आवश्यकता होती है जब स्ट्रिंग्स की एक कॉपी बदल जाती है। इसलिए एक संदर्भ जो स्मार्ट पॉइंटर गिना जाता है, केवल आवश्यक होने पर कुछ कॉपी करना संभव बनाता है।

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


1
void f () {ओबज x; } ओब्ज x स्टैक फ्रेम निर्माण / विनाश (अनइंडिंग) के माध्यम से हटा दिया जाता है ... यह रेफरी गिनती से संबंधित नहीं है।
हर्नान

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

1
"कोई फर्क नहीं पड़ता कि क्या होता है" - फ़ंक्शन के वापस आने से पहले एक अपवाद को फेंक दिया जाए तो क्या होगा?
टाइटेनियमडेकॉय

कौन सा फ़ंक्शन वापस किया गया है? यदि एक अपवाद को फू में फेंक दिया जाता है, तो बार को हटा दिया जाता है। अपवाद को फेंकने वाले बार का डिफ़ॉल्ट निर्माण एक असाधारण घटना होगी।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.