- Table of Contents
- Previous Chapter
- Next Chapter

# Chapter 8: Static Data And Functions

In the previous chapters we provided examples of classes where each object had its own set of data members data. Each of the class's member functions could access any member of any object of its class.

In some situations it may be desirable to define common data fields, that may be accessed by all objects of the class. For example, the name of the startup directory, used by a program that recursively scans the directory tree of a disk. A second example is a variable that indicates whether some specific initialization has occurred. In that case the object that was constructed first would perform the initialization and would set the flag to `done'.

Such situations are also encountered in C, where several functions need to access the same variable. A common solution in C is to define all these functions in one source file and to define the variable `static`: the variable name is invisible outside the scope of the source file. This approach is quite valid, but violates our philosophy of using only one function per source file. Another C-solution is to give the variable in question an unusual name, e.g., `_6uldv8`, hoping that other program parts won't use this name by accident. Neither the first, nor the second legacy C solution is elegant.

C++ solves the problem by defining `static members`: data and functions, common to all objects of a class and (when defined in the private section) inaccessible outside of the class. These static members are this chapter's topic.

Static members cannot be defined as virtual functions. A virtual member function is an ordinary member in that it has a `this` pointer. As static member functions have no `this` pointer, they cannot be declared virtual.

## 8.1: Static data

Any data member of a class can be declared `static`; be it in the `public` or `private` section of the class interface. Such a data member is created and initialized only once, in contrast to non-static data members which are created again and again for each object of the class.

Static data members are created as soon as the program starts. Even though they're created at the very beginning of a program's execution cycle they are nevertheless true members of their classes.

It is suggested to prefix the names of static member with `s_` so they may easily be distinguished (in class member functions) from the class's data members (which should preferably start with `d_`).

Public static data members are global variables. They may be accessed by all of the program's code, simply by using their class names, the scope resolution operator and their member names. Example:

        class Test
        {
            static int s_private_int;

            public:
                static int s_public_int;
        };

        int main()
        {
            Test::s_public_int = 145;   // OK
            Test::s_private_int = 12;   // wrong, don't touch
                                        // the private parts
        }

The example does not present an executable program. It merely illustrates the interface, and not the implementation of `static` data members, which is discussed next.

### 8.1.1: Private static data

To illustrate the use of a static data member which is a private variable in a class, consider the following:

        class Directory
        {
            static char s_path[];

            public:
                // constructors, destructors, etc.
        };

The data member `s_path[]` is a private static data member. During the program's execution only one `Directory::s_path[]` exists, even though multiple objects of the class `Directory` may exist. This data member could be inspected or altered by the constructor, destructor or by any other member function of the class `Directory`.

Since constructors are called for each new object of a class, static data members are not initialized by constructors. At most they are modified. The reason for this is that static data members exist before any constructor of the class has been called. Static data members are initialized when they are defined, outside of any member function, exactly like the initialization of ordinary (non-class) global variables.

The definition and initialization of a static data member usually occurs in one of the source files of the class functions, preferably in a source file dedicated to the definition of static data members, called `data.cc`.

The data member `s_path[]`, used above, could thus be defined and initialized as follows in a file `data.cc`:

        #include "directory.ih"

        char Directory::s_path[200] = "/usr/local";

In the class interface the static member is actually only declared. In its implementation (definition) its type and class name are explicitly mentioned. Note also that the size specification can be left out of the interface, as shown above. However, its size is (either explicitly or implicitly) required when it is defined.

Note that any source file could contain the definition of the static data members of a class. A separate `data.cc` source file is advised, but the source file containing, e.g., `main()` could be used as well. Of course, any source file defining static data of a class must also include the header file of that class, in order for the static data member to be known to the compiler.

A second example of a useful private static data member is given below. Assume that a class `Graphics` defines the communication of a program with a graphics-capable device (e.g., a VGA screen). The initialization of the device, which in this case would be to switch from text mode to graphics mode, is an action of the constructor and depends on a `static` flag variable `s_nobjects`. The variable `s_nobjects` simply counts the number of `Graphics` objects which are present at one time. Similarly, the destructor of the class may switch back from graphics mode to text mode when the last `Graphics` object ceases to exist. The class interface for this `Graphics` class might be:

        class Graphics
        {
            static int s_nobjects;              // counts # of objects

            public:
                Graphics();
                ~Graphics();                    // other members not shown.
            private:
                void setgraphicsmode();         // switch to graphics mode
                void settextmode();             // switch to text-mode
        };

The purpose of the variable `s_nobjects` is to count the number of objects existing at a particular moment in time. When the first object is created, the graphics device is initialized. At the destruction of the last `Graphics` object, the switch from graphics mode to text mode is made:

        int Graphics::s_nobjects = 0;           // the static data member

        Graphics::Graphics()
        {
            if (!s_nobjects++)
                setgraphicsmode();
        }

        Graphics::~Graphics()
        {
            if (!--s_nobjects)
                settextmode();
        }

Obviously, when the class `Graphics` would define more than one constructor, each constructor would need to increase the variable `s_nobjects` and would possibly have to initialize the graphics mode.

### 8.1.2: Public static data

Data members could also be declared in the public section of a class. This, however, is deprecated (as it violates the principle of data hiding). The static data member `s_path[]` (cf. section 8.1) could be declared in the public section of the class definition. This would allow all the program's code to access this variable directly:

        int main()
        {
            getcwd(Directory::s_path, 199);
        }

A declaration is not a definition. Consequently the variable `s_path` still has to be defined. This implies that some source file still needs to contain `s_path[]` array's definition.

### 8.1.3: Initializing static const data

Static `const` data members should be initialized like other static data members: in source files defining these data members.

Usually, if these data members are of integral or built-in primitive data types the compiler accepts in-class initializations of such data members. However, there is no formal rule requiring the compiler to do so. Compilations may or may not succeed depending on the optimizations used by the compiler (e.g., using `-O2` may result in a successful compilation, but `-O0` (no-optimizations) may fail to compile, but then maybe only when shared libraries are used...).

In-class initializations of integer constant values (e.g., of types `char, int, long`, etc, maybe `unsigned`) is nevertheless possible using (e.g., anonymous) enums. The following example illustrates how this can be done:

        class X
        {
            public:
                enum         { s_x = 34 };
                enum: size_t { s_maxWidth = 100 };
        };

To avoid confusion caused by different compiler options static data members should always explicitly be defined and initialized in a (single) source file, whether or not they are `const` data. Additionally, by defining them in a source file you avoid the inline-inconsistency.

### 8.1.4: Generalized constant expressions (constexpr)

In C macros are often used to let the preprocessor perform simple calculations. These macro functions may have arguments, as illustrated in the next example:

        #define xabs(x) ((x) < 0 ? -(x) : (x))

The disadvantages of macros are well known. The main reason for avoiding macros is that they are not parsed by the compiler, but are processed by the preprocessor resulting in mere text replacements and thus avoid type-safety or syntactic checks of the macro definition by itself. Furthermore, since macros are processed by the preprocessor their use is unconditional, without acknowledging the context in which they are applied. `NULL` is an infamous example. Ever tried to define an `enum` symbol `NULL`? or `EOF`? Chances are that, if you did, the compiler threw strange error messages at you.

Generalized const expressions can be used as an alternative.

Generalized const expressions are recognized by the modifier `constexpr` (a keyword), that is applied to the expression's type.

There is a small syntactic difference between the use of the `const` modifier and the use of the `constexpr` modifier. While the `const` modifier can be applied to definitions and declarations alike, the `constexpr` modifier can only be applied to definitions:

        extern int const externInt;     // OK: declaration of const int
        extern int constexpr error;     // ERROR: not a definition

Variables defined with the `constexpr` modifier have constant (immutable) values. But generalized const expressions are not just used to define constant variables; they have other applications as well. The `constexpr` keyword is usually applied to functions, turning the function into a constant-expression function.

A constant-expression function should not be confused with a function returning a `const` value (although a constant-expression function does return a (const) value). A constant expression function has the following characteristics:

- it returns a value;
- its return type is given the `constexpr` modifier;

Such functions are also called named constant expressions with parameters.

These constant expression functions may or may not be called with arguments that have been evaluated at compile-time (not just `const arguments', as a `const` parameter value is not evaluated at compile-time). If they are called with compile-time evaluated arguments then the returned value is considered a `const` value as well.

This allows us to encapsulate expressions that can be evaluated at compile-time in functions, and it allows us to use these functions in situations where previously the expressions themselves had to be used. The encapsulation reduces the number of occurrences of the expressions to one, simplifying maintenance and reduces the probability of errors.

If arguments that could not be compile-time evaluated are passed to constant-expression functions, then these functions act like any other function, in that their return values are no longer considered constant expressions.

Assume some two-dimensional arrays must be converted to one-dimensional arrays. The one-dimensional array must have `nrows * ncols + nrows + ncols + 1` elements, to store row, column, and total marginals, as well as the elements of the source array itself. Furthermore assume that `nrows` and `ncols` have been defined as globally available `size_t const` values (they could be a class's static data). The one-dimensional arrays are data members of a class or struct, or they are also defined as global arrays.

Now that constant-expression functions are available the expression returning the number of the required elements can be encapsulated in such a function:

        size_t const nRows = 45;
        size_t const nCols = 10;

        size_t constexpr nElements(size_t rows, size_t cols)
        {
            return rows * cols + rows + cols + 1;
        }

            ....

        int intLinear[ nElements(nRows, nCols) ];

        struct Linear
        {
            double d_linear[ nElements(nRows, nCols) ];
        };

If another part of the program needs to use a linear array for an array of different sizes then the constant-expression function can also be used. E.g.,

        string stringLinear[ nElements(10, 4) ];

Constant-expression functions can be used in other constant expression functions as well. The following constant-expression function returns half the value, rounded upwards, that is returned by `nElements`:

        size_t constexpr halfNElements(size_t rows, size_t cols)
        {
            return (nElements(rows, cols) + 1) >> 1;
        }

Classes should not expose their data members to external software, so as to reduce coupling between classes and external software. But if a class defines a `static const size_t` data member then that member's value could very well be used to define entities living outside of the class's scope, like the number of elements of an array or to define the value of some enum. In situations like these constant-expression functions are the perfect tool to maintain proper data hiding:

        class Data
        {
            static size_t const s_size = 7;

            public:
                static size_t constexpr size();
                size_t constexpr mSize();
        };

        size_t constexpr Data::size()
        {
            return s_size;
        }

        size_t constexpr Data::mSize()
        {
            return size();
        }

        double data[ Data::size() ];        // OK: 7 elements
        short data2[ Data().mSize() ];      // also OK: see below

Please note the following:

- Constant-expression functions are implicitly declared inline. As an illustration: the file `sum.h` declares a `constexpr` function: `int constexpr sum(int x, int y);` It's used by `main`:

          #include <iostream>
          #include "sum.h"

          int main()
          {
              std::cout << "sum: " << sum(21, 21) << '\n';
          }

  When this file (`main.cc`) is compiled the compiler reports:

  > sum.h:1:15: warning: inline function ``constexpr int sum(int, int)`' used but never defined.

  Here the compiler explicitly states that `sum` is an inline function. Moreover, even if `sum.cc` defines `sum` and `main.o` and `sum.o` are linked the linker reports:

  > undefined reference to ``sum(int, int)`'.

  Consequently, the definitions of `constexpr` functions must compile-time be available which in practice means that they're defined in (class) header files;
- Non-static constant-expression member functions are implicitly `const`, and a `const` member modifier for them is optional;
- Constant values (e.g., static constant data members) used by constant-expression functions must be known by the time the compiler encounters the functions' definitions. That's why `s_size` was initialized in `Data`'s class interface.

Some final notes: `constexpr` functions may

- define any kind of variable except for `static` or `thread_local` variables;
- define variables without initializers;
- use conditional statements (`if` and `switch`);
- use repetition statements, including the range-based `for` statement;
- use expressions changing the values of objects that are local to the `constexpr` function;
- `constexpr` member functions can be non-const. But non-const `constexpr` member functions can only modify data members of objects that were defined local to the `constexpr` function calling the non-const `constexpr` member function.

#### 8.1.4.1: Constant expression data

As we've seen, (member) functions and variables of primitive data types can be defined using the `constexpr` modifier. What about class-type objects?

Objects of classes are values of class type, and like values of primitive types they can be defined with the `constexpr` specifier. Constant expression class-type objects must be initialized with constant expression arguments; the constructor that is actually used must itself have been declared with the `constexpr` modifier. Note again that the `constexpr` constructor's definition must have been seen by the compiler before the `constexpr` object can be constructed:

        class ConstExpr
        {
            public:
                constexpr ConstExpr(int x);
        };

        ConstExpr ok{ 7 };              // OK: not declared as constexpr

        constexpr ConstExpr err{ 7 };   // ERROR: constructor's definition
                                        //        not yet seen

        constexpr ConstExpr::ConstExpr(int x)
        {}

        constexpr ConstExpr ok{ 7 };                // OK: definition seen
        constexpr ConstExpr okToo = ConstExpr{ 7 }; // also OK

A constant-expression constructor has the following characteristics:

- it is declared with the `constexpr` modifier;
- its member initializers only use constant expressions;
- its body is empty.

An object constructed with a constant-expression constructor is called a user-defined literal. Destructors and copy constructors of user-defined literals must be trivial.

The `constexpr` characteristic of user-defined literals may or may not be maintained by its class's members. If a member is not declared with a `constexpr` return value, then using that member does not result in a constant-expression. If a member does declare a `constexpr` return value then that member's return value is considered a `constexpr` if it is by itself a constant expression function. To maintain its `constexpr` characteristics it can refer to its classes' data members only if its object has been defined with the `constexpr` modifier, as illustrated by the example:

        class Data
        {
            int d_x;

            public:
                constexpr Data(int x)
                :
                    d_x(x)
                {}

                int constexpr cMember()
                {
                    return d_x;
                }

                int member() const
                {
                    return d_x;
                }
        };

        Data d1{ 0 };           // OK, but not a constant expression

        enum e1 {
            ERR = d1.cMember()  // ERROR: cMember(): no constant
        };                      //        expression anymore

        constexpr Data d2{ 0 }; // OK, constant expression

        enum e2 {
            OK = d2.cMember(),  // OK: cMember(): now a constant
                                //                expression
            ERR = d2.member(),  // ERR: member(): not a constant
        };                      //                expression

## 8.2: Static member functions

In addition to static data members, C++ allows us to define static member functions. Similar to static data that are shared by all objects of the class, static member functions also exist without any associated object of their class.

Static member functions can access all static members of their class, but also the members (`private` or `public`) of objects of their class if they are informed about the existence of these objects (as in the upcoming example). As static member functions are not associated with any object of their class they do not have a `this` pointer. In fact, a static member function is completely comparable to a global function, not associated with any class (i.e., in practice they are. See the next section (8.2.1) for a subtle note). Since static member functions do not require an associated object, static member functions declared in the public section of a class interface may be called without specifying an object of its class. The following example illustrates this characteristic of static member functions:

        class Directory
        {
            string d_currentPath;
            static char s_path[];

            public:
                static void setpath(char const *newpath);
                static void preset(Directory &dir, char const *newpath);
        };
        inline void Directory::preset(Directory &dir, char const *newpath)
        {
                                                        // see the text below
            dir.d_currentPath = newpath;                // 1
        }

        char Directory::s_path[200] = "/usr/local";     // 2

        void Directory::setpath(char const *newpath)
        {
            if (strlen(newpath) >= 200)
                throw "newpath too long";

            strcpy(s_path, newpath);                    // 3
        }

        int main()
        {
            Directory dir;

            Directory::setpath("/etc");                 // 4
            dir.setpath("/etc");                        // 5

            Directory::preset(dir, "/usr/local/bin");   // 6
            dir.preset(dir, "/usr/local/bin");          // 7
        }

- at 1 a static member function modifies a private data member of an object. However, the object whose member must be modified is given to the member function as a reference parameter.

  Note that static member functions can be defined as inline functions.

- at 2 a relatively long array is defined to be able to accommodate long paths. Alternatively, a `string` or a pointer to dynamic memory could be used.
- at 3 a (possibly longer, but not too long) new pathname is stored in the static data member `s_path[]`. Note that only static members are used.
- at 4, `setpath()` is called. It is a static member, so no object is required. But the compiler must know to which class the function belongs, so the class is mentioned using the scope resolution operator.
- at 5, the same is implemented as in 4. Here `dir` is used to tell the compiler that we're talking about a function in the `Directory` class. Static member functions can be called as normal member functions, but this does not imply that the static member function receives the object's address as a `this` pointer. Here the member-call syntax is used as an alternative for the classname plus scope resolution operator syntax.
- at 6, `currentPath` is altered. As in 4, the class and the scope resolution operator are used.
- at 7, the same is implemented as in 6. But here `dir` is used to tell the compiler that we're talking about a function in the `Directory` class. Here in particular note that this is not using `preset()` as an ordinary member function of `dir`: the function still has no `this`-pointer, so `dir` must be passed as argument to inform the static member function `preset` about the object whose `currentPath` member it should modify.

In the example only public static member functions were used. C++ also allows the definition of private static member functions. Such functions can only be called by member functions of their class.

### 8.2.1: Calling conventions

As noted in the previous section, static (public) member functions are comparable to classless functions. However, formally this statement is not true, as the C++ standard does not prescribe the same calling conventions for static member functions as for classless global functions.

In practice the calling conventions are identical, implying that the address of a static member function could be used as an argument of functions having parameters that are pointers to (global) functions.

If unpleasant surprises must be avoided at all cost, it is suggested to create global classless wrapper functions around static member functions that must be used as call back functions for other functions.

Recognizing that the traditional situations in which call back functions are used in C are tackled in C++ using template algorithms (cf. chapter 19), let's assume that we have a class `Person` having data members representing the person's name, address, phone and mass. Furthermore, assume we want to sort an array of pointers to `Person` objects, by comparing the `Person` objects these pointers point to. Keeping things simple, we assume that the following public static member exists:

        int Person::compare(Person const *const *p1, Person const *const *p2);

A useful characteristic of this member is that it may directly inspect the required data members of the two `Person` objects passed to the member function using pointers to pointers (double pointers).

Most compilers allow us to pass this function's address as the address of the comparison function for the standard C `qsort()` function. E.g.,

        qsort
        (
            personArray, nPersons, sizeof(Person *),
            reinterpret_cast<int(*)(void const *, void const *)>(Person::compare)
        );

However, if the compiler uses different calling conventions for static members and for classless functions, this might not work. In such a case, a classless wrapper function like the following may be used profitably:

        int compareWrapper(void const *p1, void const *p2)
        {
            return
                Person::compare
                (
                    static_cast<Person const *const *>(p1),
                    static_cast<Person const *const *>(p2)
                );
        }

resulting in the following call of the `qsort()` function:

        qsort(personArray, nPersons, sizeof(Person *), compareWrapper);

Note:

- The wrapper function takes care of any mismatch in the calling conventions of static member functions and classless functions;
- The wrapper function handles the required type casts;
- The wrapper function might perform small additional services (like dereferencing pointers if the static member function expects references to `Person` objects rather than double pointers);
- As an aside: in C++ programs functions like `qsort()`, requiring the specification of call back functions are seldom used. Instead using existing generic template algorithms is preferred (cf. chapter 19).

- Table of Contents
- Previous Chapter
- Next Chapter