Storage Duration, Lifetime, and Dangling
Static, thread, automatic, dynamic โ four clocks that decide when objects live and die. Most catastrophic C++ bugs are lifetime bugs in disguise.
Every object lives on one of four storage durations โ four different clocks:
- static โ created at program start, destroyed at exit: globals,
staticlocals,staticmembers. Initialization of function-local statics is thread-safe and lazy (C++11 "magic statics"). - thread โ
thread_local: one instance per thread, born at thread start, dies at thread end. - automatic โ locals: born at definition, destroyed when the scope exits (in reverse order of declaration).
- dynamic โ
new/deletedirectly (rare in modern code; ownership should live in RAII types).
Destruction order is a contract
Within a scope, destruction runs in reverse construction order. Members are destroyed in reverse declaration order โ after the destructor body runs. Locking discipline, mutex scoping, and cleanup code all depend on this:
struct Job {
std::ofstream log; // declared first -> destroyed last
std::lock_guard<std::mutex> g; // declared second -> destroyed first
Job(std::mutex& m) : log("job.log"), g(m) {}
};
Dangling: the lifetime bug family
A reference/pointer/view that outlives its object is dangling โ using it is undefined behavior, and the compiler usually will not stop you:
- returning a reference/pointer to a local,
- storing a
string_vieworspanover a temporary, - iterators invalidated by container mutation,
- captured-by-reference lambdas escaping their scope.
std::string_view bad() {
std::string local = "temporary";
return local; // compiles; the view dangles on return
}
Lifetime extension โ the narrow exception
A const reference bound to a temporary extends the temporary's lifetime to the reference's (and a range-for over a temporary container works for the loop's duration). But extension does not chain through function returns or past a constructor body into a reference member โ the most-surveyed "gotcha" in C++:
struct Holder {
const std::string& s;
Holder() : s("dangles!") {} // temporary dies at the closing brace
};
Defensive habit: views borrow โ never store a string_view/span/& in a type whose lifetime you cannot prove is shorter than the viewed object's. In this module's graded work you will repair dangling code and predict destruction order exactly.