Context
I'm trying to get a grasp on the placement-new mechanism since I never have had to use it. I'm trying to understand how to properly use it out of pure curiosity.
For the question, we will consider the following code base for illustration purposes:
struct Pack
{
int a, b, c, d;
Pack() = default;
Pack(int w, int x, int y, int z) : a(w), b(x), c(y), d(z)
{}
~Pack()
{
std::cout << "Destroyed\n";
}
};
std::ostream & operator<<(std::ostream & os, const Pack & p)
{
os << '[' << p.a << ", " << p.b << ", " << p.c << ", " << p.d << ']';
return os;
}
Basically it just defines a structure holding some data (4 integers in the example), and I overloaded the operator<<() to simplify the code in the testing part.
I know that when using placement-new, one has to manually call the destructor of the object because we can't use delete since we only want to destroy the object (and not to free the memory since it was already allocated).
Example (A):
char b[sizeof(Pack)];
Pack * p = new (b) Pack(1, 2, 3, 4);
std::cout << *reinterpret_cast<Pack*>(b) << '\n';
p->~Pack();
Question
I was wondering, when using placement-new into an object of the same type, is it still necessary to call the destructor of the object ?
Example (B):
Pack p;
new (&p) Pack(1, 2, 3, 4);
std::cout << p << '\n';
I did a quick test of both version here and it seems that the destructor is properly called when the underlying object goes out of scope while being of type Pack.
I know it's stupid to use placement-new in such a trivial example instead of directly creating the object. But in a real-case example, it can be a buffer or Pack instead of a buffer of char.
I would like to know if I am right to assume that I don't need to manually call the destructor in the example (B) or if I'm missing something.
Extra
As a subsidiary question, in the example (A), is it legal to get rid of the returned pointer and call the destructor through the reinterpret_casted pointer instead ?
For example can it be rewritten like this:
char b[sizeof(Pack)];
new (b) Pack(1, 2, 3, 4);
std::cout << *reinterpret_cast<Pack*>(b) << '\n';
reinterpret_cast<Pack*>(b)->~Pack(); // Is it legal ?
It would be useful in the case I have a buffer of objects and I want to destroy its elements without having to keep the returned pointers somewhere.
[Disclaimer]: Of course, in a real case program, I would use a std::vector and the emplace_back() function instead of my own buffer. As already mentioned, this question is purely out of curiosity, just to understand how it's meant to work under the hood.