C++ Storage Classes
Storage classes define the scope (visibility) and lifetime of variables/functions in a C++ program. These specifiers are placed before the type they modify. The following are the storage classes available in a C++ program:
- auto
- register
- static
- extern
- mutable
- thread_local (C++11)
Starting from C++17, the auto
keyword is no longer a C++ storage class specifier, and the register
keyword is deprecated.
auto Storage Class
Since C++11, the auto keyword has been used in two situations: to automatically deduce the type of a variable based on its initializer expression, and as a placeholder for the return type of a function.
In the C++98 standard, the auto
keyword was used for declaring automatic variables, but this usage was rare and redundant, so it was removed in C++17.
Automatically deducing the type of the declared variable based on the initializer expression, for example:
auto f = 3.14; // double
auto s("hello"); // const char*
auto z = new auto(9); // int*
auto x1 = 5, x2 = 5.0, x3 = 'r'; // Error, must be initialized to the same type
register Storage Class
The register storage class is used to define local variables that should be stored in a register instead of RAM. This means the variable has a maximum size equal to the register size (usually one word) and can't have the unary '&' operator applied to it (as it has no memory location).
{
register int miles;
}
Registers are only used for variables that require quick access, such as counters. It's also important to note that defining a 'register' does not guarantee that the variable will be stored in a register; it means the variable might be stored in a register, depending on hardware and implementation constraints.
static Storage Class
The static storage class instructs the compiler to keep a local variable in existence during the life of the program, without needing to create and destroy it each time it comes into and goes out of scope. Thus, using static for local variables allows their values to be maintained between function calls.
The static modifier can also be applied to global variables. When a static global variable is used, it limits the variable's scope to the file in which it is declared.
In C++, when static
is used on class data members, it causes only one copy of that member to be shared by all objects of the class.
Example
#include <iostream>
// Function declaration
void func(void);
static int count = 10; /* Global variable */
int main()
{
while(count--)
{
func();
}
return 0;
}
// Function definition
void func( void )
{
static int i = 5; // Local static variable
i++;
std::cout << "Variable i is " << i ;
std::cout << " , Variable count is " << count << std::endl;
}
When the above code is compiled and executed, it produces the following result:
Variable i is 6 , Variable count is 9
Variable i is 7 , Variable count is 8
Variable i is 8 , Variable count is 7
Variable i is 9 , Variable count is 6
Variable i is 10 , Variable count is 5
Variable i is 11 , Variable count is 4
Variable i is 12 , Variable count is 3
Variable i is 13 , Variable count is 2
Variable i is 14 , Variable count is 1
Variable i is 15 , Variable count is 0
extern Storage Class
The extern storage class is used to give a reference of a global variable that is visible to all program files. When you use 'extern', for variables that cannot be initialized, the variable name points to a previously defined storage location.
When you have multiple files and you define a global variable or function that can be used in other files, you can use extern
in another file to reference the already defined variable or function. You can think of extern
as a way to declare a global variable or function in another file.
The extern modifier is typically used when there are two or more files sharing the same global variables or functions, as shown below:
First file: main.cpp
#include <iostream>
int count;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
Second file: support.cpp
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
std::cout << "Count is " << count << std::endl;
}
Here, the extern keyword in the second file is used to declare the count that has already been defined in the first file main.cpp. Now, compile these two files as follows:
$ g++ main.cpp support.cpp -o write
This will produce the write executable program. Try executing write, and it will produce the following result:
$ ./write
Count is 5
Mutable Storage Class
The mutable specifier applies only to class objects, which will be discussed at the end of this tutorial. It allows a member of an object to override constness. That is, a mutable member can be modified by a const member function.
Thread_local Storage Class
Variables declared with the thread_local specifier are accessible only in the thread they are created in. The variable is created when the thread is created and destroyed when the thread is destroyed. Each thread has its own copy of the variable.
The thread_local specifier can be combined with static or extern.
thread_local can only be applied to the declaration and definition of data; it cannot be used for function declarations or definitions.
The following demonstrates variables that can be declared as thread_local:
thread_local int x; // Global variable under namespace
class X
{
static thread_local std::string s; // Static member variable of a class
};
static thread_local std::string X::s; // X::s needs to be defined
void foo()
{
thread_local std::vector<int> v; // Local variable
}