C++ Storage Classes
Storage classes define the scope (visibility) and lifetime of variables/functions in C++ programs. These specifiers are placed before the type they modify. The following lists the storage classes available in C++ programs:
auto: This is the default storage class specifier and can usually be omitted. Variables specified by auto have automatic storage duration, i.e., their lifetime is limited to the block in which they are defined. auto variables are usually allocated on the stack.
register: Used to suggest that the compiler store the variable in a CPU register to improve access speed. In C++11 and later, register is a deprecated feature and no longer has any practical effect.
static: Used to define variables or functions with static storage duration, whose lifetime spans the entire runtime of the program. Inside a function, the value of a static variable remains unchanged between function calls. Within a file or at global scope, static variables have internal linkage and can only be accessed in the file where they are defined.
extern: Used to declare variables or functions with external linkage, which can be shared between multiple files. By default, global variables and functions have extern storage class. Using extern in one file to declare a global variable or function defined in another file enables cross-file sharing.
mutable (C++11): Used to modify member variables in a class, allowing their values to be modified in const member functions. It is commonly used for data such as caches or counters that need to be modified in a const context.
thread_local (C++11): Used to define variables with thread-local storage duration; each thread has its own independent copy. The lifetime of a thread-local variable is the same as the lifetime of the thread.
As of C++17, the auto keyword is no longer a C++ storage class specifier, and the register keyword has been deprecated.
The storage class specifiers therein provide programmers with a means to control the lifetime and visibility of variables and functions.
Reasonable use of storage class specifiers can improve program maintainability and performance.
As of C++11, register has lost its original role, while mutable and thread_local are newly introduced features used to solve specific programming problems.
Below is an example demonstrating different storage class specifiers:
Example
// Global variable with external linkage, default storage class is extern
int globalVar;
void function() {
// Local variable with automatic storage duration, default storage class is auto
auto int localVar = 10;
// Static variable with static storage duration, lifetime spans the entire program
static int staticVar = 20;
const int constVar = 30; // const variable has static storage duration by default
// Attempting to modify a const variable, compilation error
// constVar = 40;
// mutable member variable, can be modified in const member functions
class MyClass {
public:
mutable int mutableVar;
void constMemberFunc() const {
mutableVar = 50; // Allows modifying mutable member variable
}
};
// Thread-local variable, each thread has its own independent copy
thread_local int threadVar = 60;
}
int main() {
extern int externalVar; // Declare a variable with external linkage
function();
return 0;
}
auto storage class
Since C++ 11,autoThe keyword is used in two situations: when declaring a variable, automatically inferring the variable's type based on the initialization expression; when declaring a function, as a placeholder for the function's return value.
In the C++98 standard, the auto keyword was used for the declaration of automatic variables, but because it was rarely used and redundant, this usage was removed in C++17.
Automatically infer the type of the declared variable based on the initialization expression, for example:
register storage class
register is a storage class used to declare variables and hint to the compiler to store these variables in registers for fast access.
Using the register keyword can improve the execution speed of a program because it reduces the number of memory accesses.
However, note that the register storage class is only a hint; the compiler can ignore it, because modern compilers usually automatically optimize code and choose an appropriate storage location.
Syntax:
register data_type variable_name;
registerIt is a storage class keyword, used to hint to the compiler to store variables in registers.data_typeIt is the data type of the variable, which can be any legal C++ data type.variable_nameis the name of the variable.
The register storage class is used to hint to the compiler to store variables in registers in order to improve access speed. However, due to the automatic optimization capabilities of modern compilers, using the register keyword is not necessary and is rarely used in practice.
In the C++11 standard, the register keyword is no longer a storage class specifier but a deprecated feature. This means that in C++11 and later versions, using the register keyword will have no effect on the program.
In C++, references or pointers can be used to improve access speed, especially when dealing with large data structures.
static storage class
staticThe storage class instructs the compiler to keep local variables in existence throughout the life of the program, without needing to create and destroy them each time they enter and leave scope. Therefore, using static to modify a local variable can preserve the value of the local variable between function calls.
The static modifier can also be applied to global variables. When static modifies a global variable, it restricts the variable's scope to the file in which it is declared.
In C++, when static is used on a class data member, it results in only one copy of that member being shared by all objects of the class.
Example
When the above code is compiled and executed, it produces the following results:
变量 i 为 6 , 变量 count 为 9 变量 i 为 7 , 变量 count 为 8 变量 i 为 8 , 变量 count 为 7 变量 i 为 9 , 变量 count 为 6 变量 i 为 10 , 变量 count 为 5 变量 i 为 11 , 变量 count 为 4 变量 i 为 12 , 变量 count 为 3 变量 i 为 13 , 变量 count 为 2 变量 i 为 14 , 变量 count 为 1 变量 i 为 15 , 变量 count 为 0
extern storage class
externThe storage class is used to provide a reference to a global variable that is visible to all program files. When you use 'extern' for a variable that cannot be initialized, it points the variable name to a previously defined storage location.
When you have multiple files and define a global variable or function that can be used in other files, you can use it in other files.externto get a reference to the defined variable or function. It can be understood this way,externIt is used to declare a global variable or function in another file.
The extern modifier is usually used when two or more files share the same global variables or functions, as shown below:
First file: main.cpp
Example
Second file: support.cpp
Example
Here, in the second file, theexternThe keyword is used to declare count, which has already been defined in the first file main.cpp. Now, compile these two files as follows:
$ g++ main.cpp support.cpp -o write
This will producewriteexecutable program, try to executewrite, it will produce the following results:
$ ./write Count is 5
mutable storage class
mutable is a keyword used to modify class member variables so that they can be modified in const member functions. Normally, const member functions cannot modify the state of an object, but if a member variable is declared mutable, it can be modified in const functions.
Features:
- Allows modification:
mutablemember variables can beconstmember function has been changed. - Design purpose: It is usually used in scenarios that require state management without changing the external state of an object, such as caching, lazy evaluation, etc.
Example
class Example {
public:
Example() : value(0), cachedValue(0) {}
// const member function
int getValue() const {
return value; // Read const member
}
// Modify mutable member
void increment() {
++value;
cachedValue = value * 2; // Modify mutable member
}
int getCachedValue() const {
return cachedValue; // Read mutable member
}
private:
int value; // Regular member, cannot be modified in a const function
mutable int cachedValue; // Mutable member, can be modified in const functions
};
int main() {
const Example ex;
// ex.increment(); // Error: cannot call non-const function on const object
// ex.value = 10; // Error: cannot modify member of const object
std::cout << "Value: " << ex.getValue() << std::endl;
std::cout << "Cached Value: " << ex.getCachedValue() << std::endl; // Output is 0
return 0;
}
Applicable scenarios:
- Caching: in
constCalculate and cache the result in the function without affecting the external state of the object. - State tracking: For example, log counters, tracking information such as the number of calls, avoiding invasive modifications to class logic.
Notes:
mutableCare should be taken when using the variable to avoid unexpected state changes, which may affect code readability and maintainability.mutableapplies to the need toconstIt is a specific case of changing state in the environment, rather than a universal design pattern.
thread_local storage class
thread_local is a storage class introduced in C++11, used to manage thread-specific variables in a multithreaded environment.
Variables modified with thread_local have independent instances in each thread, so each thread's operations on the variable do not affect other threads.
- Independence: Each thread has its own independent copy of the variable, and read/write operations between different threads do not interfere with each other.
- Lifecycle:
thread_localThe variable is automatically destroyed when its thread ends. - Initialize:
thread_localVariables can be statically or dynamically initialized, and support initialization at the time of declaration.
thread_local is suitable for scenarios that need to store thread state, caches, or avoid data races, such as thread pools, request contexts, etc.
The following demonstrates variables that can be declared as thread_local:
Notes:
- Performance: Since each thread has an independent copy,
thread_localThe access speed of the variable may be slightly slower than global or static variables. - Static storage:
thread_localThe storage type of the variable is static storage duration, so it persists throughout the entire runtime of the program.