AnT stands with Russia
AnT stands with Russia

Reputation: 320641

User-defined operator new that returns null pointer

I know that quite few C++ FAQs out there (and answers here on SO) say that there's no need to check the return value of a plain new-expression for null, since plain new-expression indicates failures by throwing exceptions. They basically state that plain new-expression never returns null. (By "plain new-expression" I mean a new-expression that is not a nothrow one).

However, despite this looking like a very basic question, I suddenly realized that I don't understand what specific assumptions they make (if any) when they give that answer.

In particular, I wonder if am I allowed to overload the basic plain form of ::operator new to always return null pointer, and therefore expect that all plain new-expressions that use that operator will now return null pointers as well.

According to the language specification, if my ::operator new is declared as non-throwing, then I may/shall indicate memory allocation failure by returning a null pointer. So, let's do just that

void *operator new(size_t s) throw() {
  return 0;
}

int main() {
  int *i = new int;
}

In my experiments, the above new-expression does successfully return a null pointer. So, am I breaking any rules in the above code or not? Is it legal to declare a plain ::operator new as non-throwing?

And if the above code is fine, then I'd presume that when someone states that a plain new "never returns null pointer", they do that under assumption that the standard-library-provided version of ::operator new has not been replaced. Is that presumption correct?

Upvotes: 6

Views: 2091

Answers (1)

user657267
user657267

Reputation: 21000

The operators you can replace are as follows

[replacement.functions]

(2.1) — operator new(std::size_t)
(2.2) — operator new(std::size_t, const std::nothrow_t&)
(2.3) — operator new[](std::size_t)
(2.4) — operator new[](std::size_t, const std::nothrow_t&)
(2.5) — operator delete(void*)
(2.6) — operator delete(void*, const std::nothrow_t&)

void *operator new(size_t s) throw() is invalid, it has to throw in case of error

[new.delete.single]

void* operator new(std::size_t size);

3 Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else throw a bad_alloc exception. This requirement is binding on a replacement version of this function.

You can however safely replace the non-throwing noexcept overloads with a function that always returns null, as whoever calls these overloads must be aware of this behavior and check the return value accordingly. Obviously they won't be called unless the nothrow tag is passed, i.e. int* i = new (std::nothrow) int;

void* operator new(std::size_t size, const std::nothrow_t&) noexcept;

7 Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else return a null pointer. This nothrow version of operator new returns a pointer obtained as if acquired from the (possibly replaced) ordinary version. This requirement is binding on a replacement version of this function.

Upvotes: 2

Related Questions