The debate between defensive/exception handling I find very interesting.
You seem to be implying these two concepts are at odds with each other. They aren't. In fact, one common form of defensive programming is checking a function's arguments and throwing exceptions if they're invalid.
But at the same time, if that happens I would prefer if the compiler /stops/ the program and says, "You raised an error." So I can fix it.
One of the benefits of throwing exceptions is that by default they will stop the program.
A more useful comparison would be error codes versus exceptions. These are both mechanisms for dealing with unavoidable failures. In other words, failures that are not the result of a programming error, but are something that the programmer simply cannot prevent. Common examples include running out of memory, a certain file not existing or not allowing write access, or a network request timing out.
Before exceptions, these sorts of failures were usually indicated by an error code returned by the function that may unavoidably fail. The problems with this approach are: 1) It's easy for a programmer to simply never check error codes, leaving a nearly endless list of rare and subtle bugs with completely unpredictable consequences. 2) Since it's just one integer, you don't get a lot of information about why the failure happened. 3) In many cases, the place where the potentailly failing function is called is not a place where a failure can reasonably be recovered from. This requires the error code to be "passed back up" the call stack by every single function that may call it directly or indirectly. Needless to say, this is error prone.
Exceptions were intended as a solution to these problems. When an exception is thrown, it's impossible for the programmer to accidentally ignore it; either they go out of their way to catch and handle it, or the program crashes. Exceptions also tend to contain far more information about the failure than a single integer. And you don't have to write any code to allow exceptions to propagate through a function; they just keep going up until they reach code that actually wants to deal with them.
So the best places to use exceptions are when you have an unavoidable potential failure that would be dangerous to ignore and is typically difficult or impossible to recover from at the place it happens.
As a simple and perhaps somewhat extreme example of that, consider what happens when you run out of memory. Any function that creates objects can potentially run out of memory. If you were to try and handle these errors properly with error codes, almost every single non-trivial function in your program would have to remember to check if allocation failed, check if any of the other functions its calling failed, and return the out of memory code when they do. And there's no sane way to handle the failure aside from showing an "Out of memory" error message to the user (which is typically only feasible near the top of your program). So the fact that C++ handles this via an exception saves you a tremendous amount of extremely tedious and error-prone code; you only need write the code to show that "Out of memory" error message.
Exceptions are also sometimes useful for detecting and quickly failing on logic errors. Say you write a square root function. It only works on positive numbers, but the compiler can't stop people from passing a negative number into it. You can simply ignore this problem and declare that anyone making this mistake is invoking undefined behavior (see "design by contract"). But defensive programming is often a good thing, and we'd like our API to be a bit more user-friendly than that. You can't return an error code since the square root function is expected to return a square root. You can log an error message, but that doesn't stop the program right away so the programmer may not notice it. If you throw an exception, that will immediately stop the program and display a clear error message, so that's usually a good choice. Many languages also have some kind of "assertion" mechanism that's also a good solution for this. Assertions behave very similarly to exceptions, except that you can't catch them, so they're simpler in some ways.