Sign in to follow this  
NickGravelyn

RAII And Exceptions

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zahlman
The idea is that constructors just throw the exceptions; calling code is responsible for catching them.


Now I feel dumb. The whole time I was confused because I was reading that RAII was to eliminate massive uses of try/catch so I didn't realize the idea was to surround the constructor and usage in a large try/catch. Thanks for clearing that up.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this