रिसोर्स एक्विजिशन का क्या मतलब है इनिशियलाइजेशन (RAII)?


283

रिसोर्स एक्विजिशन का क्या मतलब है इनिशियलाइजेशन (RAII)?



13
यह वही है जो मेरे लिए घर चलाती है। stroustrup.com/bs_faq2.html#finally
हाल कैनरी

2
3 वाक्यों और 2 उदाहरणों के साथ Microsoft संदर्भ अभी तक बहुत स्पष्ट है! msdn.microsoft.com/en-us/library/hh438480.aspx
Gab 是

जवाबों:


374

यह अविश्वसनीय रूप से शक्तिशाली अवधारणा के लिए वास्तव में भयानक नाम है, और शायद नंबर 1 चीजों में से एक है जो सी ++ डेवलपर्स को याद करते हैं जब वे अन्य भाषाओं पर स्विच करते हैं। इस अवधारणा का नाम बदलकर स्कोप-बाउंड रिसोर्स मैनेजमेंट के रूप में स्थापित करने की कोशिश की गई है , हालांकि यह अभी तक पकड़ा नहीं गया है।

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

RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation();  // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks

RAII के साथ

class ManagedResourceHandle {
public:
   ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
   ~ManagedResourceHandle() {delete rawHandle; }
   ... // omitted operator*, etc
private:
   RawResourceHandle* rawHandle;
};

ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();

इस उत्तरार्द्ध मामले में, जब अपवाद को फेंक दिया जाता है और स्टैक निराधार होता है, तो स्थानीय चर नष्ट हो जाते हैं जो सुनिश्चित करता है कि हमारा संसाधन साफ ​​हो गया है और रिसाव नहीं करता है।


2
@the_mandrill: मैंने इस कार्यक्रम में ideone.com/1Jjzuc की कोशिश की। लेकिन कोई विध्वंसक कॉल नहीं है। Tomdalling.com/blog/software-design/… का कहना है कि C ++ गारंटी देता है कि स्टैक पर वस्तुओं के विनाशकर्ता को बुलाया जाएगा, भले ही एक अपवाद फेंक दिया गया हो। तो, क्यों विध्वंसक यहाँ निष्पादित नहीं किया? क्या मेरा संसाधन लीक हो गया है या इसे कभी मुक्त या जारी नहीं किया जाएगा?
विध्वंसक

8
एक अपवाद फेंक दिया गया है, लेकिन आप इसे नहीं पकड़ रहे हैं, इसलिए एप्लिकेशन समाप्त हो जाता है। यदि आप एक कोशिश {} कैच () {} के साथ लपेटते हैं तो यह अपेक्षा के अनुरूप काम करता है: ideone.com/xm2GR9
the_mandrill

2
यह निश्चित नहीं है कि यदि Scope-Boundस्टोरेज स्पेसिफिकेशंस के साथ सबसे अच्छा नाम पसंद है , तो गुंजाइश के साथ एक इकाई की भंडारण अवधि निर्धारित करें। स्कोप-बाउंड करने के लिए इसे कम करना एक उपयोगी सरलीकरण है, हालांकि यह 100% सटीक नहीं है
सेबनाग

125

यह एक प्रोग्रामिंग मुहावरा है जिसका संक्षिप्त अर्थ है कि आप

  • एक संसाधन को एक वर्ग में विभाजित करें (जिसका निर्माता आमतौर पर - लेकिन जरूरी नहीं कि ** - संसाधन का अधिग्रहण करता है, और इसका विध्वंसक रिलीज करता है)
  • कक्षा के एक स्थानीय उदाहरण के माध्यम से संसाधन का उपयोग करें *
  • संसाधन स्वतः मुक्त हो जाता है जब ऑब्जेक्ट गुंजाइश से बाहर हो जाता है

यह गारंटी देता है कि संसाधन के उपयोग के दौरान जो कुछ भी होता है, वह अंततः मुक्त हो जाएगा (चाहे सामान्य वापसी के कारण, युक्त वस्तु का विनाश, या एक अपवाद फेंक दिया गया हो)।

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

* अद्यतन: "स्थानीय" का अर्थ स्थानीय चर या किसी वर्ग का गैर-स्थिर सदस्य चर हो सकता है। बाद के मामले में सदस्य चर को उसके मालिक ऑब्जेक्ट के साथ आरंभीकृत और नष्ट कर दिया जाता है।

** Update2: जैसा कि @sbi ने बताया, संसाधन - हालांकि अक्सर कंस्ट्रक्टर के अंदर आवंटित किया जाता है - बाहर भी आवंटित किया जा सकता है और एक पैरामीटर के रूप में पारित किया जा सकता है।


1
AFAIK, संक्षिप्त का अर्थ यह नहीं है कि वस्तु स्थानीय (स्टैक) चर पर होनी चाहिए। यह किसी अन्य ऑब्जेक्ट का सदस्य चर हो सकता है, इसलिए जब 'होल्डिंग' ऑब्जेक्ट नष्ट हो जाता है, तो सदस्य ऑब्जेक्ट भी नष्ट हो जाता है, और संसाधन जारी किया जाता है। वास्तव में, मुझे लगता है कि विशेष रूप से इसका मतलब केवल यह है कि संसाधन को शुरू करने और जारी करने का कोई तरीका open()/ close()तरीका नहीं है , बस कंस्ट्रक्टर और विध्वंसक है, इसलिए संसाधन का 'धारण' सिर्फ जीवनकाल है, चाहे वह जीवनकाल हो संदर्भ (स्टैक) या स्पष्ट रूप से (गतिशील आवंटन) द्वारा संभाला
जेवियर

1
वास्तव में कुछ भी नहीं कहता है कि निर्माणकर्ता में संसाधन का अधिग्रहण किया जाना चाहिए। फाइल स्ट्रीम्स, स्ट्रिंग्स एक अन्य कंटेनरों को करते हैं, लेकिन संसाधन बस कंस्ट्रक्टर को पास किया जा सकता है , जैसा कि आमतौर पर स्मार्ट पॉइंटर्स के साथ होता है। चूँकि आपका सबसे उत्कट उत्तर है, आप इसे ठीक करना चाह सकते हैं।
sbi

यह एक संक्षिप्त नाम नहीं है, यह एक संक्षिप्त नाम है। IIRC ज्यादातर लोग इसे "ar ey ay ay" उच्चारण करते हैं, इसलिए यह वास्तव में DARPA जैसे एक संक्षिप्त नाम के लिए योग्य नहीं है, जिसे वर्तनी के बजाय DARPA घोषित किया जाता है। इसके अलावा, मैं कहूंगा कि RAII एक मुहावरे के बजाय एक प्रतिमान है।
dtech

@Peter Torok: मैंने इस कार्यक्रम में ideone.com/1Jjzuc की कोशिश की । लेकिन कोई विध्वंसक कॉल नहीं है। Tomdalling.com/blog/software-design/... का कहना है कि सी ++ गारंटी देता है कि स्टैक पर वस्तुओं की नाशक कहा जाएगा, भले ही एक अपवाद फेंक दिया है। तो, क्यों विध्वंसक यहाँ निष्पादित नहीं किया? क्या मेरा संसाधन लीक हो गया है या इसे कभी मुक्त या जारी नहीं किया जाएगा?
विध्वंसक

50

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

इसके बहुत ही दिल में, मुहावरे में स्थानीय, स्वचालित वस्तुओं में रिसोर्सेसिंग संसाधन (मेमोरीज़, ओपन फाइल्स, अनलॉक्ड म्यूटेक्स, यू-नेम-इट) होते हैं , और ऑब्जेक्ट के नष्ट होने पर रिसोर्स को रिलीज़ करने वाले ऑब्जेक्ट को नष्ट करने की सुविधा होती है। इसके अंतर्गत आने वाले दायरे का अंत:

{
  raii obj(acquire_resource());
  // ...
} // obj's dtor will call release_resource()

बेशक, ऑब्जेक्ट हमेशा स्थानीय, स्वचालित ऑब्जेक्ट नहीं होते हैं। वे एक वर्ग के सदस्य भी हो सकते हैं:

class something {
private:
  raii obj_;  // will live and die with instances of the class
  // ... 
};

यदि ऐसी वस्तुएं स्मृति का प्रबंधन करती हैं, तो उन्हें अक्सर "स्मार्ट पॉइंटर्स" कहा जाता है।

इसके कई रूप हैं। उदाहरण के लिए, पहले कोड में स्निपेट पर सवाल उठता है कि अगर कोई नकल करना चाहता है तो क्या होगा obj। सबसे आसान तरीका यह होगा कि बस नकल को खारिज कर दिया जाए। std::unique_ptr<>, एक स्मार्ट पॉइंटर जो अगले सी ++ मानक द्वारा चित्रित मानक पुस्तकालय का हिस्सा है, ऐसा करता है।
इस तरह के एक अन्य स्मार्ट पॉइंटर, std::shared_ptrसंसाधन का "साझा स्वामित्व" (एक गतिशील रूप से आवंटित वस्तु) है। यही है, इसे स्वतंत्र रूप से कॉपी किया जा सकता है और सभी प्रतियां एक ही ऑब्जेक्ट को संदर्भित करती हैं। स्मार्ट पॉइंटर इस बात पर नज़र रखता है कि कितनी प्रतियाँ एक ही ऑब्जेक्ट को संदर्भित करती हैं और पिछले एक के नष्ट होने पर उसे हटा देगी।
एक तीसरा संस्करण द्वारा चित्रित किया गया हैstd::auto_ptr जो एक प्रकार के चाल-शब्दार्थ को कार्यान्वित करता है: एक वस्तु केवल एक सूचक के स्वामित्व में होती है, और किसी वस्तु की प्रतिलिपि बनाने का प्रयास करने से वस्तु का स्वामित्व (प्रति सिंटैक्स हैकरी के माध्यम से) कॉपी संचालन के लक्ष्य तक पहुंच जाएगा।


4
std::auto_ptrका अप्रचलित संस्करण है std::unique_ptrstd::auto_ptrC ++ 98 में जितना संभव हो उतना नकली चालित std::unique_ptrशब्दार्थ, C ++ 11 के नए चाल शब्दार्थ का उपयोग करता है। नया वर्ग बनाया गया था क्योंकि C ++ 11 का चाल शब्दार्थ अधिक स्पष्ट है ( std::moveअस्थायी को छोड़कर) की आवश्यकता है, जबकि इसे गैर-कास्ट से किसी भी प्रतिलिपि के लिए डिफ़ॉल्ट किया गया था std::auto_ptr
Jan Hudec

@ जियाओकै: एक बार, कई साल पहले (यूज़नेट पर), स्ट्रॉस्ट्रुप ने खुद ऐसा कहा था।
sbi

21

किसी वस्तु का जीवनकाल उसके दायरे से निर्धारित होता है। हालांकि, कभी-कभी हमें ज़रूरत होती है, या यह उपयोगी है, एक ऐसी वस्तु बनाने के लिए जो उस दायरे से स्वतंत्र रूप से रहती है जहां इसे बनाया गया था। C ++ में, ऑपरेटर newका उपयोग ऐसी वस्तु बनाने के लिए किया जाता है। और ऑब्जेक्ट को नष्ट करने के लिए, ऑपरेटर deleteका उपयोग किया जा सकता है। ऑपरेटर द्वारा बनाई गई वस्तुओं newको गतिशील रूप से आवंटित किया जाता है, अर्थात गतिशील मेमोरी में आवंटित किया जाता है (जिसे हीप या फ्री स्टोर भी कहा जाता है )। इसलिए, newतब तक बनाई गई एक वस्तु मौजूद रहेगी, जब तक कि वह स्पष्ट रूप से उपयोग करके नष्ट न हो जाए delete

कुछ गलतियाँ जो उपयोग करते समय हो सकती हैं newऔर deleteहैं:

  • लीक हुई वस्तु (या मेमोरी): newकिसी ऑब्जेक्ट को आवंटित करने और ऑब्जेक्ट को भूलने के लिए उपयोग करना delete
  • समयपूर्व हटाएं (या झूलने का संदर्भ ): किसी ऑब्जेक्ट, ऑब्जेक्ट के लिए किसी अन्य पॉइंटर को पकड़ना deleteऔर फिर दूसरे पॉइंटर का उपयोग करना।
  • डबल डिलीट : deleteकिसी ऑब्जेक्ट को दो बार करने की कोशिश करना ।

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

RAII का उपयोग करने वाले C ++ मानक पुस्तकालय के उदाहरण हैं std::stringऔर std::vector

इस कोड के टुकड़े पर विचार करें:

void fn(const std::string& str)
{
    std::vector<char> vec;
    for (auto c : str)
        vec.push_back(c);
    // do something
}

जब आप एक वेक्टर बनाते हैं और आप इसे तत्वों को धक्का देते हैं, तो आप ऐसे तत्वों को आवंटित करने और उनसे निपटने के बारे में परवाह नहीं करते हैं। वेक्टर newढेर पर अपने तत्वों के लिए जगह आवंटित करने के लिए और deleteउस स्थान को मुक्त करने के लिए उपयोग करता है। आप वेक्टर के एक उपयोगकर्ता के रूप में जिसे आप कार्यान्वयन विवरण के बारे में परवाह नहीं करते हैं और वेक्टर को लीक नहीं होने पर भरोसा करेंगे। इस मामले में, वेक्टर अपने तत्वों का हैंडल ऑब्जेक्ट है।

मानक पुस्तकालय से अन्य उदाहरण है कि उपयोग आरए II हैं std::shared_ptr, std::unique_ptr, और std::lock_guard

इस तकनीक का दूसरा नाम SBRM है , जो स्कोप-बाउंड रिसोर्स मैनेजमेंट के लिए छोटा है ।


1
"एसबीआरएम" मेरे लिए बहुत मायने रखता है। मैं इस सवाल पर आया क्योंकि मुझे लगा कि मैं आरएआई को समझ गया था लेकिन नाम मुझे फेंक रहा था, "स्कोप-बाउंड रिसोर्स मैनेजमेंट" के बजाय इसे सुनकर मुझे तुरंत एहसास हुआ कि मैं वास्तव में अवधारणा को समझ गया हूं।
JShorthouse

मुझे यकीन नहीं है कि इस प्रश्न के उत्तर के रूप में इसे चिह्नित क्यों नहीं किया गया था। यह एक बहुत ही गहन और अच्छी तरह से लिखा जवाब है, धन्यवाद @elmiomar
अब्दलरहमान Shoman

13

पुस्तक सी + + डिजाइन पैटर्न के साथ प्रोग्रामिंग पता चला RAII के रूप में वर्णन करता है:

  1. सभी संसाधनों को हासिल करना
  2. संसाधनों का उपयोग करना
  3. संसाधन जारी करना

कहाँ पे

  • संसाधनों को कक्षाओं के रूप में लागू किया जाता है, और सभी पॉइंटर्स के चारों ओर क्लास रैपर होते हैं (उन्हें स्मार्ट पॉइंटर्स बनाते हैं)।

  • संसाधनों को उनके निर्माणकर्ताओं को आमंत्रित करके प्राप्त किया जाता है और उनके विध्वंसकों को आमंत्रित करके (प्राप्त करने के रिवर्स ऑर्डर में) जारी किया जाता है।


1
@Brandin मैंने अपनी पोस्ट को संपादित किया है ताकि पाठक उस सामग्री पर ध्यान केंद्रित करें, जो कॉपीराइट कानून के ग्रे क्षेत्र पर बहस करने के बजाय, जो उचित उपयोग का गठन करता है।
डेनिस

7

RAII वर्ग के तीन भाग हैं:

  1. संसाधन को विध्वंसक में छोड़ दिया जाता है
  2. वर्ग के उदाहरणों का आवंटन किया जाता है
  3. कंस्ट्रक्टर में संसाधन का अधिग्रहण किया जाता है। यह हिस्सा वैकल्पिक है, लेकिन आम है।

RAII का अर्थ है "संसाधन प्राप्ति प्रारंभिक है।" RAII का "संसाधन अधिग्रहण" भाग वह है जहाँ आप कुछ ऐसा शुरू करते हैं जिसे बाद में समाप्त किया जाना चाहिए, जैसे:

  1. एक फ़ाइल खोलना
  2. कुछ स्मृति आवंटित करना
  3. एक ताला प्राप्त करना

"इनिशियलाइज़ेशन" भाग का मतलब है कि अधिग्रहण एक वर्ग के कंस्ट्रक्टर के अंदर होता है।

https://www.tomdalling.com/blog/software-design/resource-acquisition-is-initialisation-raii-explained/


5

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

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