Notes

Thoughts, write-ups, and things worth remembering.

static

#cpp#keywords Updated Sep 16, 2026

One keyword, three unrelated meanings depending on where you put it.

Static member variable — shared across every instance

class Enemy {
    static int count;   // one shared copy, not one per Enemy
};
int Enemy::count = 0;   // must be defined once, outside the class

Enemy::count++;   // accessed via the class, not an instance

Static member function — no this, callable without an instance

class MathUtils {
    static float clamp(float v, float lo, float hi);
};
MathUtils::clamp(5, 0, 1);   // no object needed at all

Can’t touch non-static members — there’s no this to find them on.

Static local variable — persists across calls

int nextId() {
    static int id = 0;   // initialized ONCE, on the first call ever
    return id++;
}

Keeps its value between calls, unlike an ordinary local which resets every time.

Static local object — lazy, thread-safe construction

class Logger {
public:
    Logger() { std::cout << "Logger created\n"; }
};

void doSomething() {
    static Logger logger;   // constructed ONCE, the first time this line runs
}

Not just for primitives — a static local can be a full class instance. It’s constructed the first time execution reaches its declaration, and C++11 guarantees this is thread-safe even if multiple threads hit doSomething() for the first time simultaneously — no manual locking needed. This exact pattern (a static local instance, returned by reference) is how a lazy, thread-safe Singleton gets implemented in modern C++.

Static global object — the initialization order fiasco

// file1.cpp
Config globalConfig;                     // static storage duration, built before main()

// file2.cpp
Logger globalLogger(globalConfig);       // uses globalConfig - is it ready yet?

Static objects at global/namespace scope are constructed before main() runs. Within one .cpp file, top-to-bottom order is guaranteed. Across different .cpp files, the order is undefined — globalLogger might construct before or after globalConfig, and that order can silently change between builds or compilers.

This is the “static initialization order fiasco.” The fix is the function-local static from above: wrap the global in a function returning a reference to a local static, so it only constructs the first time it’s actually needed — never before something it depends on.

Destruction order — reverse of construction, at program exit

static Logger logger;         // constructed 1st
static Database db(logger);   // constructed 2nd, depends on logger

// at exit: db destroyed first (logger still alive, safe to use)
//          logger destroyed second

Statics are torn down in the reverse of their construction order when the program exits. Get the construction order wrong and there’s a mirror-image teardown bug waiting too: using an already-destroyed static during shutdown.

Static at file scope — invisible outside this file

static void helperOnlyUsedHere() { ... }

Gives internal linkage — this symbol can’t be seen or linked against from any other .cpp file, even if it was accidentally declared in a shared header.

Design patterns