2

I cannot resolve the following issue by myself:

Suppose we are reusing a memory in a following way:

struct A 
{ 
    int a;
    A(){ }
    ~A(){ }
};

struct B : A 
{ 
    int b;
    B(){ }
    ~B(){ }
};

A *a = (A*) malloc(sizeof(A) + sizeof(B));
B *b = new (a) B; //Constructor of B is calling

The lifetime of object reffered to by a has ended before the constructor of B is starting to call or it has ended when the constructor of B has finished?

St.Antario
  • 26,175
  • 41
  • 130
  • 318
  • @RSahu _Lifetime of object that a points to does not end until you call delete a;_ It is not quite so. Because 3.8 said that the lifetime of an object of type T ended when if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or the storage which the object occupies is reused or released. – St.Antario Sep 08 '14 at 05:00
  • @St.Antario: What are you trying to accomplish by doing this? First of all, `new A` is only guaranteed to allocate enough memory to hold an instance of `A`, not an instance of `B`. Placement new (which is what `new (a) B` is) performs no memory allocation; it just constructs `B` at where `a` points to. What you have is undefined behavior and is not at all guaranteed to work. – In silico Sep 08 '14 at 05:01
  • @Insilico I understand that it is UB. But I'm trying to enquire a _details_ and to find a formal explanantion using the Standard. – St.Antario Sep 08 '14 at 05:05

4 Answers4

1

You try to use the placement new operator to initialize b. This operator does not call the destructor of class A first (a), but initializes a new one into the memory pointed to by a. This is problematic (as mentioned in the comment), because the sizeof(B) is greater than sizeof(A) and you allocated only sizeof(A) at the pointer a. So it is undefined behavior.

The lifetime of the object a formally does not end. You get something like:

class A { int a; };
void* p = malloc(sizeof(A));
A* a1 = new (p) A();
A* a2 = new (p) A();

I think, you will get something like double called destructor on the same memory, but that is something compiler-implementation specific. I don't think, that the standard does say anything about this.

  • _I don't think, that the standard does say anything about this._ Hmmm... Assume that we have to reuse memory in the following way `struct C{ C(){ } ~C(){ } }; C *c = new C; new (c) C;`. The code snippet shall not produce UB. The lifetime of original c ends when the constructor for new c is starting or the constructor of new c has called? – St.Antario Sep 08 '14 at 05:17
  • The original `c` is nowhere deleted. So if you allocate any resources (e.g.) files in `C`, you won't get them freed until `delete` is called. Then you reuse this memory piece and this will result in UB too, because two objects are initialized in the same piece of memory (see http://stackoverflow.com/a/1022332/4010250). – Zerschmetterling Sep 08 '14 at 05:24
  • _Then you reuse this memory piece and this will result in UB too_ I don't agree because the Standard has relevant section about this. See http://stackoverflow.com/questions/25657435/does-reuse-storage-start-lifetime-of-a-new-object – St.Antario Sep 09 '14 at 04:09
1

As soon as you enter the constructor of B, a is not pointing to an object A any more.

The reason is that even before the first instruction of the constructor of an object the runtime already has done the VMT, base sub-objects and member initialization.

Also if the constructor of B doesn't terminate because of an exception the memory has already been used anyway and another object eventually present at the same memory address doesn't exist any more.

In other words just don't do that... in C++ objects are not just a pile of bytes and they have rights; for example the right of having their destructor called ;-)

6502
  • 112,025
  • 15
  • 165
  • 265
0

The standard from the relevant section (3.8) says,

The lifetime of an object of type T ends when:

- if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or

- the storage which the object occupies is reused or released.

I interpret to mean that the life time of the object pointed to by a ends when members of B are initialized. Until that happens, the storage is not reused.

Community
  • 1
  • 1
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

The lifetime of a ends when you delete it and not before - since forcing two different objects to occupy the same space this way is itself undefined behaviour and definitely not recommended your compiler may or may not treat that memory as available for reuse and overwrite b.

If you need to have two objects occupying the same location, e.g.: Message Variants or you are trying to write your own debugger then you should use a union type.

The other reason that you are not recommended to do this sort of thing is that you will create a maintenance nightmare. e.g. later in your code you could have:

b.b = 3
while (a.a > 0) {
   b.b--;
}
Steve Barnes
  • 27,618
  • 6
  • 63
  • 73