Variables: <scope, duration, linkage>
The storage class determines the part of memory where storage is allocated for an object and how long the storage allocation continues to exist. A storage class specifier is used to refine the decl of a variable, a function and parameters. The specifier affects a variable's storage duration, scope and linkage.
The storage duration of a variable is the portion of program execution during which storage for the variable exists. A variable's scope can be block ("{}"), function, program or namespace, etc. The linkage of a variable determines whether it may be shared by more than one file in the same program.
auto
is no longer a storage class specifier, and is instead a simple type specifier.auto
.register
keyword was deprecated since c++11, and will be removed from c++17.static
specifier is used in a decl of a class member, it declares a static member; when used in a decl of an obj, it specifies static storage duration (except if combined with thread_local); when used in a decl at namespace scope, it specifies internal linkage.extern
specifier is only allowed in the decls of variables and funcs (except class members or func paras). It specifies external linkage, and does not technically affect storage duration, but it cannot be used in a definition of an automatic storage duration obj, so all extern objs have static or thread durations. In addition, a variable decl that uses extern and has no initializer is not a definition.thread_local
keyword is only allowed for objects declared at namespace scope, objs declared at block scope, and static data members. It can be combined with static
or extern
to specify internal or external linkage, respectively, but that additional static doesn't affect the storage duration.Only one storage class specifier may appear in a decl except that thread local may be combined with stati or with extern.
All objects in a program have one of the following storage durations:
static
, extern
or thread_local
. If auto variables are not inited, they will contain garbage. Memory is allocated for the obj at each invocation of the block.static
or extern
.thread_local
have this storage duration. thread_local
can appear together with static
or extern
to adjust linkage.thread_local unsigned int rage = 1;
std::mutex cout_mutex;
void increase_rage(const std::string& thread_name){
++ rage; //modifying outside a lock is okay
//, this is a thread-local variable
std::lock_guard<std::mutex> lock(cout_mutex);
}
Variables declared inside a block are called local variables, which have automatic duration.
Variables declared outside of a block are called global variables, which have static duration (means they are created when program starts and are destroyed when it ends).
#include <iostream>
int value(5); // global variable
static int n; //file scoped variable
int main()
{
int value = 7; // hides the global variable value
value++; // increments local value, not global value
::value--; // decrements global value, not local value
std::cout << "global value: " << ::value << "\n";
std::cout << "local value: " << value << "\n";
return 0;
} // local value is destroyed
scope is the code part where the declaration can be seen and used.
int f(int n=2) //scope of 'n' begins
try //func try block
{ //the body of the func begins
++ n; //'n' is in scope and refers to func para
{
int n = 2; //scope of local var 'n' begins
//scope of func para 'n' interrupted
++ n; //'n' refers to the local var in this block
} //scope of the local var 'n' ends
//scope of func para 'n' resumes
} catch (...) {
++ n; //'n' is in scope and refers to func para
throw;
} //last exception handler ends, 'n' scope ends
int a = n; //ERROR: name 'n' is not in scope
a label (an only a label) is declared inside a func is in scope everywhere in that func, in all nested blocks, before and after its own decl.
void f() {
{
goto label; //'label' in scope even though decled later
label:;
}
goto label; //label ignores block scope
}
void g() {
goto label; //ERROR: label not in scope in g()
}
Namespaces provide a method for preventing name conflicts in large projects. Symbols declared inside a namespace block are placed in a named scope that prevents them from being mistaken for identically-named symbols in other scopes. Multi namespace blocks with the same name are allowed. All decls within those blocks are declared in the named scope.
The top-level scope of a translation unit ("file scope" or "global scope") is also a namespace that is properly called "global namespace scope", where the entity scope begins at the decl and continues to the end of translation unit.
namespace vec {
template< typename T >
class vector {
// ...
};
} // of vec
int main(){
std::vector<int> v1; // Standard vector.
vec::vector<int> v2; // User defined vector.
v1 = v2; // Error: v1 and v2 are different object's type.
{ using namespace std;
vector<int> v3; // Same as std::vector
v1 = v3; } // OK
{ using vec::vector;
vector<int> v4; // Same as vec::vector
v2 = v4; } // OK
return 0;
}
All vars and funcs defined outside funcs have file scope, meaning that they are visible from their declarations until the end of the file (file is the compiled source file, with all includes being resolved).
The potential scope of a name declared in a class begins at the point of decl and includes the rest of the class body and all func bodies (even if defined outside the class def or before the decl of the name).
class X {
int f(int a = n) { //X::n is in scope inside default para
return a*n; //X::n is in scope inside func body
}
int g();
int i = n*2; //X::n is in scope inside initializer
//int x[n]; //ERROR: n is not in scope in class body
static const int n = 1;
int x[n]; //OK: n is now in scope in class body
};
int X::g() { return n; }//X::n is in scope
translation unit: a basic unit of compilation. Commonly known as a "source file" or a ".cpp file" after being preprocessed, finishing including all header files.
Linkage: the way the names of objects and functions are shared between translation units.
external linkage: they can refer to program elements in any translation unit in the program, i.e. the element is shared among the units. The same name in another translation unit is guaranteed to refer to the same obj or class. (global)
//global.cpp
int i; //global variable, extern by default
------------
//main.cpp
extern int i; //forward decl for i
internal linkage: they refer only to program elements inside their own translation units; they are not shared with other units. The same name in another translation unit may refer to a different obj or a different class. (local)
static int i; //static ensures internal linkage
//can only be used within this file
const int ci; //static by default
extern const int eci; //explicitly extern
static int si; //explicitly static
int foo(); //extern by default
static int bar(); //explicitly static
no linkage: they refer to unique entities. The same name in another scope may not refer to the same obj.
class X{ static int n}; //declaration (uses 'static')
int X::n = 1; //definition (does not use 'static')
Static member funcs are not associated with any obj. When called, they have no this
pointer.
#include<iostream>
class X{
private:
static int n;
int m = 2;
public:
static int get_n(){return n;}
//ERROR: invalid use in static member function
//static int get_m(){return m;}
int get_n_2(){return n;}
int get_m_2(){return m;}
};
int X::n = 1; //init
int main(int argc, char *argv[]){
//ERROR: n is a private member
//std::cout << "n= " << X::n << std::endl;
std::cout << "n= " << X::get_n() << std::endl;
//std::cout << "m= " << X::get_m() << std::endl;
X my_x;
std::cout << "n= " << my_x.get_n_2() << std::endl;
std::cout << "m= " << my_x.get_m_2() << std::endl;
}
static variables declared at block scope are inited the first time control passes through their decl (unless their initialization is zero- or constant-initialization, which can be performed before the block is first entered). On all further calls, the decl is skipped.
Declarations introduces names and types without giving details, such as where storage is located or how things are implemented:
extern int x; //obj decl
class Widget; //class decl
bool func(const Widget& w); //func decl
enum class Color; //scoped enum decl
Definitions provide the storage locations or implementation details:
int x; //obj definition
class Widget { //class def
...
};
bool func(const Widget& w){ //func def
return w.size() < 10;
}
enum class Color
{ Yellow, Red, Blue }; //scoped enum def