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, why not on failure? Is there any specific reason for this behaviour?
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, why not on failure? Is there any specific reason for this behaviour?
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).
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;
}
Just a historical note, not an answer to this question (there are plenty of good answers)... a long long time ago when dinosaurs roamed the earth and Turbo C++ was ruling the world of C++ programmers, new
actually returned NULL
. Quote from a book of those days (0bj3ct-0r13nt3d Pr0gr4mm1ng with ANSI and Turbo C++ - title intentionally obfuscated so that no one by mistake reads it anymore) page 115.
If the new operator fails to allocate memory it returns NULL which can be used to detect failure or success of new operator.
because of this legacy code is full with a lot of NULL checking ... a real nightmare to bring them up to the current standard...
However, here is a tiny piece of the C++ standard 5.3.4/13:
[ Note: unless an allocation function is declared with a non-throwing exception-specification (15.4), it indicates failure to allocate storage by throwing a std::bad_alloc exception (Clause 15, 18.6.2.1); it returns a non-null pointer otherwise. If the allocation function is declared with a non-throwing exception-specification, it returns null to indicate failure to allocate storage and a non-null pointer otherwise. —end note ] If the allocation function returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
which tells you that in some special cases, new
can return NULL
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.
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?
In C, all requests for dynamic memory are made by function calls; it's necessary that code check any allocation requests to see if they return null
, but there is a well-defined place where such checks can be made (i.e. right after the call). In C++, while it is possible for code to construct objects by calling new
, most objects are created by creating an object in which they play a part.
Given:
class Story
{
Potter harry;
Weasley ron,ginny,george,fred;
Grainger hermione;
Longbottom neville;
Creevy colin;
Story()
{
}
}
If the constructor for harry
tries to create a new
object and fails, the only way to prevent the system from trying uselessly to create objects for all the other fields would be to have the constructor throw an exception. Even though the constructor for harry
failed, the other constructors might succeed in creating those objects if they require less memory than a Potter
object; since the failure to create harry
would render the Story
object useless, however, any effort spent creating objects for the remaining fields would not only be wasted, but would require the system to expend even more effort destroying those uselessly-created objects.
Since in most cases the only thing the system would be able to do when new
fails is to throw an exception, it's easier to have new
throw the exception itself and have code catch the exception if necessary, than to require that all code which calls new
must check whether it succeeded and throw an exception if it didn't.
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;
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.