Scope, Lifetime and Namespaces

Scope decides where a name can be used; lifetime decides how long an object exists. Namespaces group related names so that libraries — and your own code — don't collide. Together they keep large programs organised.

Block scope and lifetime

A variable declared in a block exists from its declaration to the closing brace, and is destroyed there. For objects that own resources, destruction means cleanup — the basis of RAII (lesson 30):

C++
#include <iostream>
#include <string>
#include <utility>

struct Announcer {
    std::string name;
    explicit Announcer(std::string n) : name(std::move(n)) { std::cout << "create " << name << '\n'; }
    ~Announcer() { std::cout << "destroy " << name << '\n'; }
};

int main() {
    Announcer outer{"outer"};
    {
        Announcer inner{"inner"};
        std::cout << "inside the block\n";
    }
    std::cout << "after the block\n";
}
Output
create outer
create inner
inside the block
destroy inner
after the block
destroy outer

Objects are destroyed in reverse order of creation. The ~Announcer function is a destructor (lesson 32).

Static local variables

A static local variable is created once, the first time execution reaches it, and lives until the program ends:

C++
#include <iostream>

int next_ticket() {
    static int counter = 100;
    return counter++;
}

int main() {
    for (int i = 0; i < 3; ++i) std::cout << next_ticket() << ' ';
    std::cout << '\n';
}
Output
100 101 102

Global variables

Variables outside any function live for the whole program and are visible everywhere in the file. Avoid mutable globals — any function can change them, making bugs hard to trace. Global constants (constexpr) are fine.

Namespaces

A namespace groups names. Code outside refers to them with :::

C++
#include <iostream>

namespace geometry {
    constexpr double pi = 3.14159265358979;

    double circle_area(double r) { return pi * r * r; }

    namespace imperial {
        double feet_to_metres(double feet) { return feet * 0.3048; }
    }
}

int main() {
    std::cout << geometry::circle_area(1.0) << '\n';
    std::cout << geometry::imperial::feet_to_metres(10) << '\n';
}
Output
3.14159
3.048

C++17 lets you write nested namespaces in one go: namespace geometry::imperial { … }.

using declarations and directives

FormEffectAdvice
std::vectorFully qualified nameClearest; the usual style
using std::vector;Brings one name into scopeFine in .cpp files and function bodies
using namespace std;Brings every name in std into scopeAvoid, and never in headers
namespace fs = std::filesystem;A short alias for a long namespaceHandy and safe

Here's the kind of clash using namespace std; invites — std::count already exists:

C++
#include <algorithm>
#include <iostream>
using namespace std;

int count = 0;

int main() {
    count++;
    cout << count << '\n';
}
Compiler error
main.cpp:8:5: error: reference to 'count' is ambiguous
main.cpp:9:13: error: reference to 'count' is ambiguous

Anonymous namespaces

An unnamed namespace makes its contents private to the current source file — the C++ equivalent of static at file scope:

C++
#include <iostream>

namespace {
    int helper(int x) { return x * 2; }   // invisible to other files
}

int main() {
    std::cout << helper(21) << '\n';
}
Output
42

Lesson 48 shows how this fits into multi-file programs.

Try it yourself

Put two conversion functions in a units namespace and call them with a namespace alias.

Try it yourself · main.cpp
Edit the code, copy it into main.cpp, then compile and run it with c++ -std=c++20 -Wall main.cpp -o main && ./main. How to set up a C++ compiler
Output
77 F, 100 C