6 Basics [basic]

6.7 Memory and objects [basic.memobj]

6.7.3 Lifetime [basic.life]

The lifetime of an object or reference is a runtime property of the object or reference.
A variable is said to have vacuous initialization if it is default-initialized and, if it is of class type or a (possibly multi-dimensional) array thereof, that class type has a trivial default constructor.
The lifetime of an object of type T begins when:
  • storage with the proper alignment and size for type T is obtained, and
  • its initialization (if any) is complete (including vacuous initialization) ([dcl.init]),
except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union ([dcl.init.aggr], [class.base.init]), or as described in [class.union] and [class.copy.ctor], and except as described in [allocator.members].
The lifetime of an object o of type T ends when:
  • if T is a non-class type, the object is destroyed, or
  • if T is a class type, the destructor call starts, or
  • the storage which the object occupies is released, or is reused by an object that is not nested within o ([intro.object]).
The lifetime of a reference begins when its initialization is complete.
The lifetime of a reference ends as if it were a scalar object requiring storage.
[Note
:
[class.base.init] describes the lifetime of base and member subobjects.
— end note
]
The properties ascribed to objects and references throughout this document apply for a given object or reference only during its lifetime.
[Note
:
In particular, before the lifetime of an object starts and after its lifetime ends there are significant restrictions on the use of the object, as described below, in [class.base.init] and in [class.cdtor].
Also, the behavior of an object under construction and destruction might not be the same as the behavior of an object whose lifetime has started and not ended.
[class.base.init] and [class.cdtor] describe the behavior of an object during its periods of construction and destruction.
— end note
]
A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling a destructor or pseudo-destructor ([expr.prim.id.dtor]) for the object.
For an object of a class type, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression is not used to release the storage, the destructor is not implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated29 or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.
For an object under construction or destruction, see [class.cdtor].
Otherwise, such a pointer refers to allocated storage ([basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of type void* is well-defined.
Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below.
The program has undefined behavior if:
  • the object will be or was of a class type with a non-trivial destructor and the pointer is used as the operand of a delete-expression,
  • the pointer is used to access a non-static data member or call a non-static member function of the object, or
  • the pointer is implicitly converted ([conv.ptr]) to a pointer to a virtual base class, or
  • the pointer is used as the operand of a static_­cast ([expr.static.cast]), except when the conversion is to pointer to cv void, or to pointer to cv void and subsequently to pointer to cv char, cv unsigned char, or cv std​::​byte ([cstddef.syn]), or
  • the pointer is used as the operand of a dynamic_­cast ([expr.dynamic.cast]).
[Example
:
#include <cstdlib>

struct B {
  virtual void f();
  void mutate();
  virtual ~B();
};

struct D1 : B { void f(); };
struct D2 : B { void f(); };

void B::mutate() {
  new (this) D2;    // reuses storage --- ends the lifetime of *this
  f();              // undefined behavior
  ... = this;       // OK, this points to valid memory
}

void g() {
  void* p = std::malloc(sizeof(D1) + sizeof(D2));
  B* pb = new (p) D1;
  pb->mutate();
  *pb;              // OK: pb points to valid memory
  void* q = pb;     // OK: pb points to valid memory
  pb->f();          // undefined behavior: lifetime of *pb has ended
}
— end example
]
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways.
For an object under construction or destruction, see [class.cdtor].
Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using the properties of the glvalue that do not depend on its value is well-defined.
The program has undefined behavior if:
  • the glvalue is used to access the object, or
  • the glvalue is used to call a non-static member function of the object, or
  • the glvalue is bound to a reference to a virtual base class ([dcl.init.ref]), or
  • the glvalue is used as the operand of a dynamic_­cast ([expr.dynamic.cast]) or as the operand of typeid.
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if the original object is transparently replaceable (see below) by the new object.
An object is transparently replaceable by an object if:
  • the storage that occupies exactly overlays the storage that occupied, and
  • and are of the same type (ignoring the top-level cv-qualifiers), and
  • is not a complete const object, and
  • neither nor is a potentially-overlapping subobject ([intro.object]), and
  • either and are both complete objects, or and are direct subobjects of objects and , respectively, and is transparently replaceable by .
[Example
:
struct C {
  int i;
  void f();
  const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();                 // lifetime of *this ends
    new (this) C(other);        // new object of type C created
    f();                        // well-defined
  }
  return *this;
}

C c1;
C c2;
c1 = c2;                        // well-defined
c1.f();                         // well-defined; c1 refers to a new object of type C
— end example
]
[Note
:
If these conditions are not met, a pointer to the new object can be obtained from a pointer that represents the address of its storage by calling std​::​launder ([ptr.launder]).
— end note
]
If a program ends the lifetime of an object of type T with static, thread, or automatic storage duration and if T has a non-trivial destructor,30 the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined.
This is true even if the block is exited with an exception.
[Example
:
class T { };
struct B {
   ~B();
};

void h() {
   B b;
   new (&b) T;
}                               // undefined behavior at block exit
— end example
]
Creating a new object within the storage that a const complete object with static, thread, or automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
[Example
:
struct B {
  B();
  ~B();
};

const B b;

void h() {
  b.~B();
  new (const_cast<B*>(&b)) const B;     // undefined behavior
}
— end example
]
In this subclause, “before” and “after” refer to the “happens before” relation ([intro.multithread]).
[Note
:
Therefore, undefined behavior results if an object that is being constructed in one thread is referenced from another thread without adequate synchronization.
— end note
]
For example, before the construction of a global object that is initialized via a user-provided constructor ([class.cdtor]).
That is, an object for which a destructor will be called implicitly—upon exit from the block for an object with automatic storage duration, upon exit from the thread for an object with thread storage duration, or upon exit from the program for an object with static storage duration.