शब्द और अवधारणा के अर्थ को समझना - RAII (संसाधन अधिग्रहण प्रारंभिक है)


110

क्या आप C ++ डेवलपर्स हमें एक अच्छा विवरण दे सकते हैं कि RAII क्या है, यह क्यों महत्वपूर्ण है, और अन्य भाषाओं के लिए इसकी कोई प्रासंगिकता हो सकती है या नहीं?

मैं कर एक छोटा सा पता है। मेरा मानना ​​है कि यह "संसाधन अधिग्रहण प्रारंभिक है" के लिए खड़ा है। हालाँकि, वह नाम मेरे (संभवतः गलत) के साथ समझ में नहीं आता है कि RAII क्या है: मुझे यह आभास होता है कि RAII स्टैक पर वस्तुओं को इनिशियलाइज़ करने का एक तरीका है जैसे कि, जब वे चर दायरे से बाहर जाते हैं, तो विध्वंसक स्वतः कहा जाता है कि संसाधनों को साफ किया जाना चाहिए।

तो क्यों नहीं कहा जाता है कि "स्टैक को ट्रिगर क्लीनअप का उपयोग करना" (UTSTTC :)? आप वहां से "RAII" में कैसे पहुंचेंगे?

और आप ढेर पर कुछ कैसे बना सकते हैं जो ढेर पर रहने वाली किसी चीज की सफाई का कारण बन जाएगा? इसके अलावा, क्या ऐसे मामले हैं जहां आप RAII का उपयोग नहीं कर सकते हैं? क्या आप कभी भी अपने आप को कचरा संग्रहण की इच्छा रखते हैं? कम से कम एक कचरा संग्रहकर्ता जो आप कुछ वस्तुओं के लिए उपयोग कर सकते हैं जबकि दूसरों को प्रबंधित करने की अनुमति दें?

धन्यवाद।


27
UTSTTC? मुझें यह पसंद है! यह RAII की तुलना में बहुत अधिक सहज है। RAII का नाम बुरी तरह से है, मुझे संदेह है कि कोई भी C ++ प्रोग्रामर इस पर विवाद करेगा। लेकिन इसे बदलना आसान नहीं है। ;)
जुलफ

10
इस मामले पर स्ट्रॉस्ट्रुप की राय देखें: group.google.com/group/comp.lang.c++.moderated/msg/…
sbi

3
@sbi: वैसे भी, केवल ऐतिहासिक शोध के लिए आपकी टिप्पणी पर +1। मेरा मानना ​​है कि एक अवधारणा के नाम (RAII) पर लेखक का (बी। स्ट्रॉस्ट्रुप) का दृष्टिकोण अपने स्वयं के उत्तर के लिए काफी दिलचस्प है।
पियरसबल

1
@ अंपेरेसल: ऐतिहासिक शोध? अब आपने मुझे बहुत बूढ़ा बना दिया है। :(मैं पूरे धागे को पढ़ रहा था, फिर वापस, और यहां तक ​​कि खुद को सी ++ नौसिखिया नहीं माना!
sbi

3
+1, मैं एक ही सवाल पूछने वाला था, खुशी है कि मैं केवल वही नहीं हूं, जो अवधारणा को समझता है, लेकिन नाम का कोई मतलब नहीं है। लगता है इसे RAOI - रिसोर्स एक्विजिशन ऑन इनिशिएटिव कहा जाना चाहिए था।
laurent

जवाबों:


132

तो क्यों नहीं कहा जाता है कि "स्टैक को ट्रिगर क्लीनअप का उपयोग करना" (UTSTTC :)?

RAII आपको बता रहा है कि क्या करना है: एक कंस्ट्रक्टर में अपने संसाधन प्राप्त करें! मैं जोड़ूंगा: एक संसाधन, एक निर्माणकर्ता। UTSTTC उस का केवल एक अनुप्रयोग है, RAII बहुत अधिक है।

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

C ++ में, अपवाद और (C ++ शैली) टेम्पलेट के संयोजन के कारण संसाधन प्रबंधन विशेष रूप से जटिल है। हुड के नीचे एक तिरछी नज़र के लिए, GOTW8 देखें )।


सी ++ गारंटी देता है कि विध्वंसक को कहा जाता है यदि और केवल तभी निर्माणकर्ता सफल हुआ। उस पर भरोसा करते हुए, RAII कई बुरा समस्याओं को हल कर सकता है जो औसत प्रोग्रामर को भी पता नहीं हो सकता है। यहां "मेरे स्थानीय चर जब भी मैं वापस लौटेंगे तब नष्ट हो जाएंगे" से परे कुछ उदाहरण हैं।

आइए हम FileHandleआरएआई को रोजगार देने वाले एक अति सरल वर्ग के साथ शुरू करें :

class FileHandle
{
    FILE* file;

public:

    explicit FileHandle(const char* name)
    {
        file = fopen(name);
        if (!file)
        {
            throw "MAYDAY! MAYDAY";
        }
    }

    ~FileHandle()
    {
        // The only reason we are checking the file pointer for validity
        // is because it might have been moved (see below).
        // It is NOT needed to check against a failed constructor,
        // because the destructor is NEVER executed when the constructor fails!
        if (file)
        {
            fclose(file);
        }
    }

    // The following technicalities can be skipped on the first read.
    // They are not crucial to understanding the basic idea of RAII.
    // However, if you plan to implement your own RAII classes,
    // it is absolutely essential that you read on :)



    // It does not make sense to copy a file handle,
    // hence we disallow the otherwise implicitly generated copy operations.

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;



    // The following operations enable transfer of ownership
    // and require compiler support for rvalue references, a C++0x feature.
    // Essentially, a resource is "moved" from one object to another.

    FileHandle(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
    }

    FileHandle& operator=(FileHandle&& that)
    {
        file = that.file;
        that.file = 0;
        return *this;
    }
}

यदि निर्माण विफल रहता है (अपवाद के साथ), कोई अन्य सदस्य कार्य नहीं - विध्वंसक भी नहीं - कहा जाता है।

RAII अमान्य स्थिति में वस्तुओं का उपयोग करने से बचता है। यह पहले से ही जीवन को आसान बनाता है इससे पहले कि हम वस्तु का उपयोग करें।

अब, हम अस्थायी वस्तुओं पर एक नज़र डालते हैं:

void CopyFileData(FileHandle source, FileHandle dest);

void Foo()
{
    CopyFileData(FileHandle("C:\\source"), FileHandle("C:\\dest"));
}

हैंडल करने के लिए तीन त्रुटि मामले हैं: कोई भी फाइल नहीं खोली जा सकती है, केवल एक फाइल को खोला जा सकता है, दोनों फाइलें खोली जा सकती हैं लेकिन फाइलों की नकल करना विफल हो गया है। गैर-आरआईआई कार्यान्वयन में, Fooस्पष्ट रूप से सभी तीन मामलों को संभालना होगा।

RAII संसाधनों को जारी करता है, जिसे तब प्राप्त किया गया था, जब एक बयान में कई संसाधन हासिल किए गए हों।

अब, कुछ वस्तुओं को एकत्र करते हैं:

class Logger
{
    FileHandle original, duplex;   // this logger can write to two files at once!

public:

    Logger(const char* filename1, const char* filename2)
    : original(filename1), duplex(filename2)
    {
        if (!filewrite_duplex(original, duplex, "New Session"))
            throw "Ugh damn!";
    }
}

के निर्माता Loggerयदि असफल हो जायेगी originalकी निर्माता विफल रहता है (क्योंकि filename1खोला नहीं जा सका), duplexके निर्माता विफल रहता है (क्योंकि filename2खोला नहीं जा सका), या फ़ाइलों के अंदर करने के लिए लिख Loggerके निर्माता शरीर विफल रहता है। इनमें से किसी भी मामले में, Loggerविध्वंसक को नहीं बुलाया जाएगा - इसलिए हम Loggerफाइलों को जारी करने के लिए विध्वंसक पर भरोसा नहीं कर सकते । लेकिन अगर originalनिर्माण किया गया था, तो इसके विध्वंसक को Loggerनिर्माणकर्ता की सफाई के दौरान कहा जाएगा ।

RAII आंशिक निर्माण के बाद सफाई को सरल बनाता है।


नकारात्मक अंक:

नकारात्मक अंक? सभी समस्याओं को RAII और स्मार्ट पॉइंटर्स के साथ हल किया जा सकता है ;-)

RAII को कभी-कभी तब नुकसान होता है जब आपको विलंबित अधिग्रहण की आवश्यकता होती है, एकत्रित वस्तुओं को ढेर पर धकेलता है।
कल्पना कीजिए कि लकड़हारे की जरूरत है SetTargetFile(const char* target)। उस मामले में, संभाल, कि अभी भी एक सदस्य होने की Loggerजरूरत है, हीप पर निवास करने की आवश्यकता है (उदाहरण के लिए एक स्मार्ट सूचक में, संभाल के विनाश को उचित रूप से ट्रिगर करने के लिए।)

मैंने वास्तव में कचरा संग्रहण की कभी कामना नहीं की है। जब मैं सी # करता हूं तो मुझे कभी-कभी आनंद का क्षण महसूस होता है कि मुझे सिर्फ देखभाल करने की आवश्यकता नहीं है, लेकिन बहुत अधिक मुझे सभी शांत खिलौने याद आते हैं जो नियतात्मक विनाश के माध्यम से बनाए जा सकते हैं। ( IDisposableसिर्फ इस्तेमाल करने से इसमें कटौती नहीं होती है।)

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


फ़ाइलहैंडल नमूने पर एक नोट: यह पूरा होने का इरादा नहीं था, सिर्फ एक नमूना - लेकिन गलत निकला। धन्यवाद जोहान्स स्काउब को इंगित करने के लिए और इसे सही C ++ 0x समाधान में बदलने के लिए FredOverflow। समय के साथ, मैं यहाँ प्रलेखित दृष्टिकोण के साथ बस गया हूँ ।


1
+1 इंगित करने के लिए कि GC और ASAP जाली नहीं लगाते हैं। अक्सर चोट नहीं करता है, लेकिन जब यह निदान करना आसान नहीं होता है: /
मैथ्यू एम।

10
विशेष रूप से एक वाक्य जिसे मैंने पहले पढ़ लिया था, उस पर ध्यान नहीं दिया। आपने कहा कि "RAII" आपको बता रहा है, "अपने संसाधनों को निर्माणकर्ताओं के अंदर हासिल करें।" यह समझ में आता है और लगभग शब्द "RAII" का एक शब्द है। अब मैं इसे और भी बेहतर कर सकता हूं (अगर मैं कर सकता हूं तो मैं आपको फिर से वोट
चार्ली फूल

2
जीसी का एक प्रमुख लाभ यह है कि एक मेमोरी आवंटन फ्रेमवर्क "असुरक्षित" कोड की अनुपस्थिति में झूलने वाले संदर्भों के निर्माण को रोक सकता है (यदि "असुरक्षित" कोड की अनुमति है, तो निश्चित रूप से, फ्रेमवर्क कुछ भी नहीं रोक सकता है)। जीसी भी अक्सर आरएआई से बेहतर होता है जब साझा किए गए अपरिवर्तनीय वस्तुओं जैसे तारों से निपटना होता है, जिसमें अक्सर कोई स्पष्ट स्वामी नहीं होता है और सफाई की आवश्यकता नहीं होती है। यह दुर्भाग्यपूर्ण है कि अधिक चौखटे जीसी और आरएआई को संयोजित करने की आवश्यकता नहीं है, क्योंकि अधिकांश अनुप्रयोगों में अपरिवर्तनीय वस्तुओं (जहां जीसी सबसे अच्छा होगा) और क्लीनअप (जहां आरएआईआई सबसे अच्छा है) की आवश्यकता होती है।
सुपरकैट

@ सुपरकैट: मुझे आम तौर पर जीसी पसंद है - लेकिन यह केवल जीसी "समझता" के लिए काम करता है। उदाहरण के लिए .NET GC, COM ऑब्जेक्ट्स की लागत नहीं जानता है। बस उन्हें एक लूप में बनाते और नष्ट करते समय, यह खुशी से पता स्थान या आभासी मेमोरी के बारे में आवेदन को जमीन में चला देगा - जो भी पहले आता है - शायद जीसी करने के बारे में भी सोचे बिना। --- इसके अलावा, यहां तक ​​कि एक पूरी तरह से GC'd वातावरण में, मैं अभी भी नियतात्मक विनाश की शक्ति को याद करता हूं: आप अन्य कलाकृतियों के लिए एक ही पैटर्न लागू कर सकते हैं, जैसे कि सर्टिफिकेट शर्तों के तहत UI तत्व दिखाते हैं।
पीटरचेन

@peterchen: मुझे लगता है कि ओओपी-संबंधित सोच के एक बहुत अनुपस्थित वस्तु वस्तु स्वामित्व की अवधारणा है। स्वामित्व का ट्रैक रखना संसाधनों के साथ वस्तुओं के लिए अक्सर स्पष्ट रूप से आवश्यक होता है, लेकिन अक्सर संसाधनों के बिना पारस्परिक वस्तुओं के लिए भी आवश्यक होता है। सामान्य तौर पर, वस्तुओं को संभवतः या साझा की जाने वाली अपरिवर्तनीय वस्तुओं के संदर्भ में, या जिनके वे अनन्य स्वामी होते हैं, के संदर्भ में उनकी उत्परिवर्तनीय स्थिति को संक्षिप्त करना चाहिए। इस तरह के अनन्य स्वामित्व के लिए विशेष रूप से विशेष रूप से लिखने का उपयोग आवश्यक नहीं है, लेकिन अगर Fooमालिक है Bar, और Bozइसे उत्परिवर्तित करता है, ...
सुपरकैट

42

वहाँ उत्कृष्ट उत्तर हैं, इसलिए मैं अभी कुछ चीजें भूल गया हूं।

0. RAII स्कोप के बारे में है

RAII दोनों के बारे में है:

  1. कंस्ट्रक्टर में रिसोर्स (चाहे कोई भी रिसोर्स) हो, और उसे डिस्ट्रक्टर में अन-एक्वायर करना।
  2. कंस्ट्रक्टर को निष्पादित किया जाता है जब चर घोषित किया जाता है, और विध्वंसक स्वचालित रूप से निष्पादित होता है जब चर दायरे से बाहर हो जाता है।

दूसरों ने पहले ही इस बारे में जवाब दिया, इसलिए मैं विस्तृत नहीं करूंगा।

1. जब जावा या C # में कोडिंग करते हैं, तो आप पहले से ही RAII का उपयोग करते हैं ...

महाशूरुद्दीन: क्या! जब मैं कहता हूं, "निकोल, मुझे मेरी चप्पल लाकर दे, और मुझे मेरी रात दे," यह गद्य है?

फिलिप मास्टर: जी, सर।

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

- मोलियार: द मिडिल क्लास जेंटलमैन, एक्ट 2, दृश्य 4

जैसा कि महाशय जर्सडेन ने गद्य के साथ किया था, C # और यहां तक ​​कि जावा के लोग पहले से ही RAII का उपयोग करते हैं, लेकिन छिपे हुए तरीकों से। उदाहरण के लिए, निम्नलिखित जावा कोड (जिसे C # के synchronizedसाथ बदलकर उसी तरह लिखा गया है lock):

void foo()
{
   // etc.

   synchronized(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

... पहले से ही RAII का उपयोग कर रहा है: म्यूटेक्स अधिग्रहण कीवर्ड ( synchronizedया lock) में किया जाता है , और गुंजाइश से बाहर निकलने पर संयुक्त अधिग्रहण किया जाएगा।

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

C ++ में जावा और C # से अधिक लाभ यह है कि RAII का उपयोग करके कुछ भी बनाया जा सकता है। उदाहरण के लिए, C ++ में synchronizedन तो कोई सीधा बिल्ड-इन समतुल्य है lock, लेकिन हम फिर भी उन्हें रख सकते हैं।

C ++ में, यह लिखा जाएगा:

void foo()
{
   // etc.

   {
      Lock lock(someObject) ; // lock is an object of type Lock whose
                              // constructor acquires a mutex on
                              // someObject and whose destructor will
                              // un-acquire it 

      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

जिसे आसानी से Java / C # तरीके से लिखा जा सकता है (C ++ macros का उपयोग करके):

void foo()
{
   // etc.

   LOCK(someObject)
   {
      // if something throws here, the lock on someObject will
      // be unlocked
   }

   // etc.
}

2. RAII के वैकल्पिक उपयोग हैं

WHITE RABBIT: [गायन] मैं देर से / बहुत देर से / बहुत महत्वपूर्ण तारीख के लिए हूँ। / "हैलो" कहने का समय नहीं है। / अलविदा। / मैं लेट हो गया, मैं लेट हो गया, लेट हो गया।

- एलिस इन वंडरलैंड (डिज्नी संस्करण, 1951)

आपको पता है कि कंस्ट्रक्टर को कब (ऑब्जेक्ट डिक्लेरेशन पर) कहा जाएगा, और आप जानते हैं कि इसके संबंधित डिस्ट्रक्टर को कब (स्कोप के बाहर निकलने पर) कहा जाएगा, इसलिए आप एक लाइन के साथ लगभग जादुई कोड लिख सकते हैं। C ++ वंडरलैंड में आपका स्वागत है (कम से कम, C ++ डेवलपर के दृष्टिकोण से)।

उदाहरण के लिए, आप एक काउंटर ऑब्जेक्ट लिख सकते हैं (मैं इसे एक अभ्यास के रूप में देता हूं) और इसके चर को घोषित करके इसका उपयोग करता हूं, जैसे कि ऊपर की लॉक ऑब्जेक्ट का उपयोग किया गया था:

void foo()
{
   double timeElapsed = 0 ;

   {
      Counter counter(timeElapsed) ;
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

जो निश्चित रूप से लिखा जा सकता है, फिर से, जावा / सी # मैक्रो का उपयोग करके रास्ता:

void foo()
{
   double timeElapsed = 0 ;

   COUNTER(timeElapsed)
   {
      // do something lengthy
   }
   // now, the timeElapsed variable contain the time elapsed
   // from the Counter's declaration till the scope exit
}

3. C ++ की कमी क्यों है finally?

[के बारे में] यह अंतिम उलटी गिनती है!

- यूरोप: अंतिम उलटी गिनती (क्षमा करें, मैं उद्धरण से बाहर था, यहाँ ... :-)

finally(या तो एक के माध्यम से खंड दायरे से बाहर निकलने के मामले में संसाधन निपटान को संभालने के लिए सी # / जावा में प्रयोग किया जाता है returnया एक फेंक दिया अपवाद)।

Astute विनिर्देशन पाठकों ने ध्यान दिया होगा C ++ में अंत में कोई खंड नहीं है। और यह कोई त्रुटि नहीं है, क्योंकि C ++ को इसकी आवश्यकता नहीं है, क्योंकि RAII पहले से ही संसाधन निपटान को संभालता है। (और मेरा विश्वास करो, सी ++ विध्वंसक लिखना सही जावा अंत में क्लॉज, या यहां तक ​​कि एक सी # सही डिस्पोज विधि लिखने से अधिक परिमाण है)।

फिर भी, कभी-कभी, एक finallyखंड शांत होगा। क्या हम इसे C ++ में कर सकते हैं? हाँ हम कर सकते हैं! और फिर से RAII के वैकल्पिक उपयोग के साथ।

निष्कर्ष: RAII C ++ में दर्शन से अधिक है: यह C ++ है

आरए II? यह सी ++ है !!!

- सी ++ डेवलपर की अपमानजनक टिप्पणी, एक अस्पष्ट स्पार्टा राजा और उसके 300 दोस्तों द्वारा बेशर्मी से नकल की गई

जब आप C ++ में अनुभव के कुछ स्तर पर पहुंच जाते हैं, तो आप RAII के संदर्भ में, बाधाओं और विनाशकारी स्वचालित निष्पादन के संदर्भ में सोचना शुरू कर देते हैं ।

आप के मामले में सोचना शुरू स्कोप , और {और }पात्रों अपने कोड में सबसे महत्वपूर्ण में से लोगों को हो गया है।

और लगभग सब कुछ RAII के संदर्भ में सही बैठता है: अपवाद सुरक्षा, म्यूटेक्स, डेटाबेस कनेक्शन, डेटाबेस अनुरोध, सर्वर कनेक्शन, घड़ियां, ओएस हैंडल, आदि, और अंतिम, लेकिन कम से कम, मेमोरी नहीं।

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

और एक पहेली की तरह, सब कुछ फिट बैठता है।

RAII C ++ का इतना हिस्सा है, C ++ इसके बिना C ++ नहीं हो सकता है।

यह बताता है कि अनुभवी C ++ डेवलपर्स RAII के साथ क्यों आसक्त हैं, और RAII पहली चीज क्यों है जो वे किसी अन्य भाषा की कोशिश करते समय खोजते हैं।

और यह बताता है कि क्यों गार्बेज कलेक्टर, जबकि प्रौद्योगिकी का एक शानदार टुकड़ा, अपने आप में C ++ के विकास के दृष्टिकोण से इतना प्रभावशाली नहीं है:

  • आरएआई पहले से ही एक जीसी द्वारा संभाले गए अधिकांश मामलों को संभालता है
  • शुद्ध प्रबंधित वस्तुओं पर गोलाकार संदर्भों के साथ एक GC रास से बेहतर व्यवहार करता है (कमजोर बिंदुओं के स्मार्ट उपयोग से कम)
  • फिर भी A GC स्मृति तक सीमित है, जबकि RAII किसी भी प्रकार के संसाधन को संभाल सकता है।
  • जैसा कि ऊपर वर्णित है, RAII बहुत कुछ कर सकता है ...

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

16

1
उनमें से कुछ मेरे प्रश्न के अनुसार सही हैं, लेकिन एक खोज ने उन्हें चालू नहीं किया, और न ही "संबंधित प्रश्न" सूची, जो आपके द्वारा एक नया प्रश्न दर्ज करने के बाद दिखाई देती है। लिंक के लिए धन्यवाद।
चार्ली फूल

1
@ चार्ली: खोज में निर्माण कुछ मायनों में बहुत कमजोर है। टैग सिंटैक्स ("[विषय]") का उपयोग करना बहुत सहायक है, और बहुत से लोग Google का उपयोग करते हैं ...
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

10

RAII संसाधनों को प्रबंधित करने के लिए C ++ विध्वंसक शब्दार्थ का उपयोग कर रहा है। उदाहरण के लिए, स्मार्ट पॉइंटर पर विचार करें। आपके पास पॉइंटर का एक पैरामीटराइज्ड कंस्ट्रक्टर है जो ऑब्जेक्ट के एड्रेस के साथ इस पॉइंटर को इनिशियलाइज़ करता है। आप ढेर पर एक सूचक आवंटित करते हैं:

SmartPointer pointer( new ObjectClass() );

जब स्मार्ट पॉइंटर दायरे से बाहर जाता है, तो पॉइंटर क्लास का डिस्ट्रक्टर कनेक्टेड ऑब्जेक्ट को हटा देता है। सूचक स्टैक-आबंटित है और ऑब्जेक्ट - हीप-आबंटित है।

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


2
तो इसे UCDSTMR कहा जाना चाहिए :)
डैनियल डारनास

एक दूसरे विचार पर, मुझे लगता है कि UDSTMR अधिक उपयुक्त है। भाषा (C ++) दी गई है, इसलिए "C" अक्षर को संक्षिप्त नाम की आवश्यकता नहीं है। UDSTMR संसाधनों को प्रबंधित करने के लिए विनाशकारी शब्दार्थ का उपयोग करने के लिए खड़ा है।
डैनियल डारनास

9

मैं इसे और अधिक मजबूती से पिछली प्रतिक्रियाओं पर रखना चाहूंगा।

RAII, रिसोर्स एक्विजिशन इनिशियलाइज़ेशन का अर्थ है कि किसी वस्तु के आरंभीकरण के संदर्भ में सभी अर्जित संसाधनों का अधिग्रहण किया जाना चाहिए। यह "नग्न" संसाधन अधिग्रहण को मना करता है। तर्क यह है कि C ++ में क्लीनअप ऑब्जेक्ट बेसिस पर काम करता है, फंक्शन-कॉल बेसिस पर नहीं। इसलिए, सभी क्लीनअप ऑब्जेक्ट्स द्वारा किए जाने चाहिए, फ़ंक्शन कॉल नहीं। इस अर्थ में C ++ अधिक-ऑब्जेक्ट ओरिएंटेड है तो उदाहरण के लिए Java। जावा क्लीनअप finallyक्लॉज़ में फ़ंक्शन कॉल पर आधारित है ।


बहुत बढ़िया जवाब। और "एक वस्तु का आरंभीकरण" का अर्थ है "बाधाएं", हाँ?
चार्ली फ्लावर्स

@Charlie: हाँ, विशेष रूप से इस मामले में।
MSalters

8

मैं कोपिटिस के साथ कॉन्सुर करता हूं। लेकिन जोड़ना चाहते हैं कि संसाधन कुछ भी नहीं हो सकता है सिर्फ स्मृति। संसाधन एक फ़ाइल, एक महत्वपूर्ण खंड, एक थ्रेड या डेटाबेस कनेक्शन हो सकता है।

इसे रिसोर्स एक्विजिशन इज़ इनिशियलाइज़ेशन कहा जाता है क्योंकि रिसोर्स को नियंत्रित करने पर ऑब्जेक्ट का निर्माण तब किया जाता है, जब कंस्ट्रक्टर विफल हो जाता है (यानी अपवाद के कारण) रिसोर्स का अधिग्रहण नहीं किया जाता है। फिर एक बार जब ऑब्जेक्ट स्कोप से बाहर निकल जाता है तो रिसोर्स रिलीज़ हो जाता है। c ++ गारंटी देता है कि जिस स्टैक पर सफलतापूर्वक निर्माण किया गया है, वह सभी वस्तुओं को नष्ट कर दिया जाएगा (इसमें बेस क्लास और सदस्यों के कंस्ट्रक्टर शामिल हैं, भले ही सुपर क्लास कंस्ट्रक्टर विफल हो जाए)।

RAII के पीछे तर्क संसाधन अधिग्रहण अपवाद को सुरक्षित बनाना है। अधिग्रहण किए गए सभी संसाधनों को ठीक से जारी नहीं किया जाता है, जहां कोई अपवाद नहीं होता है। हालाँकि यह उस वर्ग की गुणवत्ता पर निर्भर करता है जो संसाधन प्राप्त करता है (यह अपवाद सुरक्षित होना चाहिए और यह कठिन है)।


बहुत बढ़िया, नाम के पीछे के तर्क को समझाने के लिए धन्यवाद। जैसा कि मैंने इसे समझा है, आप RAII को समझ सकते हैं, "किसी भी अन्य तंत्र के माध्यम से किसी भी संसाधन को हासिल न करें (निर्माण-आधारित) आरंभीकरण"। हाँ?
चार्ली फूल

हां, यह मेरी नीति है, हालांकि मैं अपनी RAII कक्षाएं लिखने से बहुत सावधान हूं क्योंकि उन्हें अपवाद सुरक्षित होना चाहिए। जब मैं उन्हें लिखता हूं तो मैं विशेषज्ञों द्वारा लिखित अन्य RAII कक्षाओं का पुन: उपयोग करके अपवाद सुरक्षा सुनिश्चित करने का प्रयास करता हूं।
आईएन

मैंने उन्हें लिखना मुश्किल नहीं समझा। यदि आपकी कक्षाएं ठीक से छोटी हैं, तो वे बिल्कुल भी मुश्किल नहीं हैं।
रॉब के

7

कचरा संग्रहण के साथ समस्या यह है कि आप निर्धारक विनाश को खो देते हैं जो RAII के लिए महत्वपूर्ण है। एक बार एक चर दायरे से बाहर चला जाता है, तो यह कचरा कलेक्टर तक है जब ऑब्जेक्ट को पुनः प्राप्त किया जाएगा। ऑब्जेक्ट द्वारा आयोजित संसाधन को तब तक आयोजित किया जाएगा जब तक कि विध्वंसक को बुलाया नहीं जाता।


4
समस्या केवल नियतत्ववाद की नहीं है। असली समस्या यह है कि फाइनल करने वाले (जावा नामकरण) जीसी के रास्ते में आते हैं। जीसी कुशल है क्योंकि यह मृत वस्तुओं को याद नहीं करता है, बल्कि उन्हें गुमनामी में अनदेखा करता है। GCs को अंतिम रूप देने के लिए एक अलग तरीके से वस्तुओं को ट्रैक करना चाहिए, जिसे वे कहते हैं
डेविड रॉड्रिग्ज़ - dribeas

1
java / c # को छोड़कर आप शायद फाइनल में आने के बजाय अंततः ब्लॉक में सफाई करेंगे।
जे.के.

4

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

कचरा एकत्र भाषाओं / प्रौद्योगिकियों (जैसे जावा, .NET) की तुलना में, C ++ किसी वस्तु के जीवन पर पूर्ण नियंत्रण की अनुमति देता है। स्टैक आबंटित ऑब्जेक्ट के लिए, आपको पता चल जाएगा कि ऑब्जेक्ट का विनाशकर्ता कहा जाएगा (जब निष्पादन गुंजाइश से बाहर हो जाता है), वह चीज जो कचरा संग्रह के मामले में वास्तव में नियंत्रित नहीं है। यहां तक ​​कि C ++ में स्मार्ट पॉइंटर्स का उपयोग करना (जैसे कि बढ़ावा :: साझा_प्ट्र), आपको पता चल जाएगा कि जब इंगित ऑब्जेक्ट का कोई संदर्भ नहीं है, तो उस ऑब्जेक्ट का विध्वंसक कहा जाएगा।


3

और आप ढेर पर कुछ कैसे बना सकते हैं जो ढेर पर रहने वाली किसी चीज की सफाई का कारण बन जाएगा?

class int_buffer
{
   size_t m_size;
   int *  m_buf;

   public:
   int_buffer( size_t size )
     : m_size( size ), m_buf( 0 )
   {
       if( m_size > 0 )
           m_buf = new int[m_size]; // will throw on failure by default
   }
   ~int_buffer()
   {
       delete[] m_buf;
   }
   /* ...rest of class implementation...*/

};


void foo() 
{
    int_buffer ib(20); // creates a buffer of 20 bytes
    std::cout << ib.size() << std::endl;
} // here the destructor is called automatically even if an exception is thrown and the memory ib held is freed.

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

class mutex
{
   // ...
   take();
   release();

   class mutex::sentry
   {
      mutex & mm;
      public:
      sentry( mutex & m ) : mm(m) 
      {
          mm.take();
      }
      ~sentry()
      {
          mm.release();
      }
   }; // mutex::sentry;
};
mutex m;

int getSomeValue()
{
    mutex::sentry ms( m ); // blocks here until the mutex is taken
    return 0;  
} // the mutex is released in the destructor call here.

इसके अलावा, क्या ऐसे मामले हैं जहां आप RAII का उपयोग नहीं कर सकते हैं?

नहीं वास्तव में नहीं।

क्या आप कभी भी अपने आप को कचरा संग्रहण की इच्छा रखते हैं? कम से कम एक कचरा संग्रहकर्ता जो आप कुछ वस्तुओं के लिए उपयोग कर सकते हैं जबकि दूसरों को प्रबंधित करने की अनुमति दें?

कभी नहीँ। कचरा संग्रह केवल गतिशील संसाधन प्रबंधन के एक बहुत छोटे सबसेट को हल करता है।


मैंने जावा और C # का बहुत कम उपयोग किया है, इसलिए मैंने इसे मिस करने के लिए कभी नहीं किया है, लेकिन जीसी ने निश्चित रूप से मेरी शैली को खराब कर दिया है जब यह संसाधन प्रबंधन के लिए आया था जब मुझे उनका उपयोग करना था, क्योंकि मैं RAII का उपयोग नहीं कर सकता था।
Rob K

1
मैंने C # का बहुत उपयोग किया है और आपसे 100% सहमत हूं। वास्तव में, मैं एक भाषा में एक गैर-निर्धारक जीसी को एक दायित्व मानता हूं।
Nemanja Trifunovic

2

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


1
नहीं, वे नहीं। लेकिन क्या आपके पास ढेर पर स्मार्ट पॉइंटर बनाने का एक अच्छा कारण है? वैसे, स्मार्ट पॉइंटर केवल एक उदाहरण था जहां RAII उपयोगी हो सकता है।
ई डोमिनिक

1
शायद "स्टैक" बनाम "हीप" का मेरा उपयोग थोड़ा मैला है - "स्टैक" पर एक वस्तु से मेरा मतलब किसी स्थानीय वस्तु से है। यह स्वाभाविक रूप से ढेर पर किसी वस्तु का एक हिस्सा हो सकता है। "ढेर पर एक स्मार्ट पॉइंटर बनाएं" से मेरा मतलब स्मार्ट पॉइंटर पर नए / डिलीट का उपयोग करना था।
ई डोमिनिक

1

RAII रिसोर्स एक्विजिशन इनिशियलाइज़ेशन के लिए एक परिचित है।

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

RAII तकनीक इस ऑटो-प्रबंधित ऑब्जेक्ट सुविधा का उपयोग उन वस्तुओं को संभालने के लिए करती है जो नए / नए [] का उपयोग करके अधिक मेमोरी के लिए पूछकर ढेर / फ्री-स्टोर पर बनाई गई हैं, जिन्हें हटाने / हटाने [कॉल] द्वारा स्पष्ट रूप से नष्ट कर दिया जाना चाहिए। । ऑटो-प्रबंधित ऑब्जेक्ट की क्लास इस अन्य ऑब्जेक्ट को लपेटेगी जो हीप / फ़्री-स्टोर मेमोरी पर बनाई गई है। इसलिए जब ऑटो-प्रबंधित ऑब्जेक्ट का कंस्ट्रक्टर चलाया जाता है, तो लिपटे ऑब्जेक्ट को हीप / फ्री-स्टोर मेमोरी पर बनाया जाता है और जब ऑटो-प्रबंधित ऑब्जेक्ट का हैंडल कार्यक्षेत्र से बाहर चला जाता है, तो उस ऑटो-प्रबंधित ऑब्जेक्ट के डिस्ट्रक्टर को स्वचालित रूप से कहा जाता है जिसमें लिपटे होते हैं ऑब्जेक्ट का उपयोग नष्ट कर दिया जाता है। OOP अवधारणाओं के साथ, यदि आप ऐसी वस्तुओं को निजी दायरे में किसी अन्य वर्ग के अंदर लपेटते हैं, तो आपको लिपटे हुए वर्ग के सदस्यों और विधियों तक पहुंच नहीं होगी यही कारण है कि स्मार्ट पॉइंटर्स (उर्फ हैंडल क्लासेस) के लिए डिज़ाइन किए गए हैं। ये स्मार्ट पॉइंटर्स लपेटी गई वस्तु को बाहरी दुनिया में टाइप किए गए ऑब्जेक्ट के रूप में उजागर करते हैं और वहां किसी भी सदस्य / विधियों को इनवॉइस करने की अनुमति देते हैं कि एक्सपोज्ड मेमोरी ऑब्जेक्ट से बना है। ध्यान दें कि स्मार्ट पॉइंटर्स में विभिन्न आवश्यकताओं के आधार पर विभिन्न स्वाद होते हैं। आपको आंद्रेई अलेक्जेंड्रेस्कु द्वारा मॉडर्न सी ++ प्रोग्रामिंग का उल्लेख करना चाहिए या इसके बारे में अधिक जानने के लिए लाइब्रेरी (www.boostorg) साझा_पट / कार्यान्वयन / प्रलेखन को बढ़ावा देना चाहिए। आशा है कि यह आपको RAII को समझने में मदद करेगा। आपको आंद्रेई अलेक्जेंड्रेस्कु द्वारा मॉडर्न सी ++ प्रोग्रामिंग का उल्लेख करना चाहिए या इसके बारे में अधिक जानने के लिए लाइब्रेरी (www.boostorg) साझा_पट / कार्यान्वयन / प्रलेखन को बढ़ावा देना चाहिए। आशा है कि यह आपको RAII को समझने में मदद करेगा। आपको आंद्रेई अलेक्जेंड्रेस्कु द्वारा मॉडर्न सी ++ प्रोग्रामिंग का उल्लेख करना चाहिए या इसके बारे में अधिक जानने के लिए लाइब्रेरी (www.boostorg) साझा_पट / कार्यान्वयन / प्रलेखन को बढ़ावा देना चाहिए। आशा है कि यह आपको RAII को समझने में मदद करेगा।

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