Effective C++ item 8: Prevent exceptions from leaving destructors.

Throw an exception in destructor is dangerous and you should never let the exception leave destructors. If there are two exceptions propagating, the program will terminate or yield undefined behavior. Consider the example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
class Bad
{
public:
~Bad()
{
std::cout << "Throw in ~Bad()." << std::endl;
throw 1;
}
};
int main()
{
try
{
Bad bad;
}
catch(...)
{
std::cout << "Print This" << std::endl;
}
try
{
Bad bad;
std::cout << "Throw in other place." << std::endl;
throw 2;
}
catch(...)
{
std::cout << "Never print this " << std::endl;
}
}

The output will be like:

1
2
3
4
5
Throw in ~Bad().
Print This
Throw in other place.
Throw in ~Bad().
terminate called after throwing an instance of 'int'

Notice that two exception will propagate in the code so that it terminates.

So what should we do if we have to throw an exception in a destructor?

Consider we have a class to handle database connection and has a close method to close the connection. It will throw an exception in close method if close fails.

1
2
3
4
5
6
7
class DBConnection
{
public:
...
static DBConnection create();
void close(); // it will throw an exception
};

To ensure that clients don’t forget to call close on DBConnection, we have an resource-managing class to handle that:

1
2
3
4
5
6
7
8
9
10
11
class DBConn
{
public:
...
~DBConn()
{
db.close();
}
private:
DBConnection db;
};

But here we allow exception propagate out destructor which is dangerous as we discussed previously. so what’s the solution? We can catch the exception throw in destructor in the same destructor so that it will not propagate out that destructor. But that leads to either terminate the program my ourselves or swallow that exception. That’s not good. What we should do is to provide a close method for clients so that they can handle the exception themselves. But in destructor, we still swallow the exception if clients don’t call close method themselves.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class DBConn
{
public:
...
void close()
{
db.close();
closed = true;
}
~DBConn()
{
if (!closed)
{
db.close();
}
catch(...)
{
// log that close failed and swallow the exception
}
}
private:
DBConnection db;
bool closed;
};

Reference:
“Effective C++” Third Edition by Scott Meyers.