Hello, dear reader. After reviewing my previous post about exception handling many people advised me to write about RAII either. As the latter is not C++-specific only and it actually does not much relate to exception handling I decided to write about it in a separate post. So What is RAII?
RAII stands for Resource Acquisition Is Initialization. It is a programming technique invented by Bjarne Stroustrup and intended to make the usage of resources more safe in terms of their allocation and deallocation. In C++ after an exception is thrown during program execution the only thing that the standard guarantees to be executed is the destructors of automatic objects allocated on the stack (the destructors of objects with static storage duration are also called and all this is done in std::exit() function which also calls user-defined functions registered with std::atexit(), though these are not the case for RAII). So if we want to allocate a resource to use it somehow, RAII implies its allocation in a class constructor, and deallocation in the destructor. Thus, if anything goes wrong, we can be sure that the resource will be deallocated properly. Let's try this on a simple example.
The most common example used through the literature is the file resource, e.g. a handle of the opened file stream, we also can give this example here, as it does not change the essence. Suppose we need to open an xml file, read some structured data in portions (3 high-level items at a time), process it (e.g. convert to another format, or just deserialize an object from the xml file), and finally close the file. Here is a code snippet for the function which covers all the steps:
RAII stands for Resource Acquisition Is Initialization. It is a programming technique invented by Bjarne Stroustrup and intended to make the usage of resources more safe in terms of their allocation and deallocation. In C++ after an exception is thrown during program execution the only thing that the standard guarantees to be executed is the destructors of automatic objects allocated on the stack (the destructors of objects with static storage duration are also called and all this is done in std::exit() function which also calls user-defined functions registered with std::atexit(), though these are not the case for RAII). So if we want to allocate a resource to use it somehow, RAII implies its allocation in a class constructor, and deallocation in the destructor. Thus, if anything goes wrong, we can be sure that the resource will be deallocated properly. Let's try this on a simple example.
The most common example used through the literature is the file resource, e.g. a handle of the opened file stream, we also can give this example here, as it does not change the essence. Suppose we need to open an xml file, read some structured data in portions (3 high-level items at a time), process it (e.g. convert to another format, or just deserialize an object from the xml file), and finally close the file. Here is a code snippet for the function which covers all the steps:
MyClass LoadFromXMLFile(const
std::string& filePath)
{
// Create an empty object
MyClass obj;
// Open a stream to read from xml file
std::ifstream
in(filePath, std::ios_base::in);
// Read data in small chunks and fill the object
FillObjectDataFromStream(obj,
in);
// Close the stream
in.close();
// Return the object
return obj;
// Return the object
return obj;
}
The dangerous part of this function is FillObjectDataFromStream() function. If it throws an exception the file stream will never be closed (not talking about process termination). So this means that we do not release a resource which is not used already and that is very bad practice.
Now if we want to take advantage of the RAII technique we need a helper class, which will allocate the resource in the constructor and release it in the destructor. Let's name this class FileStreamOpener:
Now if we want to take advantage of the RAII technique we need a helper class, which will allocate the resource in the constructor and release it in the destructor. Let's name this class FileStreamOpener:
class FileStreamOpener
{
private:
std::ifstream
_in;
public:
FileStreamOpener(const std::string& filePath)
{
_in.open(filePath.c_str(), std::ios_base::in);
}
~FileStreamOpener()
{
_in.close();
}
std::ifstream&
GetStream()
{
return _in;
}
};
Now we need to modify LoadFromXMLFile() function, here we go:
MyClass LoadFromXMLFile(const
std::string& filePath)
{
// Create an object
MyClass obj;
// Implicitly open a stream through FileStreamOpener
FileStreamOpener
fso(filePath);
// Read data in small chunks and fill the object
FillObjectDataFromStream(obj,
fso.GetStream());
// We don't even need to close the stream
// as fso will be destructed when the flow
// goes out of this function's body scope
// and the stream will be closed.
return obj;
}return obj;
The details are given in comments. Now whenever the destructor of 'fso' object is called, more precisely, during stack unwinding if exception is thrown or after the execution flow leaves the function body scope, the file stream will be closed, i.e. the resource will be properly released. This is the whole idea of the RAII idiom. Thanks for your time.
This comment has been removed by the author.
ReplyDeleteNever seen such a practical and proper explanation.......Thanks.....Please write about all design patterns and and how to identify them and apply them....When such a simple explanation is given I personally feel your
ReplyDeletemoving a section of software engineers to next level....