Static Members and Friends

Some data and functions belong to a class as a whole rather than to any single object: a count of how many objects exist, a factory function, a shared configuration. That's what static members are for. A friend, by contrast, is outside code that a class explicitly allows to see its private parts.

Static data members

A static data member is shared by all objects of the class. Declare it inline static (C++17) to define it right in the class:

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

class Ticket {
public:
    explicit Ticket(std::string event) : event_(std::move(event)), number_(next_number_++) {
        ++live_count_;
    }
    ~Ticket() { --live_count_; }
    Ticket(const Ticket&) = delete;              // tickets can't be copied
    Ticket& operator=(const Ticket&) = delete;

    void print() const { std::cout << "ticket #" << number_ << " for " << event_ << '\n'; }
    static int live_count() { return live_count_; }

private:
    std::string event_;
    int number_;
    inline static int next_number_ = 1000;
    inline static int live_count_ = 0;
};

int main() {
    Ticket a{"concert"};
    {
        Ticket b{"theatre"};
        Ticket c{"concert"};
        c.print();
        std::cout << Ticket::live_count() << " tickets exist\n";
    }
    a.print();
    std::cout << Ticket::live_count() << " ticket exists\n";
}
Output
ticket #1002 for concert
3 tickets exist
ticket #1000 for concert
1 ticket exists

A static member function has no this and can only use static members. Call it with the class name: Ticket::live_count(). = delete removes the copy operations (lesson 35).

Static constants

C++
#include <iostream>

class Password {
public:
    static constexpr int min_length = 12;
    static bool acceptable(const char* candidate) {
        int n = 0;
        while (candidate[n] != '\0') ++n;
        return n >= min_length;
    }
};

int main() {
    std::cout << "minimum: " << Password::min_length << '\n';
    std::cout << std::boolalpha << Password::acceptable("hunter2") << ' '
              << Password::acceptable("correct horse battery") << '\n';
}
Output
minimum: 12
false true

Factory functions

Static functions are a common way to offer named ways of creating objects — clearer than several constructors whose arguments look alike:

C++
#include <iostream>

class Duration {
public:
    static Duration from_seconds(long s) { return Duration{s}; }
    static Duration from_minutes(long m) { return Duration{m * 60}; }
    static Duration from_hours(long h) { return Duration{h * 3600}; }
    long seconds() const { return seconds_; }
private:
    explicit Duration(long s) : seconds_(s) {}   // private: use the factories
    long seconds_;
};

int main() {
    auto film = Duration::from_minutes(135);
    auto flight = Duration::from_hours(11);
    std::cout << film.seconds() << ' ' << flight.seconds() << '\n';
}
Output
8100 39600

(For real time code, use std::chrono — lesson 46.)

Friends

A class can declare a function or another class its friend, giving it access to private members. The most common use is the stream output operator, which must be a non-member function (lesson 34):

C++
#include <iostream>

class Point {
public:
    Point(double x, double y) : x_(x), y_(y) {}
    friend std::ostream& operator<<(std::ostream& out, const Point& p) {
        return out << '(' << p.x_ << ", " << p.y_ << ')';
    }
private:
    double x_, y_;
};

int main() {
    Point p{3, 4.5};
    std::cout << "point " << p << '\n';
}
Output
point (3, 4.5)

Friendship is granted by the class, not taken, and it isn't inherited or transitive. Use it sparingly: a friend is part of the class's implementation in all but name.

Try it yourself

Give a class an automatic ID: each new object gets the next number from a static counter.

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
order 2 for Ben
3 orders created