Storage Classes in C++
Storage class specifier generally tells the compiler how a subsequent variable can be stored. The order of their storage determines the
scope or visibility and the
lifetime of the variables. There are five types of storage classes available in C++ :
- Extern
- Static
- Register
- Auto
- Mutable
Syntax for declaring the storage class is as below:
< storage_class_specifier> < datatype> < variable_name>
Let's see in detail about each type of storage class.
Extern Storage class:
This will provide the external linkage to the variables. There are three types of linkage in c++: internal, external, and none. External linkage
means the variable is
defined somewhere else other than the current working file. "Extern " made the variable visible and accessible to the
other files. Internal linkage means the scope of
the variable is limited only to the file where it is defined and is not accessible outside the file.
All the global variables declared as static are examples of
internal linkage. No linkage means the scope of the variable is limited only to certain
blocks like function or conditional loop. Local variables are an example of
no linkage.
Now to understand the importance of extern we must know about the concept of "declaration " and "definition ". By "declaration" we basically
mean to declare the name and
type of the variable. And by "definition " we allocate the storage to the variable. In most cases declaration are
also the definitions. However by preceding the variable
name with "extern " we are declaring the variable without defining it. In this way when
we need to refer to this variable in some other file we can declare that variable
using "extern " in that file.
In short we can say that, "extern " storage class can be used in such case where our project contain multiple files and we need to share the
same
global variables or function within these file.
Let's see a simple code snippet for better understanding:
File 1:
int val;
void main(){
val = 110
}
File2:
extern int val;
void function2(){
cout<<"Value of val: "<< val
}
Here extern keyword is used to declare the value of val in other file.
Static Storage class:
We can say static variables as the permanent variables within the scope in which it is defined either in function scope or file scope. When we
declare a variable as
static then the compiler will maintain a single copy of it through the lifetime of the program and not create and destroy
it each time. It will be created only once
and its value will be updated whenever it comes into scope. This will be very helpful while writing the generalized functions.
We can declare both the local and global as static. Let's check into this.
-
Static Local Varaibles :- Whenever we declare a local variable as static then this variable will neither be created every time whenever
that block will come into execution nor will be destroyed whenever that block goes out of the scope. It will be created only once and
only its value will be updated whenever that block will come into execution. Also as it is a local variable so it is not visible or accessible
from outsidethat block.
Let's see the code snippet for a better explanation:#include < iostream> void numTrack(void) { static int num_val = 25; num_val = num_val + 10; std::cout<< num_val << std::endl; } int main() { std::cout<< "Printing outputs.."<< std::endl; for(int i = 0; i<=5; i++){ numTrack(); } return 0; }
In the above code, we are calling the function "numTrack() " six times, and in all these calls the value of the variable "num_val " is
properly maintained. This variable will be created only once and assigned the value 25 when the function is being called the first
time and from the next time onwards its value will be updated. Unlike the normal local variables, it won't be destroyed when the
scope comes out the function "numTrack() ".
So the output we get for this is as below:-Printing outputs.. 35 45 55 65 75 85
-
Static Global Varaibles :-
When we made the global variable static then the compiler will maintain the single copy of this variable throughout that file in which it is
declared. This variable is accessed and updated by the functions defined in that file.
Also, this variable has the file scope, which means the functions from the other files will not able to access this variable.
A small code snippet for this is as below:-#include < iostream> static int num_orig; void numUpdate(void) { num_orig = num_orig + 10; std::cout<< num_orig << std::endl; } void num_init(int val) { num_orig = val; } int main() { num_init(22); std::cout<< "Printing outputs.."<< std::endl; for(int i = 0; i<=5; i++){ numUpdate(); } return 0; }
Here "num_orig " is the static global variable that is accessed by the functions, "num_Init() " and "numUpdate() " for giving it an initial
value and to increase its value respectively.
Its output will be as below:-Printing outputs.. 32 42 52 62 72 82
Register Storage class:
Register specifier directs the compiler to store the value of the variables in a register of the CPU instead of storing it into memory or RAM
where normal variables
are stored. The advantage of doing this is that the operations on a register variable could occur much faster than the
normal variable. It is because if the value
of the variable is already stored in the CPU there is no extra time required to fetch the variable from
the memory for execution.
Below is the code snippet for using the register specifier:-
#include < iostream>
int calc_pwr(register int num, register int pwr)
{
register int local_val = 1;
for(int i=1;i<=pwr;i++){
local_val = local_val*num;
}
return local_val;
}
int main() {
std::cout << "5 raise to power of 3:- "<< calc_pwr(5,3);
return 0;
}
Here we are using the register variable inside the for loop because in a loop the same variable is being accessed repeatedly, so storing it in a
register variable
will save the access time.
The output of the code is as below:-
5 raise to power of 3:- 125
Auto Storage class:
Auto is the default storage class for all the variables. The scope of this is limited only to the block inside which the variable is declared. Outside
the block,
the "auto " storage class variables are not visible.
Below is the code snippet to show the syntax of the auto storage class:
void autoStorageDemo() {
....
auto var1 = 10;
auto var2 = 5;
auto str = "Test";
...
}
Mutable:
Mutable is mainly used in the context of the members of a class. When the object of our class is of constant type and if there is a requirement
to
change their values during runtime then the "mutable " keyword will solve our purpose here.
Consider a scenario where we are having a class that contains some member variables. And it requires that we need to maintain some of the
members as constant and
the values of some members should be updated during runtime. So, in this case, we declare those member variables
whose values need to be updated in the future as
"mutable ", and then while creating the instance of the class we will create the const instance.
Below is a code snippet for explanation:-
#include < iostream>
class Sample {
public:
int num1;
mutable int num2;
Sample(int arg1, int arg2) {
num1 = arg1;
num2 = arg2;
}
};
int main() {
const Sample obj(5,50);
std::cout<<"Initial values are:-"<<"\n";
std::cout<< obj.num1<<" & "<< obj.num2<<"\n";
// obj.num1 = obj.num1 * 5; //This line throw ERROR>
obj.num2 = obj.num2 * 5; //This is allowed as num2 is mutable.
std::cout<<"Final values are:-"<<"\n";
std::cout<< obj.num1<<" & "<< obj.num2<<"\n";
return 0;
}
Here we are creating the constant object "obj " of our class "Sample ", so when we are trying to update the value of variable "num1 ", then the
compiler
will throw the error as it is read-only or constant. But this is not the case with variable "num2 ", because it is of type mutable, so its value
can be
changed easily.
The output of the code is as below:-
Initial values are:-
5 & 50
Final values are:-
5 & 250
Summary:
Storage Class Type | Keyword | Accessibility |
---|---|---|
Extern | extern | Throughout the program |
Static | static | File scope |
Register | register | Block (in which it is defined) |
Auto | auto | Block (in which it is defined) |
Mutable | mutable | Class scope |
Const & Volatile Qualifiers:
Other than the Storage classes, one more important concept is the qualifiers. Two important qualifiers control how the variables may be
accessed
or modified. These are "const " and "volatile ".
-
const -
The variable of type "const " cannot be changed by the program. Once the value is assigned to it during its initialization it remains fixed.
Ex-
const int val = 50; -
volatile -
A "volatile " specifier is like the indication to the compiler that a value of the variable may get updated somewhere or in some ways, which
it is not
currently aware of.
Let's take an example of the below code snippet to better understand it.
int val = 15;
...
while(val == 15){
...
// some logic
...
}
When the above code is compiled, then the compiler may do some optimization of its own. The compiler thinks that the value of the variable
"val " is never
be changed which causes the condition of the "while " loop to "true " and it will be executed every time. So it will optimize
the while statement as:
while(true)
The compiler does this because it will save the time to fetch the value of "val " from the memory every time and bring it to the CPU. But this
optimization will not be good
if the value of this variable will be accessed and changed from outside of the program which the compiler is not
aware of and we get an undesired result. So here comes in
picture the "volatile " specifier. We have to declare the variable "val " as volatile.
volatile int val = 15;
It will tell the compiler to be aware and not do the optimization in this case.