The storage classes in C++ are : auto , static , extern , mutable , register , and thread_local . They are used in declarations to specify how a definition takes place .
Definition for a variable , is allocating its storage , for a function , it is writing down the instructions that it is going to execute , for the class types : class , struct and union , it is the declaration of their methods and data members .
extern int var_i;
/*Declaration of a variable .*/
float var_f;
/*Declaration , and definition of
an object .*/
int fctFoo(int a , int b);
/*Declaration of a function .*/
int fctFoo(int a , int b){
/*Definition of a function .*/
return a + b ;}
class classFoo;
/*Declaration of a class .*/
class classFoo{
/*Definition of the classFoo .*/
public :
int a;
void addTo(int b ); };
Table of Contents
auto
auto means that a definition is automatic to its scope . An object is automatically created when its containing block is entered , and is destroyed , when its containing block is exited .
When a local variable is declared , and if no storage class is specified , it has an automatic storage . A Local variable is declared , in the local scope .
int trivial(int arg_i , float arg_f ){
double var_d = 1 ;
return arg_i + arg_f + var_d ;}
/*arg_i , arg_f and var_d are local
variables , they are created
on the local scope entry and
destroyed on its exit .*/
The auto storage class was removed in C++11 , and the auto keyword since C++11 , has another meaning .
extern
extern means that the definition is external to the scope .
The extern keyword cannot be used in a class scope , so it cannot be used with members of a class , struct or a union . It also cannot be used with the parameters of a function , or with the declaration of a class type .
Example of the usage of extern in a local scope :
#include<iostream>
int var_i = 1 ;
/*var_i , is not declared extern ,
as such its definition is not
external to its scope .*/
float var_f = 5.0f ;
void fctTrivial(){
extern int var_i;
/*var_i is declared ,
in the local scope .
The definition of
var_i is external to
the local scope .*/
std::cout << var_i << std::endl;
/*Output:
1 .*/
std::cout <<var_f << std::endl;
/*var_f is not declared ,
in the local scope , it
is accessible from
the global scope .
Output :
5 .*/ }
int main(void ){
fctTrivial(); }
Example of the usage of extern in a global scope :
| tr_unit1.cpp | tr_unit2.cpp | g++ tr_unit1.cpp tr_unit2.cpp After compilation , the two gotten object files are merged into one . |
|---|---|---|
| int var_i = -1; | int var_i; | var_i is not declared extern in tr_unit1 . Its definition is not external to its scope . var_i is not declared extern in tr_unit2 , its definition is not external to its scope . When merging the two object files , one symbol is gotten with two definitions , this leads to duplicate symbol error . |
| float var_f = -1.0f; | extern float var_f = -1.0f; | var_f is not declared extern in translation unit one . Its definition is not external to its scope . var_f is declared extern in translation unit two . It is allowed to define an extern declaration , and var_f is defined in translation unit two . When merging the two object files , one symbol is gotten with two definitions , this lead to duplicate symbol error . |
| float var_f = -1.0f; | extern float var_f ; | var_f is not declared extern in translation unit one , and a definition is provided . var_f is declared extern in translation unit two , and no definition is provided . When merging the two files , one symbol is gotten with the definition provided in translation unit one . |
| extern double var_d = 2.0; | extern double var_d = 3.0; | var_d is declared extern in both translation unit one and two , and two definitions are provided . When merging the two object files , one symbol is gotten with two definitions , this leads to duplicate symbol error . |
| extern double var_d = 2.0; | extern double var_d ; | var_d is declared extern in both translation unit one , and translation unit two . It is defined only in translation unit one . When merging the two object files , one symbol is gotten , with the definition provided by translation unit one . |
| const int var_ci = 55; | const int var_ci = 5; | var_ci is declared in both tr_unit2 and tr_unit1 . It is declared as constant , and not declared as extern . When merging the two object files , each has a separate constant , the constant are not merged . |
| const int var_ci = 55; | extern const int var_ci = 5; | var_ci is declared in both translation units , as a constant , and defined in both . It is declared in translation unit two as extern . When merging the two gotten object files , two separate constants are gotten . |
| const int var_ci = 55; | extern const int var_ci; | var_ci is declared constant in the two translation units . In translation unit two , it is declared as extern , and no definition is provided . When merging the two gotten object files , two constants are gotten , the one from translation unit one is defined , and the one from translation unit two has no definition , this leads to the error of symbol not found . |
| extern const int var_ci = 55; | extern const int var_ci = 5; | var_ci is declared as const and extern in both translation units . A definition is provided in both translation units , this leads to an error of duplicate symbol when compiling . |
| int foo( ){ /*body*/ } | extern int foo( ); | foo is declared in both translation units one and two . It is defined in translation unit one , and is declared extern in translation unit two . When merging the two object files , one symbol is gotten for the function foo , having the definition provided by translation unit one . |
| tr_unit1.cpp | tr_unit2.cpp | g++ tr_unit1.cpp tr_unit2.cpp After compilation , the two gotten object files are merged into one . |
Example of using extern in a namespace .
#include<iostream>
namespace nmspc {
extern int var_i = -1; }
namespace nmspc {
extern int var_i ;
/*extern int var_i = -1;
This will cause an error
of var_i being redefined .*/ }
int main(void ){
std::cout << nmspc::var_i << std::endl;
/*Output :
-1 .*/}
static
static means that the definition is static , in other words , fixed to its scope .
The static storage class , can be applied only to variables , to functions , and to anonymous unions , when they are being declared , in a file scope , in a namespace scope , in a class scope , and in a local scope .
Example of usage of static in a local scope :
#include<iostream>
int iterator(){
static int var_i = -1;
/*Definition of var_i is to
be static to its scope .
var_i is not to be created
every time the local block
is entered , nor is it to be
destroyed every time the local
block is exited .*/
return ++var_i; }
int main(void ){
std::cout << iterator() << std::endl ;
std::cout << iterator() << std::endl ;
/*Output :
0
1 .*/}
Examples of usage of static in a global scope :
| tr_unit1.cpp | tr_unit2.cpp | g++ tr_unit1.cpp tr_unit2.cpp After compilation , the two gotten object files are merged into one . |
|---|---|---|
| static int stFct(){/*body*/ } | extern int stFct(); | stFct is declared static in tr_unit1 , its definition is fixed to its scope . stFct is declared extern in tr_unit2 , its definition is extern to its scope . When merging the two files , no definition is found for stFct , this will lead to compiler error. |
| static int var_i = 1 ; | int var_i = -1; | var_i is declared static in tr_unit1 , its definition is fixed to its scope . var_i is not declared static in tr_unit2 , its definition is not fixed to its scope . When merging the two gotten object files , two symbols are gotten , one is fixed to its scope , and the other is not fixed to its scope . |
/*File tr_unit1.cpp */
static int id = 0 ;
int generateId(){
return id++; }
void resetId(){
id = 0; }
/*File tr_unit2.cpp */
#include<iostream>
extern int generateId();
extern int resetId();
int main(void ){
std::cout << generateId() << std::endl ;
std::cout << generateId() << std::endl ;
resetId();
std::cout << generateId() << std::endl ; }
/*
$ g++ tr_unit1.cpp tr_unit2.cpp
$ ./a.out
0
1
0
*/
Example of usage of static in a class scope :
#include<iostream>
class Trivial{
public :
static int var_i;
static int foo_f(); };
int Trivial::var_i = 0;
int Trivial::foo_f(){
return Trivial::var_i++; }
int main(void ){
std::cout << Trivial::var_i << std::endl;
/*Output :
0 */
std::cout << Trivial::foo_f() << std::endl;
/*Output :
0 */
std::cout << Trivial::foo_f() << std::endl;
/*Output :
1 */
std::cout << Trivial::var_i << std::endl;
/*Output :
2 */ }
mutable
mutable means that the definition is mutable to its scope .
The mutable storage class , applies only to non static class data members .
#include<iostream>
class Trivial{
public:
int var_i;
mutable float var_fm;
void foo_f() const;
/*foo_f is a constant member
function .*/ };
void Trivial::foo_f() const {
/*Provide a definition for foo_f .*/
var_fm /= 2; }
int main(void ){
const Trivial trivial = {1 , 1.0f };
/*define a trivial object .*/
/*trivial.var_i = 3 ;
This statement is illegal , because
the trivial object has been declared
const .*/
trivial.var_fm = -1.0f;
/*var_fm can be assigned a value ,
since its storage class is mutable .*/
std::cout << trivial.var_fm << std::endl ;
/*Output :
-1 .*/
trivial.foo_f();
/*a function declared const , is not
allowed to changed an object data
member , unless this data member
is declared mutable .*/
std::cout << trivial.var_fm << std::endl ;
/*
Output :
0.5 .*/ }
register
register means that a definition is to be registered . This is not necessarily done , the register storage class can be ignored by the compiler .
register is done for faster access . The definition can be registered either in cache memory , or in a cpu register .
register only apply to local variables .
The register storage class was deprecated in the C++11 standard , and removed in the c++17 standard .
#include<iostream>
int fly(register int a , register int v ){
while(v > 0 ){
a *= a;
v--; }
return a; }
int main(void ){
std::cout << fly(2 , 4 ) << std::endl;
/*Output :
65536 .*/ }
thread_local
thread_local means that the definition is local to its thread .
This storage class can only be applied to variables declared in a local scope , or in a namespace such as the global namespace , or to static data members .
thread_local means that a variable is local to its thread , the variable is created when its thread is created , and is destroyed when its thread is destroyed .
The thread_local storage class , can be used with the static , and extern storage classes , and it was introduced in C++11 .
