3

I have the following code :

struct foo {};
void bar(foo *d) {
  new(d) foo(*d);
}

Does the expression new(d) foo(*d) leave the object pointed to by d unchanged? More specifically, is the above true if the class foo and all the objects contained recursively within it only have trivial copy constructors, then does new(d) foo(*d) leave *d unchanged? A situation in which that is not true could be, new first zeroes out the memory before calling the copy constructor. Are there such clauses in the C++ language?

Edit : There are non-trivial reasons why someone would want to do this. Consider copying objects across address spaces, say, from CPU memory to GPU memory. One solution for that is to do a byte-by-byte of the object. This works in lot of cases. If the class has virtual methods, then the byte-by-byte copy copies the vtable pointer, which would then be pointing to some CPU memory. One can use the above expression new(d) foo(*d) on the object to force the compiler to reset the vtable pointer.

keveman
  • 8,427
  • 1
  • 38
  • 46
  • http://stackoverflow.com/questions/2204176/how-to-initialise-memory-with-new-operator-in-c – m0skit0 May 07 '12 at 22:50
  • I'd expect to get some rather deranged results if you did this. – Edward Strange May 07 '12 at 22:52
  • Why would it? Would you expect non-placement-`new` to zero out memory? POD members that aren't explicitly initialized in the constructor have garbage values. – jamesdlin May 07 '12 at 22:53
  • 2
    Regarding your edit: I daresay that every time you feel tempted to reason about some fictitious "vtable pointer", you're about to do something wrong... – Kerrek SB May 07 '12 at 23:31

6 Answers6

2

On whether placement new zeroes out the memory, it does not, it just calls the appropriate constructor that will do whatever that constructor does, which could zero out the memory or not depending on how it is defined. In this particular case you are using the copy constructor

As of the code that you present, it is undefined behavior. Either d points to a valid object or it does not. If it refers to a valid object you are calling a constructor on an already constructed object, which is undefined behavior if the object has a non-trivial destructor. If it has not been initialized before (i.e. it does not refer to a foo object), then it is undefined behavior to copy from it.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    I don't think that "calling the constructor on an already-initialized object" is categorically wrong. If the destructor has no effects, then I believe that's OK. See 3.8(1). – Kerrek SB May 07 '12 at 23:33
  • I agree with everything you say, except the assertion that placement new does not zero out the memory. I can't see the standard making a requirement one way or another and many implementations do zero out the memory. – janm Aug 02 '16 at 10:55
2

I have just come across this question while researching a performance problem. Some code that uses placement new on objects which contained large buffers was unexpectedly slow. The reason: placement new was zeroing out the memory before calling the constructor.

My reading of the standard is in agreement with the other answers: The compiler is not required to do anything in particular.

However, gcc 4.9, gcc 5.3, clang 3.4, clang 3.8 and Apple clang all seem to zero out memory in the placement new case. Examining the assembler output, there is an explicit call to memset before calling the constructor. Stack constructed objects are not zero-initialised, so it doesn't seem to be constructor doing the work.

Examining assembler output from Dignus Systems/C++ for z/OS also seems to call a library function, presumably doing something similar (and slow).

So: Placement new is allowed to zero out the memory and it seems that many implementations do zero out memory.

Example test case:

#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");
}

Example output (clang 3.4 on FreeBSD):

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
janm
  • 17,976
  • 1
  • 43
  • 61
1

I believe this is undefined behaviour: The lifetime of an object ends once the memory in which it is stored is used for something else. The moment you enter the copy constructor with the this pointer being equal to d, the original object ceases to exist (as far as the language is concerned), and so you have a dangling reference in the copy constructor.

Even easier of course is the situation where ~foo() has effects, in which case you have yet another reason for undefined behaviour.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • It is undefined behavior if `d` points to a valid object for the reasons you point out, and if `d` points to raw memory it is still UB as it is being used as argument to the copy constructor. – David Rodríguez - dribeas May 07 '12 at 23:15
  • @DavidRodríguez-dribeas: Right. I assumed that `*d` was a valid object; otherwise the game is over right there and you go to jail without collecting $200. – Kerrek SB May 07 '12 at 23:20
  • @KerrekSB That would be the incorrect assumption for any "proper" code as in-place new is meant to be used on uninitialized memory only. – Mahmoud Al-Qudsi May 07 '12 at 23:27
  • @MahmoudAl-Qudsi: That's not true. If the destructor has no effects, you are well in your right to "overwrite" an existing object. See the standard section on "object lifetime". – Kerrek SB May 07 '12 at 23:28
1

Yes and No.

I think we are getting into compiler defined behavior here. But, for Visual C++ 2019, placement new will clear the data if there is no default constructor. See a snippet below.

int buffer[4];

class some_class
{
public:
    int a, b, c;
};

class some_class2
{
public:
    int a, b, c;

    some_class2()
    {
        ;
    }
};

buffer[0] = 1;
buffer[1] = 2;
buffer[2] = 3;
some_class* c = new (buffer) some_class();
std::cout << "placement new with a class clears it" << std::endl;
std::cout << c->a << " " << c->b << " " << c->c << std::endl;

buffer[0] = 1;
buffer[1] = 2;
buffer[2] = 3;
some_class2* d = new (buffer) some_class2();
std::cout << "placement new with a class that has a constructor does not clear it" << std::endl;
std::cout << d->a << " " << d->b << " " << d->c << std::endl;
TJ Bandrowsky
  • 842
  • 8
  • 12
0

Placement new's only job is to run the constructor on memory that has been set aside for an object that has not yet been initialized. It does nothing more and nothing less than what you would get if you manually called the constructor (though that is not possible) instead.

Mahmoud Al-Qudsi
  • 28,357
  • 12
  • 85
  • 125
  • The first sentence is misleading. There doesn't have to be "an object that already exists", and in general that would be undefined behaviour, and in any event the object would stop existing by the time the constructor started. Rather, placement-new constructs an object *at a given memory location*. – Kerrek SB May 07 '12 at 23:14
  • Meh. It's an object if it's cast as object, so if you allocated that memory and assigned to a pointer of that object type, then it's an object - just not one that's been initialized as of yet. I updated the post for accuracy. – Mahmoud Al-Qudsi May 07 '12 at 23:26
  • @MahmoudAl-Qudsi: Not really, it is an object if it has been constructed and not destroyed, it is a block of memory before construction and after destruction. – David Rodríguez - dribeas May 07 '12 at 23:28
  • The standard has a very clear concept of "object lifetime". If you prefer to think about C++ differently in your own inner monologue, that's fine, but for the sake of answering the question, I think we should stick to what the standard specifies. – Kerrek SB May 07 '12 at 23:29
0

Note that you are calling the copy constructor of an object with itself as the copy-from. I would expect this to go simply nuts. Who knows. I don't see anything in the standard that would lead me to expect anything.

Edward Strange
  • 40,307
  • 7
  • 73
  • 125