C++ Safe Exception Handling
Let's have a piece of code (fstream is just an example, we could be talking about dynamic memory allocation...):
fstream f;
try {
f.open("xxx"开发者_C百科);
...
f.close();
} catch (...) {
...
}
When something goes wrong I would like to close() the file (release memory or whatever), but I don't know the state of f. After all, the exception may come from f.open(). I don't think it would be safe to call f.close() in the catch clause as I can no longer believe f.
f could also be a pointer to a dynamically allocated array which I would like to delete [], but who knows where it points to after the exception was thrown...
This may not be very common, but what can I do when I absolutely can't affort any additional damage?
I can think about an immediate abort().
Thanks.
You should use RAII or popularly known here as SBRM (Scope Based Resource Management) :)
fstream
destructors call close
for you. When an exception is thrown, the file is closed automatically.
For managing memory, you can use smart pointers.
For managing mutexes or more general locks, most libraries provide you with a class whose destructor unlocks the mutex for you.
Never write code in the form:
acquire a resource
do stuff which can throw
release a resource
Instead, use objects whose destructors release the resource for you.
The fstream destructor will call close() for you, so you don't really need to close it yourself (unless you want to see the return code of close()).
In your example, you can move the declaration of f
into the try
block to ensure that it destroys itself; the destructor knows the object's state.
As another example, with memory allocation you can initialize the pointer to 0 before memory is actually allocated and then again reset it to zero when you release allocated memory. This let's you check if memory was allocated to avoid releasing memory that is no longer yours. Example:
char *name = 0;
try {
// perform some operations which may throw...
// now allocate
name = new char[20];
// more error prone operations here
}
catch(...){
if(name != 0){
delete[] name;
name = 0;
}
}
Again, you could use RAII here as well. Example:
class MyCharArray {
char *str;
public:
MyCharArray(size_t size){
str = new char[size];
}
~MyCharArray(){
delete[] str;
}
};
int main(){
try {
// perform some operations which may throw...
MyCharArray name(20);
// more error prone operations here
}
catch(...){
// no additional error handling required here
}
return 0;
}
Note that RAII is considered superior because, you write clean-up code only once -- in the destructor -- and not after every try
block.
Enable exceptions on the fstream object, and handle the exception at the point where it is possible :
void foo()
{
std::fstream f( "lala.txt" );
f.exceptions( std::fstream::failbit | std::fstream::badbit )
// do something
}
I wouldn't consider using boost to do the RAII to be cheating:
#include <boost/smart_ptr.hpp>
#include <fstream>
using namespace std;
static void do_close(fstream* f)
{
try
{
f->close();
delete f;
} catch(...) { /* log the error? */ }
}
int main()
{
boost::shared_ptr<fstream> f(new fstream(), &do_close);
try
{
f->open("xxx");
f->close();
} catch (...)
{
}
}
精彩评论