Seriously, do you mean safe in terms freeing allocated resources?
At the risk of answering for Sander, which I shouldn't do:
"Exception-safe" is a term used to describe the pre-post conditions of a function in the presence of an exception. It does apply to freeing resources, but also to other points regarding application work as well. For example, say you have two instance variables (member variables in C++), and you're about to perform a copy inside a 'copy function' of that object, you've been given a reference (C++ parlance) to a source object for the copy.
Tempted to be quick about this, one might say:
a = s.a;
b = s.b;
Assuming that a and b are the instance variables, and s is the source object. This code isn't exception safe.
Exception safe functions must offer one of 3 guarantees in the presence of exceptions. The basic guarantee, the strong guarantee and the no-fail guarantee. In C++, we must insist that destructors can't fail. If they're written in any way that can raise an exception, the program can't be 'valid' from the standpoint of exceptions. I'd have to point you to the literature to investigate more, lest this post turn into a chapter on the subject.
In case it's puzzling as to why a simple copy of a couple of instance variables might not be 'exception-safe', consider as an example that (especially in C++) the copy of b = s.b raises an exception. The value of a was already modified, and the value of b is it's former state. The object is now invalid, it can't be used anymore. There could, obviously, be circumstances where b's unassigned condition isn't consequential enough to be a problem, but we're not talking about specific circumstances. We're talking about any/all circumstances, the way engineers approach professional code.
The only way to make the copy exception safe is to assume that there exists a 'swap' function that's guaranteed not to fail, just like a destructor is guaranteed not to fail. Without this assumption, a copy can't be made exception safe. Instead of assigning each instance variable in order, from the source to the 'this' or 'self' members, a temporary should be used.
obj t;
t.a = s.a;
t.b = s.b;
If this doesn't throw an exception, the swap can be performed afterwards without causing a problem. The swap will be performed between 'this' (or self) and the temporary.
The reason is that if t.b = s.b fired an exception, and the function exits, the object performing this operation is exactly in the condition it was in before this happened. If it's part of data which should be saved in a complex document (say a CAD file), it's still valid. Without exception safety techniques, it wouldn't be possible to guarantee we could save the user's work before a 'graceful' exit, should that be the only course to take from this point out.
Exception-safe coding techniques are a study by themselves, and engineers that use the techniques (and insist upon them as coding standards) generate code that's more stable, robust and reliable.
One of the more surprising aspects of 'exception-safe' coding in C++ is that many times you're considering exceptions and their implications in functions that have no try/catch clauses of any kind.
The issue of stack unwinding, and object destruction, in the presence of exceptions IS about allocated resources, but more generally about the preservation of the state of the application even when an exception occurs. The possibility of 'surviving' an exception increases dramatically when 'exception-safe' techniques are used.