忽略这种做法的有用性。 (当然,欢迎现实生活中的例子。)
例如,以下程序输出a
的正确值:
#include <iostream>
using namespace std;
int main()
{
int a = 11111;
int i = 30;
int* pi = new (&i) int();
cout << a << " " << endl;
}
但是新的分配不应该创建一些与i
相邻的簿记信息(用于正确的后续解除分配),在这种情况下应该破坏i
周围的堆栈?
答案 0 :(得分:6)
是的,使用指向堆栈上对象的指针执行placement-new是完全可以的。它只会使用该特定指针来构造对象.Place-new isn实际上分配任何内存 - 您已经提供了该部分。它只做构建。随后的删除实际上不会是delete
- there is no placement delete
- 因为您需要做的就是调用对象的析构函数。实际的内存由其他东西管理 - 在这种情况下是你的堆栈对象。
例如,给定这种简单类型:
struct A {
A(int i)
: i(i)
{
std::cout << "make an A\n";
}
~A() {
std::cout << "delete an A\n";
}
int i;
};
以下是完全合理,表现良好的代码:
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
唯一无效的情况是,如果你的贴牌新对象比你新推出的内存要长 - 原因与返回悬空指针总是坏的相同:
A* getAnA(int i) {
char buf[4];
return new (buf) A(5); // oops
}
答案 1 :(得分:1)
不,因为你没有delete
一个已经放置新位置的对象,你可以手动调用它的析构函数。
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";
}
输出:
A()
hi!
~A()
bye!
在你的情况下,也没有必要调用析构函数,因为int
是可以轻易破坏的,这意味着它的析构函数无论如何都是无操作的。
请注意不要在仍处于活动状态的对象上调用placement-new,因为它不仅会破坏其状态,而且还会调用它的析构函数两次(一次是手动调用它时,也是原始对象时)应该被删除,例如在其范围的末尾)。
答案 2 :(得分:0)
新的安置是建筑,而不是分配,因此没有簿记信息可以担心。
我现在可以想到一个可能的用例,尽管它(以这种形式)将是一个错误的封装示例:
#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;
}
尽管在某些成员函数中隐藏了placement new,但仍然会在堆栈中进行构造。
所以:我没有查看标准,但是不能想到为什么不允许在堆栈上放置新内容。
一个真实世界的例子应该在https://github.com/beark/ftl的源代码中的某个位置,在它们的递归联合中,用于求和类型。