Static and const members

Introduction

Most members belong to one object, but some facts belong to the whole class, such as how many objects exist. And many member functions only read an object, which C++ lets you promise with const, so that objects can be passed around safely without being copied.

How it works

Static members

#include <iostream>
using namespace std;

class Ticket {
    static int sold;   // one for the whole class
    int number;        // one in each object
public:
    Ticket() {
        sold++;
        number = sold;
    }
    int getNumber() const {
        return number;
    }
    static int howMany() {
        return sold;
    }
};

int Ticket::sold = 0;

int main() {
    Ticket a, b, c;
    cout << "b has number " << b.getNumber() << endl;
    cout << Ticket::howMany() << " tickets sold" << endl;
    return 0;
}
Open in compiler

An ordinary data member exists once per object: each Ticket has its own number. A static data member exists once for the whole class: there is one sold, shared by every ticket. In the visualizer it appears with the globals, as Ticket::sold, because it lives outside every object.

  • A static data member is declared in the class and defined once outside it: int Ticket::sold = 0;.
  • A static member function (howMany) has no this, so it can use only static members. Call it through the class: Ticket::howMany().

Static members suit facts about the whole class: how many objects exist, the next ID to give out, a shared setting.

Syntax

class Ticket {
    static int sold;                 // one for the whole class
public:
    static int howMany() { return sold; }
    int number() const;              // does not change the object
};
int Ticket::sold = 0;                // the definition

Example

#include <iostream>
using namespace std;

class Circle {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() const {
        return 3.14159 * r * r;
    }
    void grow(double by) {
        r += by;
    }
};

void show(const Circle& c) {
    cout << c.area() << endl;   // fine: area() is const
}

int main() {
    Circle c(1);
    c.grow(1);
    show(c);
    const Circle unit(1);
    cout << unit.area() << endl;
    return 0;
}
Open in compiler

double area() const promises that area does not change the object. That promise matters because of const objects and const references:

  • show(const Circle& c) may call only const member functions on c: c.area() is fine, c.grow(1) is a compile error.
  • const Circle unit(1); can never change, so only its const member functions can be called.

Mark every member function that does not change the object as const. Otherwise your class cannot be used through const &, which is the usual way to pass objects.

Line by line

  1. Circle keeps its radius r private, and its constructor sets it.
  2. area() const only reads r, so it is marked const. grow changes r, so it is not.
  3. show takes a const Circle&, so it can call c.area(), but c.grow(1) would not compile.
  4. In main, c grows from radius 1 to 2, so show(c) prints about 12.5664.
  5. unit is a const object, so only const member functions such as area can be called on it. It prints 3.14159.

Expected result

12.5664
3.14159

In the real world

Configuration objects, counters of live connections and shared caches are often static members. Const correctness is enforced throughout the standard library, so a class without const getters cannot be used with many library functions.

Common mistakes

  • Forgetting the definition int Ticket::sold = 0; outside the class: the program then fails to link.
  • Using a non-static member (or this) inside a static member function.
  • Leaving const off a getter, so it cannot be called on a const object.

Best practices

  • Mark a member function const whenever it does not change the object.
  • Pass objects that are only read as const&.
  • Use static members sparingly, since shared state is harder to test.

Summary

A static member is shared by the whole class, and a static member function has no this. A const member function promises not to change its object, which lets const objects and const references use it.

Quick check

1. Three Ticket objects exist. How many copies of the static member sold are there?
2. Can a static member function use this?
3. void show(const Circle& c) calls c.grow(1), and grow is not const. What happens?

Key takeaways

  • A static data member is defined once outside the class.
  • A static member function can use only static members.
  • Through a const reference, only const member functions can be called.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.