46

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?

Mat
  • 202,337
  • 40
  • 393
  • 406
NDestiny
  • 1,133
  • 1
  • 12
  • 28
  • Its clear that, on failure new through an exception and we can use std::nothrow to forcefully stop that. I want to know why c++ is designed as it throwing exception rather than returning a error(as we have a return value facility) , is there any advantage throwing an exception rather than returning NULL/error. – NDestiny Oct 17 '14 at 09:02
  • 4
    Do you really want *more* potential null pointers in your code? Consider how much time is already spent avoiding issues because of them. – ssube Oct 17 '14 at 15:01
  • 2
    @DavidGrinberg when there isn't a large enough block of free memory to allocate what you asked for – OrangeDog Oct 17 '14 at 17:13
  • 1
    "Why doesn't new return NULL on failure? Why does it throw an exception only on failure?" -- Because that's what the language designers decided upon. "As it returns a pointer to the object on successes, why not on failure?" -- what object on failure? " Is there any specific reason for this behaviour?" -- Yes: it's far better behavior. – Jim Balter Oct 18 '14 at 21:24
  • @DavidGrinberg When your machine doesn't have infinite memory. – Jim Balter Oct 18 '14 at 21:25
  • 2
    +1 to reopen. This is not a duplicate. The thrust of the question is _why_ new was designed this way. The other question is concerns consistency of (possibly outdated) implementations. None of the answers on the other question address the question posed here. – Adrian McCarthy Oct 20 '14 at 16:45
  • @Jim Balter & @ All Othere : Because that's what the language designers decided upon -- why ?? what is that extra advantage we are getting here through exceptions, any real time example or any use case could clear my doubt. – NDestiny Oct 21 '14 at 10:25
  • http://www.stroustrup.com/bs_faq2.html#exceptions-why – Jim Balter Oct 21 '14 at 10:29
  • @user1716427: A big advantage is that it simplifies object composition. See my answer below. – Adrian McCarthy Oct 21 '14 at 17:24
  • Thanks @AdrianMcCarthy , you Almost cleared my doubt. – NDestiny Oct 23 '14 at 06:30

8 Answers8

65

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).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 8
    +1 for being the only one (so far) to even discuss the "why" behind it, and not just explain that both are possible or what's more common for C++ error handling.... – Tony Delroy Oct 17 '14 at 07:40
36

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;
}
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • 5
    +1 for "by design" and the *exception* pun - still, I was always wondering if making exceptions for throwing exceptions isn't something that is *faulty by design*... –  Oct 17 '14 at 12:11
17

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

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
10

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.

Peixu Zhu
  • 2,111
  • 1
  • 15
  • 13
7

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).

Aside

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?

Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
  • There's a practical means to approach this, if it were desired: You'd stack allocate just the pointer with `Foo *foo = new Foo(desired_size,'B')` and then test for null in the following code. Just as you can't declare a forward referenced undefined class in a header - only a pointer - you wouldn't declare a class without a `new` in a stack allocation. It's a very practical approach and moves the allocation off the stack. Best not to declare classes on the stack anyway; most memory models are pretty restrictive on stack sizes for various reasons. You're just asking for trouble. – fyngyrz May 28 '18 at 14:29
  • @fyngyrz: That approach is not compatible with RAII. Instantiating objects of class type directly and using smart pointers and RAII to manage the dynamic memory are perfectly reasonable things to do. – Adrian McCarthy May 29 '18 at 16:37
  • If you're doing that, sure. If not, then not. :) And modern stack models do make allocating objects on the stack iffy. – fyngyrz May 30 '18 at 19:09
  • @fyngyrz: I think that still misses the point about object composition. My example Foo contains a vector, so even if you explicitly allocate the Foo, there's still a second allocation for the vector that you cannot detect. Foo's constructor could detect it, but it has no way to report the failure except to throw an exception or invent some sort of zombie state for the Foo. Allocating objects on the stack is (intentionally) quite routine in C++--a good optimizer will sometimes change a dynamic allocation to a stack-based allocation in certain cases. – Adrian McCarthy May 30 '18 at 23:09
  • WRT allocations where you can't control and manage what's happening in the allocation, it's just a bad idea to allocate them on the stack at all. I was talking about allocations where you actually know what's going on and the object construction isn't going to hose you in some manner you have no control over. Generally speaking, it's an OPC problem - other people's code. For any class you write, if you write it well, it can fail gracefully. If it can't - you didn't write it well. Depending on exceptions is pretty lame, a last-gasp mechanism when there's just no other way. – fyngyrz Jun 01 '18 at 16:48
  • @fyngyrz: I'm answering the question _why doesn't new return a null pointer on failure?_ The short answer: composition. The language is designed to support composition (and RAII), and the fact that new is designed--by default--to throw an exception on failure is a direct result of that. If you don't think composition is a reasonable thing to do in C++, then you're going to miss out on a bunch of the value the language has to offer. – Adrian McCarthy Jun 02 '18 at 13:20
4

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.

supercat
  • 77,689
  • 9
  • 166
  • 211
2

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;
Imran Ali Khan
  • 8,469
  • 16
  • 52
  • 77
2

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.

Laura Maftei
  • 1,863
  • 1
  • 15
  • 25