我有以下代码:
struct foo {};
void bar(foo *d) {
new(d) foo(*d);
}
表达式new(d) foo(*d)
是否保持d
指向的对象不变?更具体地说,如果类foo
和递归包含的所有对象只有普通的拷贝构造函数,那么上面是真的,那么new(d) foo(*d)
是否*d
保持不变?在不需要的情况下,new
在调用复制构造函数之前首先将内存清零。 C ++语言中是否有这样的子句?
编辑:有些人想要这样做是非常重要的。考虑在地址空间中复制对象,例如,从CPU内存到GPU内存。一种解决方案是对对象进行逐字节操作。这适用于很多情况。如果该类具有虚方法,则逐字节副本将复制vtable指针,然后该指针将指向某些CPU内存。可以在对象上使用上面的表达式new(d) foo(*d)
来强制编译器重置vtable指针。
答案 0 :(得分:2)
关于是否将新零放置在内存中,它不会,它只是调用适当的构造函数来执行构造函数所做的任何操作,这可能会将内存归零或取决于它的定义方式。在这种特殊情况下,您使用的是复制构造函数
截至您提供的代码,它是未定义的行为。 d
指向有效对象或不指向有效对象。如果它引用了一个有效的对象,那么你在一个已经构造的对象上调用一个构造函数,如果该对象有一个非平凡的析构函数,那么这是一个未定义的行为。如果它之前尚未初始化(即它没有引用foo
对象),那么从中复制它是未定义的行为。
答案 1 :(得分:1)
我认为这是未定义的行为:对象的生命周期一旦存储它的内存被用于其他东西就结束。当您输入复制构造函数且this
指针等于d
时,原始对象就不再存在(就语言而言),因此您在副本中有一个悬空引用构造
当然更简单的是~foo()
有效的情况,在这种情况下,你还有另一个未定义行为的原因。
答案 2 :(得分:1)
我在研究性能问题时遇到过这个问题。在包含大缓冲区的对象上使用placement new的一些代码意外地很慢。原因是:在调用构造函数之前,placement new将内存清零。
我对标准的阅读与其他答案一致:编译器不需要特别做任何事情。
然而,gcc 4.9,gcc 5.3,clang 3.4,clang 3.8和Apple clang似乎都在放置新案例时将内存归零。检查汇编程序输出,在调用构造函数之前会显式调用memset
。堆栈构造的对象不是零初始化的,因此它似乎不是构造函数来完成工作。
检查来自Dignus Systems / C ++ for z / OS的汇编程序输出似乎也调用了一个库函数,可能做了类似的事情(并且很慢)。
所以:Placement new允许将内存清零,似乎很多实现都会将内存清零。
示例测试用例:
#include <new>
#include <cstdint>
#include <stdio.h>
struct Test {
char b[4];
void show(char const* prefix) {
for (unsigned i = 0; i < sizeof(b); ++i)
printf("%s index %d: %d\n", prefix, i, b[i]);
}
};
int main()
{
char* p = new char[sizeof(Test)];
for (unsigned i = 0; i < sizeof(Test); ++i)
p[i] = 'Q';
Test* t1 = new(p) Test();
Test t2;
t1->show("t1");
t2.show("t2");
}
示例输出(FreeBSD上的clang 3.4):
t1 index 0: 0
t1 index 1: 0
t1 index 2: 0
t1 index 3: 0
t2 index 0: 51
t2 index 1: -1
t2 index 2: 3
t2 index 3: 1
答案 3 :(得分:0)
Placement new唯一的工作是在内存上运行构造函数,该构造函数是为尚未初始化的对象预留的。如果你手动调用构造函数(尽管那是不可能的),它不会做什么,也就是你所得到的。
答案 4 :(得分:0)
请注意,您正在调用对象的复制构造函数,并将其自身作为copy-from。我希望这只是坚果。谁知道。我没有看到标准中的任何内容会让我期待任何事情。