In the following code, the exceptions are thrown in two cases as shown in the main ().
#include <iostream>
// Our own exception classes - just for fun.
class myExceptionClassA
{
public:
myExceptionClassA () {std::cout << "\nWarning: Division by zero isn't allowed.\n";}
};
class myExceptionClassB
{
public:
myExceptionClassB () {std::cout << "\nWarning: Division by dividend isn't allowed.\n";}
};
class divisionClass
{
private:
int *result;
public:
divisionClass ()
{
// Allocating memory to the private variable.
result = new int;
}
/*
The class function `doDivide`:
1. Throws above defined exceptions on the specified cases.
2. Returns the division result.
*/
int doDivide (int toBeDividedBy) throw (myExceptionClassA, myExceptionClassB)
{
*result = 200000;
// If the divisor is 0, then throw an exception.
if (toBeDividedBy == 0)
{
throw myExceptionClassA ();
}
// If the divisor is same as dividend, then throw an exception.
else if (toBeDividedBy == *result)
{
throw myExceptionClassB ();
}
// The following code won't get executed if/when an exception is thrown.
std :: cout <<"\nException wasn't thrown. :)";
*result = *result / toBeDividedBy;
return *result;
}
~divisionClass ()
{
std::cout << "\ndddddddddd\n";
delete result;
}
};
int main ()
{
divisionClass obj;
try
{
obj.doDivide (200000);
}
catch (myExceptionClassA) {}
catch (myExceptionClassB) {}
try
{
obj.doDivide (3);
}
catch (myExceptionClassA) {}
catch (myExceptionClassB) {}
try
{
obj.doDivide (0);
}
catch (myExceptionClassA) {}
catch (myExceptionClassB) {}
try
{
obj.doDivide (4);
}
catch (myExceptionClassA) {}
catch (myExceptionClassB) {}
return 0;
}
– The both exception class print statements get printed.
– The statement in the destructor gets printed only once.
– Valgrind doesn’t show any memory leaks.
anisha@linux-y3pi:~/Desktop> g++ exceptionSafe3.cpp -Wall
anisha@linux-y3pi:~/Desktop> valgrind ./a.out
==18838== Memcheck, a memory error detector
==18838== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==18838== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==18838== Command: ./a.out
==18838==
Warning: Division by dividend isn't allowed.
Exception wasn't thrown. :)
Warning: Division by zero isn't allowed.
Exception wasn't thrown. :)
dddddddddd
==18838==
==18838== HEAP SUMMARY:
==18838== in use at exit: 0 bytes in 0 blocks
==18838== total heap usage: 3 allocs, 3 frees, 262 bytes allocated
==18838==
==18838== All heap blocks were freed -- no leaks are possible
==18838==
==18838== For counts of detected and suppressed errors, rerun with: -v
==18838== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
anisha@linux-y3pi:~/Desktop>
Shouldn’t the destructor get called 3 times – two times for exceptions and one time for return statement?
Please explain the point that I am missing.
Now I tried it by removing all the try catch blocks in the main().
Destructor doesn’t get called at all?
anisha@linux-y3pi:~/Desktop> valgrind ./a.out
==18994== Memcheck, a memory error detector
==18994== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==18994== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==18994== Command: ./a.out
==18994==
Warning: Division by dividend isn't allowed.
terminate called after throwing an instance of 'myExceptionClassB'
==18994==
==18994== HEAP SUMMARY:
==18994== in use at exit: 133 bytes in 2 blocks
==18994== total heap usage: 3 allocs, 1 frees, 165 bytes allocated
==18994==
==18994== LEAK SUMMARY:
==18994== definitely lost: 0 bytes in 0 blocks
==18994== indirectly lost: 0 bytes in 0 blocks
==18994== possibly lost: 129 bytes in 1 blocks
==18994== still reachable: 4 bytes in 1 blocks
==18994== suppressed: 0 bytes in 0 blocks
==18994== Rerun with --leak-check=full to see details of leaked memory
==18994==
==18994== For counts of detected and suppressed errors, rerun with: -v
==18994== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
Aborted
anisha@linux-y3pi:~/Desktop>
When you catch an exception, the stack is “unwound” between the point where the exception was thrown, and the point where it is caught. This means that all automatic variables in the scopes between those two points are destroyed — everything inside the
trythat corresponds to whatevercatchmatches the exception.Your object
objis an automatic variable in themainfunction, outside thetry. Hence, it is not destroyed when the stack is unwound. Your code relies on that fact — after the firstcatchyou calldoDivideon it again, so it better not have been destroyed.If you don’t catch the exception at all, then it is implementation-defined whether or not the stack is unwound (15.3/9 in C++11) before the program is terminated. It looks as though in your second test, without any
catchclauses, it is not.This means that if you want your RAII objects to “work”, then you can’t allow uncaught exceptions in your program. You could do something like:
Now you’re guaranteed that any automatic variables in
do_all_the_workwill be destroyed if an exception escapes the function. The downside is that you might get less info out of your debugger, because it has forgotten the original throw site of the uncaught exception.Of course, it’s still possible for code in your program to prevent your
objfrom being destroyed, for example by callingabort().