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:
#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"; }
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
#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'; }
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:
#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'; }
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):
#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'; }
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.
order 2 for Ben 3 orders created