Tuesday, September 27, 2011

C++ RAII - Resource acquisition is initialization.

RAII - Resource Acquisition is initialization is one of the powerful idioms in C++, which makes code exception-safe and maintainable. The idiom makes use of the feature of C++ that destructor for stack allocated objects would be called at the end of scope. Generally, resources can be allocated and deallocated at any point in implementation, which could be acceptable in all-goes-well scenarios. If exception had been thrown during execution of code, after resource allocation, but before release, then huge possibility is there to have the resource not released properly.

At the end of scope, in C++, all the stack allocated objects will be destructed for sure, even exception is thrown. RAII Idiom makes use of this feature by wrapping the resource in a class. In constructor, resource would be allocated and in destructor, release would happen. This is exception-safe. This calls for cleaning up all the resources at single place, destructor, instead of sprinkling this logic everywhere.

Let me give a simple example to illustrate this concept using File Stream example. Consider a function which takes two arguments, a string and file name; Let us assume the functionality of this is to write the string into file. One may code as given below.

fstream stream;
stream.open(fileName, ios::binary | ios::out | ios::app);

if(stream.is_open())
{
stream << str; // Assume there is an exception here ...
}

stream.close();

If there is some exception thrown at some point while writing, stream.close() won't get called at all. One solution is to solve this is adding a try/ catch around the writing part, which may lead to unessential maintenance issues. Program wouldn't be easily readable also. Let us rewrite the same using RAII as below.

class SmartFile
{
public:
SmartFile(string fileName)
{
cout << "Stream Opening" << endl;
stream.open(fileName, ios::binary | ios::out | ios::app);
}
~SmartFile()
{
cout << "Stream Closing" << endl; stream.close();
}
void WriteString(string str)
{
if(stream.is_open())
{
cout << "File Writing" << endl; stream << str;
}
else
{
cout << "Write Error" << endl;
}
}
private:
fstream stream;
};

void WriteStringToFile(string str, string fileName)
{
SmartFile file(fileName);
file.WriteString(str);
}

The above function using RAII class SmartFile which is reliable, exception-safe. You can try the below given code to understand the same.

void WriteStringToFile(string str, string fileName)
{
SmartFile file(fileName);
throw 10;
file.WriteString(str);
}
Even though the above code throws an error code abnormally, SmartFile's destructor will be called.

RAII is being used in Smart Pointers, Synchronization based lock classes, etc. In a nutshell, this idiom necessitates to have all resources allocations in constructor and all deallocation logic in destructor to make the code exception-safe and maintainable.

Thanks for Reading.

No comments:

Post a Comment