Placement New and Delete

placement new and delete

The correct method is:

buf->~Buffer();
::operator delete(mem);

You can only delete with the delete operator what you received from the new operator. If you directly call the operator new function, you must also directly call the operator delete function, and must manually call the destructor as well.

how do you delete an object allocated with placement new

In the first case, there's no point in using placement new, since int doesn't have a constructor.

In the second case, it's either pointless (if myClass is trivial) or wrong, since there are already objects in the array.

You use placement new to initialise an object in a block of memory, which must be suitably aligned, and mustn't already contain a (non-trivial) object.

char memory[enough_bytes];  // WARNING: may not be properly aligned.
myClass * c = new (memory) myClass;

Once you've finished with it, you need to destroy the object by calling its destructor:

c->~myClass();

This separates the object's lifetime from that of its memory. You might also have to release the memory at some point, depending on how you allocated it; in this case, it's an automatic array, so it's automatically released when it goes out of scope.

Getting issue with placement new and delete operator


  1. Placement delete is called to free memory when a constructor called from placement new fails. You are not supposed to call any destructors from any version of operator delete, because operator delete frees memory that is left after an object that used to reside there is destroyed (or was never constructed to begin with).
  2. The only way to explicitly call a placement operator delete is to spell out two words operator delete, thus making a function-call-expression. You cannot invoke it from a delete-expression (there is no placement-delete-expression syntax). In your case, you would need to use a qualified name: Object::operator delete. Note that if you remove the explicit destructor call from Object::operator delete, as you should because of the above, the destructor will not be called. There is no way to both invoke the destructor and free the memory in a single call to a placement delete. The easiest way to handle this is to create and use a non-static member function, say void Object::destroy(Mem*).

How does C++ placement delete work internally (C++ runtime)? How to overcome its limitation?

This answer assumes the question refers to user-defined placement allocation functions:

void* operator new  ( std::size_t count, user-defined-args... );
void* operator new[]( std::size_t count, user-defined-args... );

and user-defined placement deallocation functions:

void operator delete  ( void* ptr, args... );
void operator delete[]( void* ptr, args... );

The behaviour of these functions is:

  • operator new: If defined, called by the custom single-object placement new expression with the matching signature. If a class-specific version is defined, it is called in preference to this. If neither is provided by the user, the placement new expression is ill-formed.
  • operator new[]: Ditto but for array form.
  • operator delete: If defined, called by the custom single-object placement new expression with the matching signature if the object's constructor throws an exception. If a class-specific version is defined, it is called in preference to this. If neither is provided by the user, no deallocation function is called.
  • operator delete[]: Ditto but for array form.

To answer your questions:

  1. Don't pay for what you don't use. There might be a setup where no deallocation function is required.

  2. You'll have to include a mechanism whereby the value of the void * pointer being deleted can be used to determine which memory pool it is in. This must be possible because your different pools can't return the same value simultaneously. A naive method might be to have each pool manage a non-overlapping address range and test the pointer against each pool's address range. Or perhaps you could store metadata in memory before the pointed-to location, e.g. the new version would allocate N+16 bytes, store metadata in the first 16, and return to the user a pointer to the 16th byte of the block). Or you could keep a data structure associating each active pointer with metadata.

  3. Evaluation of the new-expression for allocating an object of class type will go something like:

    • Call operator new, passing the arguments
    • Assuming that succeeds, call class constructor (if it's a class type being allocated).
    • If the class constructor throws, and matching operator delete exists, call that function, passing the arguments.
    • Now evaluation of the new-expression is finished, and control returns to either the code that contained the new-expression, or into the exception handling mechanism.

Delete on placement new


Is the exception due to call of delete on a stack?

Yes. Though to clarify a little, this is not a C++ exception. It is an error that is detected by runtime. If runtime is not that smart, you could as well run into undefined behavior.

Is it fine to use placement new on memory block on stack?

Yes. You might also take a look at alloca() and Variable-length arrays (VLA) - those are another two mechanisms of allocating memory on stack. Note that VLA is part of C99 and not C++11 (and earlier) standards, but is supported by most production-grade compilers as an extension (it might appear in C++14).

If yes then how shall I delete the character pointer.

You should not. When you use a placement new, you do not call delete, you simply invoke a destructor. For any given object o of type T you need to call o->~T(); instead of delete o;. If you must, however, deal with the code that calls delete or delete [] on such an object, you might need to overload operators delete and delete[] and make those operators do nothing (destructor is already ran at that point).

Is it possible to assign multiple variables on a single block of memory using placement new?

Yes. However, you need to take extra care to make sure you meet alignment requirements.

Placement new and delete in constexpr functions


What is the difference between my usage and usage of these operators in standard library?

Your usage is not called std::allocator<T>::allocate or std::construct_at.

These particular functions - along with a few others - are specifically granted exceptions to the normal rule:

For the purposes of determining whether an expression E is a core
constant expression, the evaluation of a call to a member function of
std​::​allocator<T> as defined in [allocator.members], where T is
a literal type, does not disqualify E from being a core constant
expression, even if the actual evaluation of such a call would
otherwise fail the requirements for a core constant expression.
Similarly, the evaluation of a call to std​::​destroy_­at,
std​::​ranges​::​destroy_­at, std​::​construct_­at, or
std​::​ranges​::​construct_­at does not disqualify E from being a
core constant expression unless: [...]

As for plain new expressions, during constant evaluation it never calls ::operator new:

During an evaluation of a constant expression, a call to an allocation function is always omitted.

Placement New in C++

Everything is right, until...

pc3 = new (buffer) JustTesting("Bad Idea", 6);

This invokes undefined behaviour (no?). You've already constructed an object of type JustTesting at buffer, but you have not destructed it! At the same time, you're creating yet another object at the same location. Then, the first object becomes corrupted (althought, in standardese minds, it still exists in a parallel universe).

You can't perform delete on a pointer to anything that hasn't been allocated (and constructed) by operator new. Similarly, you can only destroy and deallocate an array created by operator new[] with operator delete[].

Now, "placement new" is just a fancy name for a direct call to a constructor. Thus, new(buff) Type(...) is just a call to Types constructor with this set as buff. And, simmetric with what said above, you can only destroy what has been constructed.

If you use automatic storage, operator new, or whatever other implicit-RAII-conformant means that is responsible for allocating, constructing, and destructing your objects automatically (or when you specify it shall be done), then calling an object's destructor in such a context will lead to the destructor being called twice, a.k.a undefined behaviour.

Now, it happens that you (shall I repeat it again? you!) are the one who decides when and how to obtain the memory for the object, then the environment has no change of guessing when to either destroy or deallocate the object. Thus, once you call the object's destructor explicitly, the memory that once contained it is under your responsibility for being freed, somehow, if at all.

Think of it as such. An expression of the form ptr = new X(...) can be perfectly implemented as...

ptr = malloc(sizeof(X));
new(ptr) X(...);

And operator delete becomes...

ptr->~X();
free(ptr);

How to delete object constructed via placement new operator?


...instantiated via basic types as well (say int) so such code

t->~T(); is incorrect
...

Wrong. That code is legal and correct in template code even if T can be a primitive type.

C++ standard: 5.4.2

5.2.4 Pseudo destructor call [expr.pseudo]

  1. The use of a
    pseudo-destructor-name after a dot . or arrow -> operator represents
    the destructor for the non-class type named by type-name. The result
    shall only be used as the operand for the function call operator (),
    and the result of such a call has type void. The only effect is the
    evaluation of the postfix expression before the dot or arrow.
  2. The left hand side of the dot operator shall be of scalar type. The left
    hand side of the arrow operator shall be of pointer to scalar type.
    This scalar type is the object type. The type designated by the
    pseudo destructor- name shall be the same as the object type.
    Furthermore, the two type-names in a pseudodestructor- name of the
    form ::opt nested-name-specifieropt type-name :: ˜ type-name shall
    designate the same scalar type. The cv-unqualified versions of the
    object type and of the type designated by the pseudo-destructor-name
    shall be the same type.

c++ self-defined placement new and placement delete invoking

Placement delete is available only to handle exceptions which occur during evaluation of a placement new expression. If construction finishes successfully, then later on normal delete will be used.

You can call a placement deallocation function explicitly, but it won't have the same behavior as the delete operator (it won't call the destructor automatically).

In your case, the corresponding code would be:

a->~A();
A::operator delete(a, 4);

Yuck!

For arrays it is even worse, because you can't retrieve the number of elements (and number of destructors to call) from the location where the compiler stored that for its own use.

Design your overloaded operator new so that it pairs correctly with one-argument operator delete. Then users of your class can use delete ptr; and std::unique_ptr etc.

If you do require custom deallocation, then an allocation wrapper which returns std::shared_ptr with a custom deleter will be better than custom placement new.



Related Topics



Leave a reply



Submit