Static Class Variables and Functions

A guide to sharing values between objects using static class variables and functions

Ryan McCombe
Updated

Previously, we've talked about how classes are blueprints for creating objects. These objects, or instances, will all have the same structure. They have the same variables and the same functions that can access and update those variables.

Each object gets a copy of the class variables. As such, different objects can have different values for each variable:

#include <string>

class Vampire {
public:
  int Health{100};
};

int main(){
  Vampire A;
  A.Health = 50;

  Vampire B;
  B.Health = 200;
}

In the above example, all instances of Vampire will have a Health value, and those values can be different for each object.

However, what if we want all objects in our class to share a variable? For example, all Vampires will have the same Faction - we don't need to have every object getting a separate copy of this std::string:

#include <string>

class Vampire {
public:
  std::string Faction{"Undead"};
};

In this lesson, we'll introduce the concept of static members, which achieves exactly this.

Static Class Variables

Whilst a variable like Health can vary from object to object, all of the objects of this class should share the same Faction

We can do this by marking the Faction variable as static and inline:

#include <string>

class Vampire {
public:
  int Health{100};
  static inline std::string Faction{"Undead"};
};

Static variables are shared across all instances of a class. In other words, any time we access the Faction property of one of our Vampire objects, we are accessing the same location in memory.

We can verify this by checking what is returned by &, the address of operator:

#include <iostream>
#include <string>

class Vampire {
public:
  static inline std::string Faction{"Undead"};
};

int main(){
  Vampire A;
  Vampire B;
  if (&A.Faction == &B.Faction) {
    std::cout << "They're the same";
  }
}
They're the same

Updating Static Variables

Given that all instances of a class share the same static variable, updating it on one object will have the effect of updating it on all objects of that class:

#include <iostream>
#include <string>

class Vampire {
public:
  static inline std::string Faction{"Undead"};
};

int main(){
  Vampire A;
  Vampire B;

  // Setting A.Faction
  A.Faction = "Demonic";

  // Getting B.Faction
  std::cout << B.Faction;
}
Demonic

We can prevent a static variable from being updated in the usual ways, by marking it as a constant:

#include <string>

class Vampire {
public:
  const static inline std::string Faction{
    "Undead"};
};

int main(){
  Vampire A;

  // Cannot update a const
  A.Faction = "Demonic";
}
error C2678: binary '=': no operator found which takes a left-hand operand of type 'const std::string'

Using constexpr Static Variables

Another implication of a class variable being static is that it can also be marked as a compile-time constant, using the constexpr keyword.

A constexpr variable is implicitly inline, so we can simultaneously remove the inline specifier:

class Vampire {
 public:
  static constexpr char Faction[] {"Undead"}; 
};

Note that as of C++23, a std::string cannot be used as a constexpr in this way. Therefore, we've also changed the type to a C-style string. We cover strings in more detail later in the course.

Accessing Static Variables from the Class

Aside from these memory savings and the fact that our code now makes our intentions explicit, there is a further benefit to using static when appropriate.

Due to the fact every static class member is shared across all instances of a class, we can find out what that value is without needing to create an object.

We can access a static class member directly from the class, using the scope resolution operator ::

#include <iostream>
#include <string>

class Vampire {
public:
  static inline std::string Faction{"Undead"};
};

int main(){
  std::cout << "All Vampires are " <<
    Vampire::Faction;

  Vampire::Faction = "Demonic";
  std::cout << "\nThey're now " <<
    Vampire::Faction;
}
All Vampires are Undead
They're now Demonic

static Functions

We've seen how classes can have const functions, which do not write to the properties of the object on which those functions are called.

We can also have static functions. Static functions do not read nor write to the properties of the object, unless those properties are also static.

#include <string>

class Vampire {
public:
  static std::string GetFaction(){
    return Faction;
  }

private:
  static inline std::string Faction{"Undead"};
};

Static class functions can call other static functions. Attempting to call a non-static class function from a static class function will result in a compilation error.

Similar to static variables, static functions can be called from an object of the class using the . operator.

But they can also be called from the class itself, using the :: operator:

#include <iostream>
#include <string>

class Vampire {
public:
  static std::string GetFaction(){
    return Faction;
  }

private:
  static inline std::string Faction{"Undead"};
};

int main(){
  std::cout << "All Vampires are "
    << Vampire::GetFaction();
}
All Vampires are Undead

Static Classes vs Namespaces

Accessing static class members through the :: operator bears some similarities to how we access members of a namespace.

To an extent, static members of a class can accomplish similar goals as namespaces, and the concept of a "static class" (a class that contains only static members) is a common pattern in other programming languages.

However, it tends to be less useful in C++. Namespaces achieve the goal in a simpler, more direct way. For the most part, we should only create classes (or structs) if we plan to instantiate objects from them.

But, sometimes a class is the most intuitive place to put a variable or a function, even if won't change from object to object. So, for those scenarios, we can just add the code to our class, and mark it as static.

Summary

In this lesson, we explored static class members, demonstrating how they allow for shared values and behaviors across instances

Main Points Learned

  • Static class variables and functions are shared across all instances of a class
  • The static keyword is used to declare static class members
  • Using inline with static variables in header files helps avoid multiple definitions across different translation units.
  • Static variables can be accessed using the scope resolution operator :: without needing to instantiate an object.
  • Static member functions can access static variables, but cannot access non-static members.
  • The concept of a "static class" in C++ is effectively a class with only static members, serving different purposes than namespaces but sometimes used to group related static members.
  • constexpr with static members enables the definition of compile-time constants
Next Lesson
Lesson 101 of 128

Friend Classes and Functions

An introduction to the friend keyword, which allows classes to give other objects and functions enhanced access to its members

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

Static vs Non-Static Variables
What are the primary differences between static and non-static variables?
Combining Static and Non-Static Variables
Can we have both static and non-static variables in the same class? How do they interact?
Memory Allocation for Static Variables
How does marking a variable as static impact memory allocation?
The Inline Keyword for Static Variables
Why is the inline keyword necessary for static variables in header files?
Initializing Static Variables in the Constructor
Can static class variables be initialized in the constructor of a class? Why or why not?
Static vs Global Variables
How do static variables differ from global variables in C++?
Static Functions Use Cases
What are some common use cases for static functions?
Static Member Functions
Can static member functions access non-static members? Why or why not?
Advantages of Static Members
What are the advantages and disadvantages of using static members?
Initialization Order of Static Variables
How does C++ handle the initialization order of static variables in different translation units?
Static Members in C++ vs Other Languages
How does the concept of static members in C++ compare to similar concepts in other programming languages like Java or C#?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant