Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overwriting an object with an object of same type

Is the following well defined?

#include <iostream>
#include <string.h>

using namespace std;

struct Const {
    const int i; 
    Const (int i) : i(i) {}
    int get0() { return 0; } // best accessor ever!
};

int main() {
    Const *q,*p = new Const(1);
    new (p) Const(2);
    memcpy (&q, &p, sizeof p);
    cout << q->i;
    return 0;
}

Note that after construction of second Const, p doesn't semantically (intentionally?) points to new object, and the first is gone, so it is usable "as a void*". But the second object is constructed at the exact same address, so the bit pattern of p represents the address of the new object.

COMMENT

new (p) Const(2) erase the old object stored at p, so the pointer is not valid anymore, except as a pointer to storage (void*).

I want to recover the value of p as a Const*.

COMMENT 2

After either p->~Const() or memset (p, 0, sizeof *p) it is clear that p does not point to a valid object, so p can only be used as pointer to storage (void* or char*), for example to reconstruct another object. At that point p->get0() is not allowed.

Here the demolition of the old object is done by the constructor of the new one, but I don't think that makes a difference.

My intuition is that: In any case, the old object is gone, and p points to the old object, not the new one.

I am looking for a confirmation or refutation based on the standard.

SEE ALSO

I have asked essentially the same question about pointers, in C and C++ :

  • Dereferencing an out of bound pointer that contains the address of an object (array of array)
  • Is memcpy of a pointer the same as assignment?
  • Are pointer variables just integers with some operators or are they "mystical"?

Please read these discussions before answering "this is ridiculous".

like image 836
curiousguy Avatar asked Jan 08 '23 18:01

curiousguy


1 Answers

(making community-wiki as incorporating dyp's comment re 3.8/7 is very significant; while my earlier analysis was correct I would have said much the same things about code that was broken, having overlooked 3.8/7 myself)

Const *q,*p = new Const(1);
new (p) Const(2);

The new(p) Const(2); line overwrites the object that had been constructed with Const(1).

memcpy (&q, &p, sizeof p);

This is equivalent to q = p;.

cout << q->i;

This accesses the q->i member, which will be 2.

The somewhat noteworthy things are:

  • std::memcpy is an ugly way to assign p to q... it is legal though under 3.9/3:

For any trivially copyable type T, if two pointers to T point to distinct T objects obj1 and obj2, where neither obj1 nor obj2 is a base-class subobject, if the underlying bytes (1.7) making up obj1 are copied into obj2, obj2 shall subsequently hold the same value as obj1. [ Example:

T* t1p;
T* t2p;
// provided that t2p points to an initialized object ...
std::memcpy(t1p, t2p, sizeof(T));
// at this point, every subobject of trivially copyable type in *t1p contains
// the same value as the corresponding subobject in *t2p
  • The overwriting of the old Const(1) object with Const(2) is allowed as long as the program doesn't depend on side effects of the former's destructor, which it doesn't.

  • (as dyp noted in comments below) ongoing access to the Const(2) object using p is illegal under 3.8/7's third point:

pointer that pointed to the original object [...] can be used to manipulate the new object, if...

  • the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type ...
  • using q - rather than p - to access i is presumably necessary to avoid compiler optimisations based on presumed knowledge of i.

As for your commentary:

Note that after construction of second Const, p doesn't semantically (intentionally?) points to new object, and the first is gone, so it is usable "as a void*".

Given you placement-new an object at the address contained in p, p most certainly does point to the newly created object, and very intentionally, but it can't be used to manipulate that object under 3.8/7 as above.

Given you seem to have a notion of "semantically pointing" that's not defined in C++ the truth of that part of the statement's in your own mind.

'after construction of second Const, p...is usable "as a void*' makes no sense... it's not more usable as anything than it was beforehand.

But the second object is constructed at the exact same address, so the bit pattern of p represents the address of the new object.

Of course, but your comments show you think "bit pattern" is somehow distinct from the value of the pointer as applies to assignment with =, which is not true.

new (p) Const(2) erase the old object stored at p, so the pointer is not valid anymore, except as a pointer to storage (void*).

"erase" is a strange term for it... overwrites would be more meaningful. As dyp noted and explained above, 3.8/7 says you shouldn't "manipulate" the object p points to after the placement new, but the value and type of the pointer are unaffected by the placmeent new. Much as you can call f(void*) with a pointer to any type, the placement-new doesn't need to know or care about the type of the p expression.

After either p->~Const() or memset (p, 0, sizeof *p) it is clear that p does not point to a valid object, so p can only be used as pointer to storage (void* or char*), for example to reconstruct another object. At that point p->get0() is not allowed.

Most of that's true, if by "p can only be used" you mean the value of p at that time rather than the pointer itself (which can be of course also be assigned to). And you're trying to be a little too clever with the void* / char* thing - p remains a Const*, even if it's only used by placement new which doesn't care about the pointee type.

"I want to recover the value of p as a Const*."

The value of p was not changed after it was first initialised. placement-new uses the value - it does not modify it. There's nothing to recover as nothing was lost. That said, dyp's highlighted the need not to use p to manipulate the object, so while the value wasn't lost it's not directly usable as wanted either.

like image 164
6 revs Avatar answered Jan 15 '23 10:01

6 revs