贴片是否会将内存归零?

时间:2012-05-07 22:49:08

标签: c++ new-operator placement-new

我有以下代码:

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指针。

5 个答案:

答案 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。我希望这只是坚果。谁知道。我没有看到标准中的任何内容会让我期待任何事情。