Please see the below code:
unsigned char* p = new unsigned char[x];
CLASS* t = new (p) CLASS;
assert((void*)t == (void*)p);
Can I assume (void*)t == (void*)p
?
Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory. Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.
Because placement new does not allocate memory, you should not use delete to deallocate objects created with the placement syntax. You can only delete the entire memory pool ( delete whole ). In the example, you can keep the memory buffer but destroy the object stored in it by explicitly calling a destructor.
std::vector<T,Allocator>::emplace_back Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Yes you may. I believe it's guaranteed by several provisions.
[expr.new]/10 - Emphasis mine
A new-expression 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. For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement ([basic.align]) of any object type whose size is no greater than the size of the array being created. [ Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed. — end note ]
Which to me reads like the new expression must create an object (assuming it's not of array type) at the exact address returned by the allocation function. Since you are using the built-in placement new, this take us to the following
[new.delete.placement]
These functions are reserved, a C++ program may not define functions that displace the versions in the Standard C++ library ([constraints]). The provisions of ([basic.stc.dynamic]) do not apply to these reserved placement forms of operator new and operator delete.
void* operator new(std::size_t size, void* ptr) noexcept;
Returns: ptr.
Remarks: Intentionally performs no other action.
Which guarantees the address you pass to the expression is the exact address of the character array object you allocated. That's because conversions to void*
do not change the source address.
I think it's enough to promise the addresses are the same, even if the pointers are not interchangeable in general. So according to [expr.eq]/1 (thanks to @T.C.):
Two pointers of the same type compare equal if and only if they are both null, both point to the same function, or both represent the same address ([basic.compound]).
The comparison must yield true, again because the addresses are the same.
Can I assume
(void*)t == (void*)p
?
Not necessarily.
If the author of the class overloads CLASS::operator new(size_t, unsigned char*)
, for example, that operator can return anything other than the second argument, e.g.:
struct CLASS
{
static void* operator new(size_t, unsigned char* p) { return p + 1; }
};
If you would like this new expression to call the standard non-allocating placement new operator the code needs to
<new>
.void*
argument.::
to bypass CLASS::operator new
, if any.E.g.:
#include <new>
#include <cassert>
unsigned char p[sizeof(CLASS)];
CLASS* t = ::new (static_cast<void*>(p)) CLASS;
assert(t == static_cast<void*>(p));
In this case t == static_cast<void*>(p)
indeed.
This is, in fact, what GNU C++ standard library does:
template<typename _T1, typename... _Args>
inline void _Construct(_T1* __p, _Args&&... __args) {
::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With