Suppose I have a class like this:
#include <iostream>
using namespace std;
class Boda {
private:
char开发者_StackOverflow *ptr;
public:
Boda() {
ptr = new char [20];
}
~Boda() {
cout << "calling ~Boda\n";
delete [] ptr;
}
void ouch() {
throw 99;
}
};
void bad() {
Boda b;
b.ouch();
}
int main() {
bad();
}
It seems that destructor ~Boda
never gets called, thus the ptr
resource never get freed.
Here is the output of the program:
terminate called after throwing an instance of 'int'
Aborted
So it seems the answer to my question is No
.
But I thought that the stack got unwound when an exception got thrown? Why didn't Boda b
object get destructed in my example?
Please help me understand this resource problem. I want to write better programs in the future.
Also, is this the so called RAII
?
Thanks, Boda Cydo.
If the exception is not caught anywhere, then the C++ runtime is free to go straight to terminating the program without doing any stack unwinding or calling any destructors.
However, if you add a try-catch block around the call to bad()
, you will see the destructor for the the Boda
object being called:
int main() {
try {
bad();
} catch(...) { // Catch any exception, forcing stack unwinding always
return -1;
}
}
RAII means that dynamically (heap) allocated memory is always owned by an automatically (stack) allocated object that deallocates it when the object destructs. This relies on the guarantee that the destructor will be called when the automatically-allocated object goes out of scope, whether due to a normal return or due to an exception.
This corner-case behavior is normally not a problem with respect to RAII, since usually the main reason that you want the destructors to run is to free memory, and all memory is given back to the OS when your program terminates anyway. However if your destructors do something more complicated, like maybe remove a lock file on disk or something, where it would make a difference whether the program called destructors or not when crashing, you may want to wrap your main
in a try-catch block that catches everything (only to exit on exception anyway) just to ensure that the stack always unwinds before terminating.
The destructor won't be run if an exception occurs in the constructor.
It will be run if necessary (if exception is handled somewhere) if exception is raised in another method like in your example. But as the program is terminated, calling the destructor is not necessary here and behavior depends of compiler...
The idea of RAII
is that constructor allocates ressources and destructor frees them. If an exception occurs in constructor, there is no simple way to know wich ressources where allocated and which were not (it depends on the exact place in the constructor where exception occured). You should also remember that if a constructor fails, the only way to say it to caller it to raise an exception and allocated memory is freed (either stack unwinding, or heap allocated memory) as if it were never allocated.
The solution is obvious : if any exception may occur inside a constructor, you have to catch it and free allocated ressources if necessary. It may actually be some duplicated code with destructor, but that's not a big problem.
In destructor you should not raise exceptions, as it can lead to big troubles with stack unwinding.
In any other method, use exceptions as you like, but do not forget to handle them somewhere. An unhandled excception may be worse than no exception at all. I know of some programs that does not handle exceptions for some minor errors... and crash for errors that should only issue a warning.
Try flushing the stream - you will see that the destructor is indeed called:
cout << "calling ~Boda" << endl;
It's the buffering of the I/O that delays the printout to the point that program termination cuts in before actual output.
Edit:
The above holds for handled exceptions. With unhandled exceptions the standard does not specify whether stack is unwound or not. See also this SO question.
精彩评论