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 .