You seem to have two misconceptions about exceptions:
- C++ throws exceptions for all errors, like Java.
- C++ should be written by the programmer such that exceptions are thrown for all errors.
Both are indeed misconceptions. In C++, exceptions are rarely thrown by standard-library components or by the language itself, and they should be thrown only in situations where a problem is rare but out of your control, or where there is no easy other way to report an error.
An example for the former would be allocating more memory than the system is able to give you. This is a relatively rare problem with an external resource which you cannot prevent. Therefore, new
will indeed throw a std::bad_alloc
exception if you are running out of memory.
Examples for the latter are constructors and dynamic_cast
s with references.
Accessing an illegal index in a dynamically allocated array is not a good use case for exceptions, because you can easily prevent that error. If anything, it should be an assertion, because if you access an illegal index, then you have a programming error, i.e. a bug. Bugs cannot be fixed at run-time.
The usual C++ way of dealing with such low-level errors is undefined behaviour. In your example:
int *a=new int[100];
try{
a[101]=1;
}
catch(...){
//never called
}
The attempt to access a[101]
invokes undefined behaviour, which means that the program is allowed to do everything. It may crash, it may work fine, it may even throw an exception. It's all up to your compiler and your environment.
Living with undefined behaviour is not a very nice thing, of course, so it's understandable that you want to do something about it.
First all, don't ever use new[]
. Use std::vector
instead. Illegal element access with std::vector
is still undefined behaviour, but your compiler may introduce extra run-time checks such that the undefined behaviour results in an immediate crash, such that you can fix the bug.
std::vector
does also have an at
member function which throws exceptions on wrong indices, but that's more of a design error than anything, because at the std::vector
level, a wrong index indicates a problem at higher abstraction level in your program logic.
The important thing is that you give up on the idea that you can "handle" all errors equally. Split your errors into three different categories:
Bugs. A bug means that your code is wrong and that your program is not what you think it is. Use assert
for those errors and/or rely on your compiler to introduce corresponding checks in C++ standard-library components. Wrong code should crash fast such that it can do as little harm as possible. Hint: MSVC has something called "debug versions". A "debug version" is a set of compiler and linker options which enables a lot of extra run-time checks to help you at finding the bugs in your code.
Wrong input. All programs receive wrong input. You must never assume that input is correct. It does not matter if the input comes from a human being or from a machine. Wrong input must be part of normal program flow. Don't use assert
or exceptions for wrong input.
Exceptional status of external resource. This is what exceptions should be used for. Every program relies on external resources in one form or the other, usually provided by the operating system. The major external resource is memory. Something like std::vector<int> x(100);
normally does not fail, but in theory, it could, because it requires memory. Starting a new thread normally does not fail, but in theory, the operating system may not be able to start one. Those are exceptional situations, so exceptions are a good way to handle them.
These are rough guidelines, of course. It is especially hard to draw an exact line between wrong input and problems with external resources.
Still, here is an example that attempts to summarise the guidelines:
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cassert>
void printElement(std::vector<int> const& v, int index)
{
// ----------------
// Error category 1
// ----------------
assert(index >= 0);
assert(index < static_cast<int>(v.size()));
std::cout << v[index] << "\n";
}
int main()
{
std::cout << "Enter size (10-100): ";
int size = 0;
std::cin >> size;
if (!std::cin || (size < 10) || (size > 100))
{
// ----------------
// Error category 2
// ----------------
std::cerr << "Wrong input\n";
return EXIT_FAILURE;
}
try
{
std::vector<int> v(size);
printElement(v, 0);
}
catch (std::bad_alloc const&)
{
// ----------------
// Error category 3
// ----------------
std::cerr << "Out of memory\n";
return EXIT_FAILURE;
}
}