Three related ideas cause a disproportionate share of C++ bugs when they are confused:
* Scope: where a name can be used.
* Lifetime: when an object begins and ends existence.
* Storage duration: the language category describing how long storage for an object lasts (automatic, static, thread-local, or dynamic).
They often align for simple locals, but they are not the same thing. An optimizer may eliminate storage entirely while lifetime rules still apply to the abstract object.
Scope kinds you will meet first
* Block scope: names declared inside { ... } are visible until the closing brace.
* Namespace scope: names in a namespace are visible according to ordinary lookup rules and using declarations.
* Class scope: members are visible to member functions and controlled by access specifiers (public, protected, private).
Storage duration in practice
* Automatic: local variables like int x{0};. Storage is tied to scope; destroyed when the block ends, including through an exception.
* Static: namespace-scope or function-local static objects. Function-local static is constructed on first pass; namespace static lives until program exit.
* Thread-local: thread_local gives each thread its own instance; destroyed when that thread ends.
* Dynamic: heap objects from new, or owners like std::vector. Released when the owner destroys them or calls delete.
Lesson 3.3 covers stack versus heap physically; this lesson is the language rule that drives those choices.
Lifetime is not extended by pointers or references
A pointer or reference observes an object; it does not keep that object alive. When the object's lifetime ends, every pointer and reference to it becomes invalid unless you have another owner extending storage.
Prefer values and explicit ownership
Returning by value is usually the safest default API. Modern C++ elides copies when possible (RVO/NRVO) and moves efficiently otherwise. Return a reference or view only when the caller supplies storage that outlives every use, or when you document a non-owning borrow with clear preconditions.
Static locals and RAII preview
A function-local static object is constructed the first time control passes its declaration and destroyed at program exit. That gives lazy initialization, but also means destructors run during shutdown, order can matter across translation units. Lesson 4.2 generalizes the idea with RAII: bind resource release to destructor calls tied to scope.
Thread-local storage (awareness)
thread_local gives each thread its own instance. It is useful for per-thread caches or identifiers, but it is not a substitute for synchronizing shared data, see Chapter 9.
Practice: rewrite brokenLabel three ways: (1) return std::string, (2) fill a caller-provided std::array<char, N>, (3) take std::string& out and assign. For each, state scope, lifetime, and who owns the characters.