Object lifetime

< cpp‎ | language
Revision as of 09:10, 23 March 2014 by Cubbi (Talk | contribs)

Every object has a lifetime, which is a runtime property: for any object, there is a moment during the execution of a program when its lifetime begins, and there is a moment when it ends.

  • For objects of class or aggregate types that are not trivially-constructible, lifetime begins when initialization ends.
  • For objects of class types that are not trivially-destructible, lifetime ends when the execution of the destructor begins.
  • For all other objects (trivial class objects, non-class objects, array objects, etc), lifetime begins when the properly-aligned storage for the object is allocated and ends when the storage is deallocated or reused by another object.

Lifetime of an object is equal to or is nested within the lifetime of its storage, see storage duration.

Lifetime of a reference is exactly its storage duration (which makes dangling references possible)

Lifetimes of member objects and base subobjects begin and end following class initialization order.

Temporary object lifetime

Temporary objects are created in various situations: binding a reference to a prvalue, returning a prvalue from a function, cast to a prvalue, throwing an exception, entering an exception handler, and in some initializations. In every case, all temporaries are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created, and if multiple temporaries were created, they are destroyed in the order opposite to the order of creation. This is true even if that evaluation ends in throwing an exception.

There are two exceptions from that:

1. The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.

2. The lifetime of a temporary created when evaluating the default arguments of a default constructor used to initialize an element of an array ends before the next element of the array begins initialization. (since C++11)

Storage reuse

A program is not required to call the destructor of an object to end its lifetime if the object is trivially-destructible or if the program does not rely on the side effects of the destructor. However, if a program ends the lifetime of an non-trivial object, it must ensure that a new object of the same type is constructed in-place (e.g. via placement new) before the destructor may be called implicitly, i.e. due to scope exit or exception for automatic objects, due to thread exit for thread-local objects, or due to program exit for static objects; otherwise the behavior is undefined.

class T {}; // trivial
struct B {
    ~B() {} // non-trivial
void x() {
    long long n; // automatic, trivial
    new (&n) double(3.14); // reuse with a different type okay
} // okay
void h() {
    B b; // automatic non-trivially destructible
    b.~B(); // end lifetime (not required, since no side-effects)
    new (&b) T; // wrong type: okay until the destructor is called
} // destructor is called: undefined behavior

It is undefined behavior to reuse storage that is or was occupied by a const object of static, thread-local, or automatic storage duration.

struct B {
    B(); // non-trivial
    ~B(); // non-trivial
const B b; // const static
void h() {
    b.~B(); // end the lifetime of b
    new (const_cast<B*>(&b)) const B; // undefined behavior: attempted reuse of a const

Access outside of lifetime

Pointers and references to storage before initialization or after destruction may be used in limited ways, see dangling references for details.

During construction and destruction, other restrictions apply, see virtual function calls during construction and destruction.