Static Members in C++

Rishabh Agarwal
4 min readMay 27, 2023

--

Photo by Zach Vessels on Unsplash

Static variables are commonly used in C. Since C++ is a better C, static variables are also present there. But can we mark members of a class, static? The answer is Yes.

In this article, we will be discussing static data members and static member functions in C++. We will understand what makes them different from normal data members and how to use them properly. Additionally, we will understand the static initialisation order of data members. At last, we will look at an example of using the knowledge we gained to implement Singleton classes.

So let us begin…!

Static Data Members

A data member, marked static, is not associated with any object of that class. Instead, that member is associated with the class itself.

All of the static data members are constructed before the main() starts and are destructed after the main() ends. And like normal data members, static data members can also have access specifiers.

Static data members can be accessed —

  • with the class-name followed by the scope resolution operator(::)
  • as a member of any object of that class

Use of static data members virtually remove the need of using global variables in C++ (or any OOPs environment).

Static data member should be defined outside the class scope, in addition to the declaration within the class scope, to avoid the linker error.

Following is an example on how to use static data members —

class MyClass {
static int x; // Static data member, Shared by all instances
public:
void get() { return x; }
void print() {
x = x + 10;
cout << "x = " << x << endl;
}
};

int MyClass::x = 0; // Define static data member

int main() {
MyClass obj1, obj2;
obj1.get(); obj2.get();
obj1.print(); obj2.print();
}

Following is the output of this program.

x = 25
x = 35

Since the variable x is shared among various instances, calling print method twice increases the value of the shared variable twice.

Note that all the static variables must be initialised in the global scope.

Order of Initialisation of Static Data Members

For normal data member, the order of initialisation is decided by their order in class definition. Here is an example to verify this claim.

#include <bits/stdc++.h>

using namespace std;

int init_a() {
cout << "a init" << endl;
return 0;
}

int init_b() {
cout << "b init" << endl;
return 0;
}

int init_c() {
cout << "c init" << endl;
return 0;
}

class MyClass {
int a, b, c;
public:
MyClass() : b(init_b()), c(init_c()), a(init_a()) {}
};

int main() {
MyClass myClass;
}

This code produces the following output.

a init
b init
c init

As evident from the output, no matter what we put in the initialisation list, the initialisation always happen in order of data member definition inside the class.

But does the static data members also follows the same rule? No!

The order of initialisation of static data members depends on the order of their definition and initialisation in the source.

Static Member Functions

A member function can also be marked as static. Such methods are not associated with any object. This means that they do not have access to the this pointer which is implicitly available to all non-static member functions. (Never heard of this pointer before? Check out this blog to understand more.)

A static member function can not invoke other non-static member functions and it can not access non-static data members.

Static member functions can be accessed —

  • with the class-name followed by the scope resolution operator(::)
  • as a member of any object of that class

Member functions can not be overloaded with static and non-static versions.

Member functions can not be declared with keyword const.

Tutorial: Singleton Class!

Singleton is a creational design pattern in which we ensure that there is at most one instance of a class. The concepts that we learnt in this tutorial helps us create a Singleton class in C++.

Here is how we can create a singleton Printer class.

class Printer {
private:
static Printer* myPrinter;
Printer() {
cout << "Printer Constructed" << endl;
}
public:
~Printer() {
cout << "Printer Destructed" << endl;
}
static const Printer& printer() {
if(!myPrinter) myPrinter = new Printer();
return *myPrinter;
}
}

We make the default construct private and thus no new instance of this class can be created. Then we expose a public static method called printer that can be used to get an instance of the Printer class. We only ever instantiate a single object of the Printer class that is kept in the private static data member called myPrinter.

This brings us to the end of this article. I hope you learned something new today.

If you enjoyed this article, please leave some appreciation by hitting the like button.

--

--

Rishabh Agarwal

Software Engineer | Loves to write about Programming, Technology, and Mathematics!