What Are Static Member Functions?

A static member function belongs to the class rather than to any object of it. You call it through the class name, no instance required, and it is the natural companion to static member variables.

The previous lesson introduced static member variables. When one is public, reaching it is easy:

Turnstile::s_ticketsSold; // fine if public

The interesting case is when it is private, which it usually should be.

The Problem With a Private Static

Make the variable private and outside code cannot touch it at all:

#include <iostream>

class Turnstile
{
private:
    static inline int s_ticketsSold{ 0 };
};

int main()
{
    std::cout << Turnstile::s_ticketsSold << '\n';

    return 0;
}
s.cpp: In function 'int main()':
s.cpp:11:29: error: 'int Turnstile::s_ticketsSold' is private within this context
   11 |     std::cout << Turnstile::s_ticketsSold << '\n';
      |                             ^~~~~~~~~~~~~
s.cpp:6:23: note: declared private here
    6 |     static inline int s_ticketsSold{ 0 };

The usual answer to a private member is a public member function. But an ordinary member function is called on an object, so you would have to create a Turnstile just to read a counter that does not belong to any particular turnstile. That is backwards.

Making the Function Static Too

Mark the accessor static and the object disappears from the picture:

#include <iostream>

class Turnstile
{
private:
    static inline int s_ticketsSold{ 0 };

public:
    static void admit() { ++s_ticketsSold; }
    static int ticketsSold() { return s_ticketsSold; }
};

int main()
{
    Turnstile::admit();
    Turnstile::admit();
    Turnstile::admit();

    std::cout << Turnstile::ticketsSold() << " admitted\n";

    return 0;
}

Output:

3 admitted

Because a static member function is not tied to an instance, you call it through the class name and the scope resolution operator. It can also be called through an object, which compiles but misleads the reader into thinking the object matters, so avoid it.

Two Consequences of Having No Object

There is no this pointer. this names the object a member function is operating on, and a static member function has no such object. Nothing to point at, so no pointer.

Only static members are reachable. A static member function can use other static member variables and static member functions directly, and cannot touch non-static ones. Non-static members exist per object, and there is no object here to pick them from.

Defining One Outside the Class

Like ordinary member functions, static member functions can be declared in the class and defined outside it. The static keyword appears on the declaration only:

#include <iostream>

class SerialIssuer
{
private:
    static inline int s_nextSerial{ 5000 };

public:
    static int nextSerial();
};

int SerialIssuer::nextSerial()
{
    return s_nextSerial++;
}

int main()
{
    for (int issued{ 0 }; issued < 4; ++issued)
        std::cout << "Issued serial " << SerialIssuer::nextSerial() << '\n';

    return 0;
}

Output:

Issued serial 5000
Issued serial 5001
Issued serial 5002
Issued serial 5003

Every piece of this class is static, so no instance is ever created. A static variable remembers the next number and a static function hands it out and advances it.

Reminder
A member function defined inside the class definition is implicitly inline; one defined outside it is not. A static member function defined outside the class in a header therefore needs an explicit inline, or including that header in two translation units violates the one-definition rule.

Be Careful With All-Static Classes

Classes where everything is static, sometimes called pure static classes or monostates, are occasionally the right tool and frequently a trap.

You get exactly one, forever. Static members exist once per program, so there is no way to have two independent SerialIssuer instances handing out separate number ranges. Getting a second one means copying the class and renaming it.

They are global variables wearing a class. The lesson on non-const globals covered why shared mutable state that anything can reach is dangerous. A pure static class has precisely that shape: the members belong to the class, the class name is visible everywhere, and any code can modify the state and break something unrelated.

A normal class with one global instance is usually the better shape. You still get the single shared thing where that is what you want, and you can still create local instances when that turns out to be useful.

Against Namespaces

Pure static classes and namespaces overlap heavily. Both group functions and variables with static duration under a name.

The difference that decides it is access control: a class has public and private, a namespace has neither. Prefer a static class when you have static data to protect or genuinely need access control, and a namespace in every other case.

C++ Has No Static Constructors

Since a constructor initializes normal member variables, you might expect a static constructor for static ones. Some languages have them. C++ does not.

When the value can simply be written down, none is needed: initialize at the point of definition, even for a private member. That is what the examples above do.

When building the value takes actual code, the usual technique is a function that constructs the object, fills it in, and returns it, with the returned value copied into the static member:

#include <iostream>

struct LaneCodes
{
    char north{};
    char south{};
    char east{};
    char west{};
};

class Concourse
{
private:
    static LaneCodes buildLanes()
    {
        LaneCodes lanes{};
        lanes.north = 'N';
        lanes.south = 'S';
        lanes.east = 'E';
        lanes.west = 'W';

        return lanes;
    }

public:
    static inline LaneCodes s_direct{ 'N', 'S', 'E', 'W' };
    static inline LaneCodes s_built{ buildLanes() };
};

int main()
{
    std::cout << Concourse::s_direct.east << Concourse::s_built.west << '\n';

    return 0;
}

Output:

EW

s_direct takes the straightforward route because its values are literals. s_built calls a private static function, which can run loops or any other logic a constructor would have. The technique is not specific to static members, incidentally: it initializes any variable whose value needs computing.

Summary

Static member functions belong to the class, not to an object, and are called as ClassName::function(). Calling one through an object is legal but misleading.

Why they exist: they give outside code access to private static members without forcing you to instantiate an object that has nothing to do with the data.

No this pointer: there is no object being operated on, so the pointer has nothing to point at.

Access is limited to static members: non-static members belong to instances, and a static function has no instance to choose from.

Defined outside the class: write static on the declaration only, and add inline when the definition lives in a header, or you break the one-definition rule.

All-static classes: only ever one copy, and functionally equivalent to globals in a namespace with the same hazards. A normal class with a global instance is usually better.

Versus namespaces: choose a static class when you need access control over static data, and a namespace otherwise.

No static constructors: initialize at the point of definition, or call a function that builds the value and returns it.