Reputation: 320641
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
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