Storage

Table of contents

  1. Storage class specifiers
  2. Storage duration
  3. Scope
  4. Linkage types
  5. Static

1. Storage class specifiers ↑top

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.

Only one storage class specifier may appear in a decl except that thread local may be combined with stati or with extern.

2. Storage duration ↑top

All objects in a program have one of the following storage durations:

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

2. Scope ↑top

scope is the code part where the declaration can be seen and used.

block scope

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

function 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()
}

namespace scope

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;
}

file scope

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).

class scope

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

3. Linkage types ↑top

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.

4. Static ↑top

static data members

class X{ static int n}; //declaration (uses 'static')
int X::n = 1;           //definition (does not use 'static')

static member functions

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 local variables

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.

declaration vs. definition

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