RAII And Exceptions

Started by
9 comments, last by NickGravelyn 17 years, 10 months ago
I understand how to use the try/catch/throw methods for using exceptions, but how do I encorporate this idea into RAII? I think I understand RAII, but I can't figure out how to handle errors in the constructor. Any ideas?
Advertisement
You can throw an exception in a constructor to signal an error, but you need to bear in mind that if you do, the associated destructor for the object will not be run.

So if, for example, you had a constructor that allocated some memory then tried to open a file to fill the memory with something, if the file open failed you would need to explicitly deallocate the memory before you threw an exception to signal the file failure.

class resource{private:    char *data;public:    resource(const char *name)    {    data=new char[1024]; FILE *fp=fopen(name,"r");    if(!fp){ delete [] data; throw file_exception(); }    /* use data */    fclose(fp);    }};


However, a better solution would be to refactor so that the data was encapsulated in another object, since the destructor for the sub-object will be run:

class dataholder{public:    dataholder(int size){ p=new char[size]; }    ~dataholder(){ delete [] p; }    char *p;};class resource{private:    dataholder data;public:    resource(const char *name) : data(1024)    {    FILE *fp=fopen(name,"r");    if(!fp) throw file_exception();    /* use data.p */    fclose(fp);    }};


And before anyone else says it, the above C-style methods of file access are for illustration only and the C++ equivalents would be far better.

HTH Paul
One of the benefits of RAII is that it removes the need for try/catch blocks situations where they are being used to prevent resource leaks.

Because d'tors aren't run if an exception is thrown in a c'tor, a RAII class needs it's c'tors to be all-or-nothing - in practice this means that unless a RAII class holds more than one resource (which isn't a good idea in itself), then no try/catch blocks are required to implement RAII.
If you are dealing with memory specifically, then always use a smart pointer. A std::auto_ptr or boost::scoped_ptr will always clear up when it goes out of scope: whether construction has finished or not.
An implementation of the scopeguard technique can help you write RAII-like ("-like" in that it doesn't neccessarily involve "resources" per-se, but anything that needs to be cleaned up in the event an exception) code much easier as well.
I see how you can throw the errors, but without the try/catch combination, how can I do anything with it? Or is everything done in the function called there? Can someone explain that part a bit more. Thanks for all the other insight. It's very useful.
Quote:Original post by NickGravelyn
I see how you can throw the errors, but without the try/catch combination, how can I do anything with it? Or is everything done in the function called there? Can someone explain that part a bit more. Thanks for all the other insight. It's very useful.


Even if you caught the error, what would you do with it? At the level the error occurred you usually don't have enough information to try to recover. Simply do nothing. The exception will propogate back and ideally whoever has enough information to make a recovery attempt will do so, based on the type of exception thrown.
--Michael Fawcett
I gather that much, but what I don't get is what's inside that exception being thrown? In the example it was
throw file_exception();
. What is the file_exception()? I don't see how I can create an exception that will be caught.

Here's an example of what I'm worried about. Say I have a couple of RAII classes. One of them depends on the creation of the first. If the first one throws an exception, wouldn't that fail the second class's creation and just crash my program (assuming that second variable was that important)?
Quote:Original post by NickGravelyn
I gather that much, but what I don't get is what's inside that exception being thrown? In the example it was
throw file_exception();
. What is the file_exception()? I don't see how I can create an exception that will be caught.

I'm not sure what you mean. The type of the exception being thrown is usually enough information for the person catching it. Do you mean what are the results of .what() being called, or does it maintain some internal state?

Quote:Original post by NickGravelyn
Here's an example of what I'm worried about. Say I have a couple of RAII classes. One of them depends on the creation of the first. If the first one throws an exception, wouldn't that fail the second class's creation and just crash my program (assuming that second variable was that important)?

Can you give an example? Maybe pseudo-code? I'm having a hard time following this one.
--Michael Fawcett
The idea is that constructors just throw the exceptions; calling code is responsible for catching them.

Quote:Original post by NickGravelyn
I gather that much, but what I don't get is what's inside that exception being thrown? In the example it was
throw file_exception();
. What is the file_exception()? I don't see how I can create an exception that will be caught.


Anything can be caught, but it is recommended that you use and/or make exception classes derived from std::exception. This base class provides a 'virtual const char* what()' which provides an error message for output. (The reason it doesn't use std::string is because you don't want to take any chances of exceptions happening during handling of some other exception, and creating a std::string object could exception out due to lack of memory.)

You can do something like:

class file_exception: public std::exception {  char* const what() { return "OMG TEH FILE SI SUX"; }};// resource class as before. Then calling code like:try {  resource foo("this file does not exist");  foo.doWonderfulThings();} catch (file_exception& e) {  cout << e.what() << endl;} catch (...) {  cout << "couldn't do wonderful things" << endl;}


Quote:Here's an example of what I'm worried about. Say I have a couple of RAII classes. One of them depends on the creation of the first. If the first one throws an exception, wouldn't that fail the second class's creation and just crash my program (assuming that second variable was that important)?


If the first one throws an exception, then either the constructor of the second one is written to catch and handle it, or (more typically) it just falls through to the calling code, in which case the calling code has the usual responsibility for the exception, just as it would if it had instantiated the first class directly. The program only crashes if an exception falls all the way through (up to and including main()) without being caught. Even in this case, the behaviour is defined and much nicer than what you would likely get by not using exceptions.

This topic is closed to new replies.

Advertisement