The default placement new operator is declared in 18.6 [support.dynamic] ¶1 with a non-throwing exception-specification:
void* operator new (std::size_t size, void* ptr) noexcept;
This function does nothing except return ptr; so it is reasonable for it to be noexcept, however according to 5.3.4 [expr.new] ¶15 this means that the compiler must check it doesn't return null before invoking the object's constructor:
-15-
[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.
It seems to me that (specifically for placement new, not in general) this null check is an unfortunate performance hit, albeit small.
I've been debugging some code where placement new was being used in a very performance-sensitive code path to improve the compiler's code generation and the check for null was observed in the assembly. By providing a class-specific placement new overload that is declared with a throwing exception-specification (even though it can't possibly throw) the conditional branch was removed, which also allowed the compiler to generate smaller code for the surrounding inlined functions. The result of saying the placement new function could throw, even though it couldn't, was measurably better code.
So I've been wondering whether the null check is really required for the placement new case. The only way it can return null is if you pass it null. Although it's possible, and apparently legal, to write:
void* ptr = nullptr;
Obj* obj = new (ptr) Obj();
assert( obj == nullptr );
I can't see why that would be useful, I suggest it would be better if the programmer had to check for null explicitly before using placement new e.g.
Obj* obj = ptr ? new (ptr) Obj() : nullptr;
Has anyone ever needed placement new to correctly handle the null pointer case? (i.e. without adding an explicit check that ptr is a valid memory location.)
I'm wondering whether it would be reasonable to forbid passing a null pointer to the default placement new function, and if not whether there is some better way to avoid the unnecessary branch, other than trying to tell the compiler the value is not null e.g.
void* ptr = getAddress();
(void) *(Obj*)ptr; // inform the optimiser that dereferencing pointer is valid
Obj* obj = new (ptr) Obj();
Or:
void* ptr = getAddress();
if (!ptr)
__builtin_unreachable(); // same, but not portable
Obj* obj = new (ptr) Obj();
N.B. This question is intentionally tagged micro-optimisation, I am not suggesting that you go around overloading placement new for all your types to "improve" performance. This effect was noticed in a very specific performance-critical case and based on profiling and measurement.
Update: DR 1748 makes it undefined behaviour to use a null pointer with placement new, so compilers are no longer required to do the check.
While I can't see much of a question in there except "Has anyone ever needed placement new to correctly handle the null pointer case?" (I haven't), I think the case is interesting enough to spill some thoughts on the issue.
I consider the standard broken or incomplete wrt the placement new function and requirements to allocation functions in general.
If you look closely at the quoted §5.3.4,13, it implies that every allocation function has to be checked for a returned nullpointer, even if it is not noexcept. Therefore, it should be rewritten to
If the allocation function is declared with a non-throwing exception-specification and returns null, initialization shall not be done, the deallocation function shall not be called, and the value of the new-expression shall be null.
That would not harm the validity of allocation functions throwing exceptions, since they have to obey §3.7.4.1:
[...] If it is successful, it shall return the address of the start of a block of storage whose length in bytes shall be at least as large as the requested size. [...] The pointer returned shall be suitably aligned so that it can be converted to a pointer of any complete object type with a fundamental alignment requirement (3.11) and then used to access the object or array in the storage allocated (until the storage is explicitly deallocated by a call to a corresponding deallocation function).
And §5.3.4,14:
[ Note: when the allocation function returns a value other than null, it must be a pointer to a block of storage in which space for the object has been reserved. The block of storage is assumed to be appropriately aligned and of the requested size. [...] -end note ]
Obviously, a placement new that just returns the given pointer, cannot reasonably check avilable storage size and alignment. Therefore,
§18.6.1.3,1 about placement new says
[...] The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.
(I guess they missed to mention §5.3.4,14 at that place.)
However, together these paragraphs say indirectly "if you pass a garbage pointer to the palcement functions, you get UB, because §5.3.4,14 is violated". So it's up to you to check the sanity of any poitner given to placement new.
In that spirit, and with the rewritten §5.3.4,13, the standard could strip the noexcept from placement new, leading to an addition to that indirect conclusion: "...and if you pass null, you get UB as well". On the other hand, its much less likely to have a misaligned pointer or pointer to too few memory than having a null pointer.
However, this would remove the need for checking against null, and it would fit well to the philosophy "don't pay for what you don't need". The allocation function itself would not need to check, because §18.6.1.3,1 explicitly says so.
To round things up, one could consider adding a second overload
void* operator new(std::size_t size, void* ptr, const std::nothrow_t&) noexcept;
Sadly, proposing this to the committee is unlikely to result in a change, because it would break existing code relying on placement new being ok with null pointers.
Related
A placement-new expression that takes a single argument that is a pointer to pre-allocated memory will construct an object of type T in that memory.
Why does it call the standard placement-new operator void* operator new ( std::size_t count, void* ptr ); since the latter does nothing and just returns its pointer argument?
int x = 10;
int* p = new(&x) int{1024};
Could you explain the steps taken in the above by the compiler to construct a new int in the memory address of x?
Why doesn't the placement-new expression directly construct an object at the memory address it gets as a pointer, rather than calling an operator function that does nothing and just returns its pointer argument?
The general rule is that new (args...) T will call operator new(sizeof(T), args...), and this function is required to return void*. If this operator new call returns successfully, the object is then constructed into the memory pointed to by the return value.
This general rule is powerful enough to support both the ordinary new expression new int and the placement form new (&x) int without any special cases. These two expressions call different overloads of operator new, which is why the former allocates and the latter does not. No matter what, an object is constructed at the end (unless the operator new function failed by throwing an exception).
There is no need to have a special rule in the language that says operator new is not called by a placement new expression. Instead, the compiler can simply optimize the code by directly constructing the int object into &x without calling operator new first, since it already knows that the placement operator new will just return its second argument.
(Actually, the reality is a bit more complicated than this. If T is an array type, then operator new[] is called instead of operator new, and the compiler may request from operator new[] a greater amount of memory than the array will actually occupy, and adjust the returned pointer before constructing the array. There are also special rules relating to over-aligned types, and there actually is some special-casing for placement new and delete. These details are not relevant to this answer.)
As far as compiler optimizations go, is it legal and/or possible to change a heap allocation to a stack allocation? Or would that break the as-if rule?
For example, say this is the original version of the code
{
Foo* f = new Foo();
f->do_something();
delete f;
}
Would a compiler be able to change this to the following
{
Foo f{};
f.do_something();
}
I wouldn't think so, because that would have implications if the original version was relying on things like custom allocators. Does the standard say anything specifically about this?
Yes, it's legal. expr.new/10 of C++14:
An implementation is allowed to omit a call to a replaceable global
allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage
is instead provided by the implementation or provided by extending the
allocation of another new-expression.
expr.delete/7:
If the value of the operand of the delete-expression is not a null
pointer value, then:
— If the allocation call for the new-expression for the object to be
deleted was not omitted and the allocation was not extended (5.3.4),
the delete-expression shall call a deallocation function (3.7.4.2).
The value returned from the allocation call of the new-expression
shall be passed as the first argument to the deallocation function.
— Otherwise, if the allocation was extended or was provided by
extending the allocation of another new- expression, and the
delete-expression for every other pointer value produced by a
new-expression that had storage provided by the extended
new-expression has been evaluated, the delete-expression shall call a
deallocation function. The value returned from the allocation call of
the extended new-expression shall be passed as the first argument to
the deallocation function.
— Otherwise, the delete-expression will not call a deallocation
function (3.7.4.2).
So, in summary, it's legal to replace new and delete with something implementation defined, like using the stack instead of heap.
Note: As Massimiliano Janes comments, the compiler could not stick exactly to this transformation for your sample, if do_something throws: the compiler should omit destructor call of f in this case (while your transformed sample does call the destructor in this case). But other than that, it is free to put f into the stack.
These are not equivalent. f.do_something() might throw, in which case the first object remains in memory, the second gets destructed.
I'd like to point out something IMO not stressed enough in the other answers:
struct Foo {
static void * operator new(std::size_t count) {
std::cout << "Hey ho!" << std::endl;
return ::operator new(count);
}
};
An allocation new Foo() cannot generally be replaced, because:
An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.
Thus, like in the Foo example above, the Foo::operator new needs to be called. Omitting this call would change the observable behavior of the program.
Real world example: Foos might need to reside in some special memory region (like memory mapped IO) to function properly.
Consider the following code snippet constructing an instance of a POD (plain old data) struct in-place:
#include <new>
#include <cassert>
#include <cstddef>
struct Test
{
int a;
char b;
double c;
};
int main()
{
const std::size_t minimumNumberOfBytes = sizeof( Test ) * 4;
// Get a block of memory that can accommodate a Test instance and then some!
void* const ptrToMemBlock = new char[ minimumNumberOfBytes ];
assert( ptrToMemBlock );
// Construct a Test instance in-place.
const Test* const testInstance( ::new ( ptrToMemBlock ) Test() );
// Is this assumption guaranteed to be true?
assert( testInstance == ptrToMemBlock );
}
Is the assumption represented by the final assert() guaranteed to always be correct? Or is it conceivable that the compiler might decide to construct the Test instance, say a few bytes after the start of the memory block I specified in the placement-new call?
Note that I'm asking specifically about POD types here. I know that things can get iffy if multiple inheritance and stuff like that gets involved.
This assertion will always hold, because new is required to return blocks of memory with MAXIMUM possible alignment. BTW - your first assert() is worthless, as normal new does not return nullptr - it throws or aborts, only "nothrow new" can return nullptr.
Yes, the the last assert is guaranteed to hold, because this form of placement-new must always return the passed pointer, not using any space for itself:
5.3.4 New [expr.new]
8 A new-expression may obtain storage for the object by calling an allocation function (3.7.4.1). [...]
10 An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression. [...]
11 When a new-expression calls an allocation function and that allocation has not been extended, the newexpression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
[...]
Your new-expression calls the global placement-new allocation-function.
That is a non-replacable function, thus the allocation cannot be extended or omitted.
Also, you are not allocating an array but a single object, thus no padding of the request may occur at all.
18.6.1.3 Placement forms [new.delete.placement]
1 These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library (17.6.4). The provisions of (3.7.4) do not apply to these reserved placement forms of operator new and operator delete.
void* operator new(std::size_t size, void* ptr) noexcept;
2 Returns: ptr.
3 Remarks: Intentionally performs no other action.
And this guarantees that the allocation-function returns the passed pointer unchanged.
Yes, the assertion will hold. Any new expression creating a single object must request exactly sizeof(Test) bytes of storage from the allocation function; and so it must place the object at the start of that storage in order to have enough room.
Note: This is based on the specification of a new-expression in C++11. It looks like C++14 will change the wording, so the answer may be different in the future.
My question are located in my code comment:
int* a = new int[0];// I've expected the nullptr according to my logic...
bool is_nullptr = !a; // I got 'false'
delete[] a; // Will I get the memory leaks, if I comment this row?
Thank you.
For C++11, and given your code:
int* a = new int[0];
Zero is a legal size, as per 5.3.4/7:
When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to
allocate an array with no elements.
The operator invoked is as per 18.6.1.2 (emphasis mine):
void* operator new[](std::size_t size);
...
3 Required behavior: Same as for operator new(std::size_t). This requirement is binding on a replacement
version of this function.
4 Default behavior: Returns operator new(size).
...referencing 18.6.1.1...
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.
So, the pointer returned must be non-null.
You do need to delete[] it afterwards.
In C++03 new int[0] results in undefined behavior because the value between the [] must be a strictly positive value - zero is no good (5.3.4/6 "New"). So asking whether there's a memory leak afterwards is in a sense pointless.
In C++11 new int[0] results in a call to the allocator to allocate a zero length array (5.3.4/7 "New"). If the allocation request succeeds, a pointer is returned - there's nothing in the standard that says how much memory the block pointed to by that pointer contains, other than it has to be at least the requested size. However, it has at least the effect of allocating at least one character, because that address cannot be returned by the allocator again until it has been freed. In practice, the bookkeeping overhead will be more than one character.
Yes, there is a leak, and it is not implementation-dependent.
This new expression cannot yield a null pointer. It allocates memory by calling operator new[], which is required to "return a non-null pointer to suitably aligned storage, or else throw a bad_alloc exception" (see C++11 §18.6.1.1/3 and §18.6.1.2/3).
Further, the allocation function requirements (§3.7.4.1) require that each call to an allocation function returns a pointer that is distinct from all other pointers that have been allocated but not yet deallocated. Thus, the implementation cannot simply have a single "empty allocation" pointer that it always returns.
This, every array-form new expression allocates something, even if the extent is zero. If you don't deallocate that object via delete[], you have leaked it.
Yes, without delete there will be a memory leak.
Every new has to be paired with delete. Even if the programmer-allocated size is 0. Allocator may allocate more memory than requested because of alignment requirements, management overhead or anything else.
In this case it is implementation defined whether you will be returned a nullptr or not but you should be careful that you do not dereference this pointer also not calling delete will result in a Memory leak.
W.r.t to calling delete the rule is simple:
"If you call new you must call delete."
Correction:
As the citations in other answers have made clear, It cannot return you a nullptr.
guys! Out of curiosity – the following code would probably not be legal, would it?
T *p = ::operator new(sizeof(T)); // allocate memory for a T
new (p) T; // construct a T into the allocated memory
delete p; //delete the object using the standard delete operator
No. You can only delete what you get back from new- no exceptions.
There's at least one circumstance in which it's clearly undefined: if you've overloaded operator new and operator delete for T, then this will attempt to allocate memory using ::operator new, but delete it using T::operator delete. Unless your T::operator delete is purely a wrapper around ::operator delete, that's going to cause a problem.
Other than that, I think it's probably defined. The standard is very specific about the fact that a new expression allocates its memory using an allocation function (§5.3.4/10), which will be ::operator new as long as you haven't provided a T::operator new.
Your placement new expression then initializes the object, just as described for a new expression in the first bullet point of §5.3.4/15.
Then we get to the destruction side. According to $5.3.5/1: "The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression." That requires that you have used a new expression to create the object -- which you have. You've used a placement new, which is one of the possibilities for a new expression, and specified in §5.3.4/1.
The next requirements that apply seem to be: "The operand shall have a pointer type, or a class type having a single conversion function (12.3.2) to a pointer type." Again, your expression meets that as well.
I'm going to quote more requirements, without further comment, except that your code seems to meet all of them (some limit the implementation of a delete expression, not the pointer you can use in one):
(§5.3.5/2): "In the first alternative (delete object), the value of the operand of delete shall be a pointer to a non-array object or a pointer to a sub-object (1.8) representing a base class of such an object (clause 10). If not, the behavior is undefined."
(§5.3.5/3): "In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.
(§5.3.5/4): "In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined."
(§5.3.5/6): "The delete-expression will invoke the destructor (if any) for the object or the elements of the array being deleted."
(§5.3.5/7): The delete-expression will call a deallocation function (3.7.3.2).
With the initial caveat about ::operator new vs. T::operator new, I think the pointer you're using in the delete expression meets all the requirements, so the behavior should be defined.
Having said all that, I certainly hope this is purely academic interest -- even though it looks to me like the code does have defined behavior, it's a lousy idea even at very best.
Going off of DeadMG's correct assertion, there is no problem with a slight change to your code:
unsigned char* addr = new unsigned char[sizeof(MySimpleStructure)];
MySimpleStructure* p = new (addr) MySimpleStructure;
delete [] addr;
Since we're deleteing addr which was returned by new there is no issue with this being legal. Of course, after addr is deleted, p should not be accessed (its a dangling pointer at that time). Also note that MySimpleStructure as allocated via placement new to p will not have its destuctor called.