Reputation: 4057
Ignoring usefulness of such practice. (Though real-life examples are welcome, of course.)
For example, the following program outputs the correct value for a
:
#include <iostream>
using namespace std;
int main()
{
int a = 11111;
int i = 30;
int* pi = new (&i) int();
cout << a << " " << endl;
}
But isn't new-allocation supposed to create some bookkeeping information adjacent to i
(for correct subsequent deallocation), which in this case is supposed to corrupt the stack around i
?
Upvotes: 11
Views: 2468
Reputation: 303337
Yes, it's perfectly OK to perform placement-new with a pointer to an object on the stack. It will just use that specific pointer to construct the object in. Placement-new isn't actually allocating any memory - you have already provided that part. It only does construction. The subsequent deletion won't actually be delete
- there is no placement delete
- since all you need to do is call the object's destructor. The actual memory is managed by something else - in this case your stack object.
For example, given this simple type:
struct A {
A(int i)
: i(i)
{
std::cout << "make an A\n";
}
~A() {
std::cout << "delete an A\n";
}
int i;
};
The following is completely reasonable, well-behaved code:
char buf[] = {'x', 'x', 'x', 'x', 0};
std::cout << buf << std::endl; // xxxx
auto a = new (buf) A{'a'}; // make an A
std::cout << a->i << std::endl; // 97
a->~A(); // delete an A
The only case where this would be invalid would be if your placement-new-ed object outlasts the memory you new-ed it on - for the same reason that returning a dangling pointer is always bad:
A* getAnA(int i) {
char buf[4];
return new (buf) A(5); // oops
}
Upvotes: 9
Reputation: 63144
No, because you don't delete
an object which has been placement-newed, you call its destructor manually.
struct A {
A() { std::cout << "A()\n"; }
~A() { std::cout << "~A()\n"; }
};
int main()
{
alignas(A) char storage[sizeof(A)];
A *a = new (storage) A;
std::cout << "hi!\n";
a->~A();
std::cout << "bye!\n";
}
Output:
A()
hi!
~A()
bye!
In your case, there's no need to call a destructor either, because int
is trivially-destructible, meaning that its destructor would be a no-op anyway.
Beware though not to invoke placement-new on an object that is still alive, because not only will you corrupt its state, but its destructor will also be invoked twice (once when you call it manually, but also when the original object should have been deleted, for exampel at the end of its scope).
Upvotes: 1
Reputation: 16156
Placement new does construction, not allocation, so there's no bookkeeping information to be afraid of.
I can at the moment think of one possible use case, though it (in this form) would be a bad example of encapsulation:
#include <iostream>
using namespace std;
struct Thing {
Thing (int value) {
cout << "such an awesome " << value << endl;
}
};
union Union {
Union (){}
Thing thing;
};
int main (int, char **) {
Union u;
bool yes;
cin >> yes;
if (yes) {
new (&(u.thing)) Thing(42);
}
return 0;
}
Though even when the placement new is hidden in some member function, the construction still happens on the stack.
So: I didn't look in the standard, but can't think of why placement new on the stack shouldn't be permitted.
A real world example should be somewhere in the source of https://github.com/beark/ftl ... in their recursive union, which is used for the sum type.
Upvotes: 0
Reputation: 24956
Placement new constructs the element in place and does not allocate memory.
The "bookkeeping information" in this case is the returned pointer which ought to be used to destroy the placed object.
There is no delete associated with the placement since placement is a construction. Thus, the required "clean up" operation for placement new is destruction.
The "usual steps" are
(Where memory can be stack memory which is neither required to be explicitly allocated nor deallocated but comes and goes with a stack array or object.)
Note: If "placing" an object into memory of the same type on the stack one should keep in mind that there's automatic destruction at the end of the object's lifetime.
{
X a;
a.~X(); // destroy a
X * b = new (&a) X(); // Place new X
b->~X(); // destroy b
} // double destruction
Upvotes: 2