Why doesn\'t new
return NULL
on failure? Why does it throw an exception only on failure?
As it returns a pointer to the object on successes
In the comments, you emphasized that you wanted to know why new
is designed this way. In my mind, it's all about object composition.
Consider a class Foo which contains (among other things) a std::vector.
class Foo {
public:
explicit Foo(std::size_t n) : m_vec(n, 'A') {}
...
private:
std::vector<char> m_vec;
...
};
When you construct a Foo in dynamic memory, there are two memory allocations: one of the Foo itself, and one for the contents of its vector. If either one fails, you need to be assured that there are no leaks and that the problem is reported to the caller.
Foo * pfoo = new Foo(desired_size);
Suppose new
did return a null pointer upon failure. If the allocation for the Foo fails, pfoo
will be set to a null pointer, and you could rely on that to do your error detection. Terrific. You then have some error handling code like:
if (pfoo == nullptr) { ... }
Now consider the nested object. If the allocation for the contents of m_vec
fails, you'd have to detect that and report it to the calling code, but there's no way for you to get a null pointer to propagate out to the assignment to pfoo
. The only way to do that is to have std::vector throw an exception (for any kind of construction problem), so the error handling code we just added would be useless because it's looking for null pointer instead of exceptions.
Having new
throw a std::bad_alloc
allows you to treat nested dynamic memory allocation failures the same way you treat outer ones. That's a powerful way to avoid code duplication and errors.
The C++ committee could have let new
return a null pointer on an allocation failure, but every constructor that used new
for internal memory would have to detect the failure and turn it into an exception anyway. So having new
throw by default simplifies everyone's code.
For those cases where your constructor can do something reasonable even in the face of an allocation failure, you can explicitly ask for the null pointer with std::nothrow
and then handle the error (or you can catch the std::bad_alloc
).
Also consider what happens if we stack allocate a Foo.
Foo foo(desired_size, 'B');
If we had somehow managed to make construction problems return a null pointer, how would the calling code detect it?
you can specify that you want new to return 0 instead of throwing std::bad_alloc
using the std::nothrow parameter:
SomeType *p = new(std::nothrow) SomeType;
Before exceptions were introduced in C++:
A failing new
-expression produced a nullpointer.
In a failing constructor one assigned a nullpointer to this
.
After exceptions were introduced:
A failing ordinary new
-expression throws an exception.
In a failing constructor one throws an exception.
One difference is that failure reporting for constructors now works also for creation of objects without dynamic allocation.
Another difference is that code using new
-expressions now can be simpler since the error handling can be moved out of each such code place, and centralized.
The old nullpointer-result behavior is still available via std::nothrow
(include the <new>
header). This implies checking at each place using new
. Thus nullpointer results are in conflict with the DRY principle, don't repeat yourself (the redundancy offers an endless stream of opportunities to introduce errors).
in C++, operator new
is not only to allocate a chunk of memory, it is also class construction. thus, in semantic, operator new
is much richer than function malloc
. via exceptions, we get better informations and we can handle construction failures better.
By default, when the new operator is used to attempt to allocate memory and the handling function is unable to do so, a bad_alloc exception is thrown. But when nothrow is used as argument for new, it returns a null pointer instead.
The nothrow constant is a value of type nothrow_t, with the only purpose of triggering an overloaded version of the function operator new (or operator new[]) that takes an argument of this type. The value itself is not used, but that version of operator new shall return a null pointer in case of failure instead of throwing an exception.
It is by design. In C++ every kind of failure is notified by throwing an exception by default — streams, however, are exception, which does not throw exception (pun intended) by default.
You can use nothrow version as:
T *p = new (std::nothrow) T(args);
if ( p == nullptr ) //must check for nullity
{
std::cout << "memory allocation failed" << std::endl;
}