Resource Acquisition Is Initialization
From Wikipedia, the free encyclopedia
This article may require cleanup to meet Wikipedia's quality standards. Please improve this article if you can. (March 2007) |
Resource Acquisition Is Initialization, often referred to by the acronym RAII, is a popular design pattern in many object oriented languages like C++, D and Ada. The technique combines acquisition and release of resources with initialization and uninitialization of objects.
RAII involves assigning ownership of a resource to scoped objects for resource management[1]. The acquisition is typically bound to the construction (initialization) and the automatic, deterministic destruction (uninitialization) is used to guarantee the resource is released. Since scoped objects are cleaned up regardless of whether the scope exits through normal use or through an exception, RAII is a key concept for writing exception-safe code[1][2].
Contents |
[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 below) 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 and marked for inclusion in the new C++0x standard or policy based Loki::SmartPtr
from Loki library.
[edit] C++ example
The following RAII class is a lightweight wrapper to the C standard library file system calls.
#include <cstdio> class file { public: file( const char* filename ) : m_file_handle(std::fopen(filename, "w+")) { if( !m_file_handle ) throw std::runtime_error("file open failure") ; } ~file() { if( std::fclose(m_file_handle) != 0 ) { // deal with filesystem errors, fclose() may fail when flushing latest changes } } void write( const char* str ) { if( std::fputs(str, m_file_handle) == EOF ) throw std::runtime_error("file write failure") ; } private: std::FILE* m_file_handle ; // copy and assignment not implemented; prevent their use by // declaring them private. file( const file & ) ; file & operator=( const file & ) ; }; // This RAII class can then be used as follows: void example_usage() { // open file (acquire resource) file logfile("logfile.txt") ; logfile.write("hello logfile!") ; // continue using logfile ... // throw exceptions or return without worrying about closing the log; // it is closed automatically when logfile goes out of scope. }
The essence of the RAII idiom is that the class file
encapsulates the management of any finite resource, like the FILE*
file handle. When objects of such classes are bound to automatic variables, it is guaranteed that the resource will properly be disposed of at function exit. Furthermore, file
instances guarantee that a valid log file is available (by throwing an exception if the file could not be opened).
Automatic variables easily manage multiple resources within a single function: They are destructed in the reverse order of their construction, and an object is only destructed if fully constructed. That is, if no exception was thrown inside its constructor.
Using RAII-enabled resources simplifies and reduces overall code size and helps ensure program correctness.
[edit] Resource management without RAII
In Java, objects are not values and must be accessed through references; hence you cannot have automatic variables of objects that "go out of scope"; instead, all objects are dynamically allocated and have indefinite lifetimes. In addition, Java's objects are garbage collected automatically at indeterminate times (or not at all). Resources must thus be manually closed by the programmer. The preceding example would be written like this
void java_example() { // open file (acquire resource) final LogFile logfile = new LogFile("logfile.txt") ; try { logfile.write("hello logfile!") ; // continue using logfile ... // throw exceptions or return without worrying about closing the log; // it is closed automatically when exiting this block } finally { // explicitly release the resource logfile.close(); } }
The burden of releasing the resources falls on the programmer for each place the resource is used.
Ruby and Smalltalk do not support RAII, but have a simpler and more flexible pattern that makes use of methods that pass resources to closure blocks. Here is an example in Ruby:
File.open("logfile.txt", "w+") do |logfile| logfile.write("hello logfile!") end # The 'open' method has ensured that the file handle has been closed # without special precautions by the code writing to the file
This is similar to Common Lisp's 'unwind-protect'-based macros.
Python's 'with' statement and the 'using' statement in C# and Visual Basic 2005 provide deterministic resource management within a block and do away with the requirement for explicit finally
-based cleanup and release.
Perl manages object lifetime by reference counting, making it possible to use RAII similarly to C++: Objects that are no longer referenced are immediately released. A destructor can then release the resource. However, object lifetime isn't necessarily bound to any lexical scope. One can e.g. store a reference to it in a global variable, making the object (and resource) stay allocated indeterminately long. This makes it possible to accidentally leak resources that should have been released at the end of some scope.
C requires more administrative code since it doesn't support exceptions, try-finally blocks or RAII of any kind. A typical approach is to separate releasing of resources at the end of the function and jump there with gotos in the case of error. This way the cleanup code need not be duplicated.
int c_example() { int retval = 0; // return value 0 is success FILE *f = fopen("logfile.txt", "w+"); if( !f ) { retval = -1; goto bailout1; } if( fputs("hello logfile!", f) == EOF ) { retval = -2; goto bailout2; } // continue using the file resource // Releasing resources (in reverse order) bailout2: if ( fclose(f) == EOF ) { retval = -3; } bailout1: return retval; }
Variations exist, but the example demonstrates the general approach.
[edit] References
- ^ a b Bjarne Stroustrup (April 2001). "Exception Safety: Concepts and Techniques" (PDF). Retrieved on 2007-09-02.
- ^ Sutter, Herb (1999). Exceptional C++. Addison-Wesley. ISBN 0-201-61562-2.
[edit] External links
- Que es RAII y como funciona (en español)
- 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
- Interview "A Conversation with Bjarne Stroustrup" by Bill Venners
- Article "The Law of The Big Two" by Bjorn Karlsson and Matthew Wilson
- Blog Entry "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
- Article "Managing Dynamic Objects in C++" by Tom Cargill
- Blog "tombarta RAII"
- Blog Entry "RAII C++ Anti-Pattern"
- Article "Smart pointers in C++" by Julio M. Merino Vidal
- Blog Entry "Resource Management: A Critical Look at RAII" by Michael Feathers