Resource Acquisition Is Initialization
From Wikipedia, the free encyclopedia
Resource Acquisition Is Initialization, often referred to by the acronym RAII, is a popular programming technique in C++ and D. The technique combines acquisition and release of resources with initialization and uninitialization of variables.
The acquisition is bound to the construction (initialization) whereas the release is bound to the destruction (uninitialization) of the variable. Since a destructor of an automatic variable is called when leaving its scope, it can be guaranteed that the resource is released as soon as the variable's life time ends. This is also true for cases when an exception occurs. Thus, RAII is a key concept for writing exception-safe code (Sutter 1999).
Contents |
[edit] Example
Here is an example (in C++):
class LogFile { public : LogFile(const char* fileName) : m_file( OpenFile(fileName) ) // initialize data member with the file handle { if( m_file == INVALID_HANDLE ) throw FileFailedToOpen(); } // handle errors automatically ~LogFile() { CloseFile(m_file); } void write(const char* logLine) { WriteFile( m_file, logLine ); } private : FILE_HANDLE m_file; // avoid copying by declaring a private but unimplemented copy-constructor // and assignment operator LogFile (const LogFile&); LogFile& operator= (const LogFile&); };
This simple RAII class can be used like this :
bool functionA() { LogFile log("Output log of functionA"); log.write("fun Step5 succeeded"); // The function can throw exceptions and return // without worrying about closing the log: if( ... ) throw FunctionAFailure(); // or return false return true; }
Without using RAII, each function using an output log would have to manage the FILE_HANDLE resource explicitly. For example, the equivalent implementation of functionA without using RAII would be:
bool functionB() { FILE_HANDLE log; try { log = OpenFile("Output log of functionB"); // EXTRA CODE: explicit error checking: if( log == INVALID_HANDLE ) return false; WriteFile( log, "fun Step5 succeeded" ); // if the function needs to check for an error and abort accordingly: if( error ) { // EXTRA CODE: release the acquired resources CloseFile(log); return false; // or throw an exception } // EXTRA CODE: the resource needs to be closed explicitly CloseFile(log); // explicitly release the resource return true; } catch (...) { // An exception was thrown during the execution of the function // Time to cleanup the resources CloseFile(log); // Now handle the exception, or re-throw it, or return false } }
The essence of the RAII idiom is that the class LogFile encapsulates the management of any finite resource, like the FILE_HANDLE. It guarantees that the resource will properly be disposed of at function exit. Furthermore, LogFile instances guarantee that a valid log file is available (by throwing an exception if the file could not be opened).
There's also a big problem in the presence of exceptions: in functionB, if more than one resource were allocated, but an exception was to be thrown between their allocations, there's no general way to know which resources need to be released in the final catch block - and releasing a not-allocated resource is usually a bad thing. RAII takes care of this problem; the automatic variables are destructed in the reverse order of their construction, and an object is only destructed if it was fully constructed (no exception was thrown inside its constructor). So functionB can never be as safe as functionA without special coding for each situation - like checking for invalid-default values, or nesting try-catch blocks.
This frees functionA from explicitly managing the resource as would otherwise be required. When several function use LogFile, this simplifies and reduces overall code size, and helps ensure program correctness.
Languages that do not support RAII, such as Java, provide an alternative solution to ensure that a resource will be released, with try-finally blocks. This allows one to write correct code, but does not fully encapsulate the management of resources, as each and every function using LogFile may have to explicitly demand the destruction of the log file with a try-finally block.
[edit] Typical uses
The RAII technique is often used for controlling thread locks in multi-threaded applications. Another typical example of RAII is file operations, e.g. the C++ standard library's file-streams. An input file stream is opened in the object's constructor, and it is closed upon destruction of the object. Since C++ allows objects to be allocated on the stack, C++'s scoping mechanism can be used to control file access.
RAII is also used (as shown in the example above) to ensure exception safety. RAII makes it possible to avoid resource leaks without extensive use of try
/catch
blocks and is widely used in the software industry.
The ownership of dynamically allocated memory (memory allocated with new) can be controlled with RAII. For this purpose, the C++ Standard Library defines auto_ptr. Furthermore, lifetime of shared objects can be managed by a smart pointer with shared-ownership semantics such as boost::shared_ptr
defined in C++ by the Boost library.
[edit] Limitations
RAII is easiest to use in languages that automatically call the destructor for local objects when they go out of scope (e.g. C++). Where this is not the case, the programmer must assure that the destructor is called for local objects as they go out of scope.
In some garbage-collected languages (e.g. Java, C#, or Python) the garbage collector is responsible for the eventual destruction of local objects and it is thus impossible to know when the destructor will be called; to use RAII in such languages the programmer must define and call a finalization routine. However, C# includes the using statement which makes RAII much easier, see: Wikibooks:C Sharp Programming/Object Lifetime. A similar facility is included in the 2005 release of VB.NET. Python added the 'with' operator in its 2.5 release which can be used to support this pattern.
In other garbage collected languages (such as D), class instances can be declared such that when they go out of scope, the destructor will get called, thereby supporting RAII.
[edit] References
- Sutter, Herb (1999). Exceptional C++. Addison-Wesley. ISBN 0-201-61562-2.
[edit] External links
- Article "The RAII Programming Idiom" by Wikipedia editor Jon Hanna (Talliesin)
- Wiki "Resource Acquisition Is Initialization" from the Portland Pattern Repository
- Sample Chapter "Gotcha #67: Failure to Employ Resource Acquisition Is Initialization" by Stephen Dewhurst
- Article "A Conversation with Bjarne Stroustrup" by Bill Venners
- Article "The Law of The Big Two" by Bjorn Karlsson and Matthew Wilson
- RAII in C++ by Jonathan Dodds
- Article "Implementing the 'Resource Acquisition is Initialization' Idiom" by Danny Kalev
- Article "RAII" by Mike Nordell
- Article "RAII, Dynamic Objects, and Factories in C++" by Roland Pibinger
- Wikibooks RAII in VB6