Static members

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class A {
public:
    // non-static member (i.e., `data` is not visible in `fun1`
    static fun1();
    fun2();
private:
    int data;
    static int sata;
};

A a;
a.fun1();   // valid, equivalent to the following
A::fun1();
  1. 静态成员不能访问非静态成员(因为静态成员独立与类的实例(即对象)而存在,为了在没有对象被创建的情况下,静态成员还是可以使用,所以不能访问非静态成员。)
  2. 同理,类的任何对象不包含静态数据成员
  3. 静态成员不与对象,不与this指针发生交互,作为结果,静态成员函数不能声明为const
  4. 可以通过类的对象调用静态成员函数,但此调用跟对象的状态并无关系,也就是说换个对象来调用是等价的,都等价于使用类名加域作用符来调用
  5. 静态成员一般定义在类的外部,因为每个对象都共享静态成员,避免多次定义
  6. View static member as a normal function that has nothing to do with the class, except you must use :: to access static members

test for emphsize 中文, 再来一次..强调中文..

Static local variables

Function parameters, as well as variables defined inside the function body, are called local variables.

Much like a person’s lifetime is defined to be the time between their birth and death, an object’s lifetime is defined to be the time between its creation and destruction. Note that variable creation and destruction happen when the program is running (called runtime), not at compile time. Therefore, lifetime is a runtime property.

A variable’s storage duration (usually just called duration) determines what rules govern when and how a variable will be created and destroyed. In most cases, a variable’s storage duration directly determines its lifetime.

Local variables have automatic duration, which means they are created at the point of definition and destroyed at the end of the block they are defined in. For example:

1
2
3
4
5
6
7
int main()
{
    int i { 5 }; // i created and initialized here
    double d { 4.0 }; // d created and initialized here

    return 0;
} // i and d are destroyed here

For this reason, local variables are sometimes called automatic variables.

Global variables are created when the program starts, and destroyed when it ends. This is called static duration. Variables with static duration are sometimes called static variables.

In C++, variables can also be declared outside of a function. Such variables are called global variables. Unlike local variables, which are uninitialized by default, static variables are zero-initialized by default.

Scope determines where a variable is accessible. Duration determines where a variable is created and destroyed. Linkage determines whether the variable can be exported to another file or not.

Here comes the word: using the static keyword on a local variable changes its duration from automatic duration to static duration. This means the variable is now created at the start of the program, and destroyed at the end of the program (just like a global variable). As a result, the static variable will retain its value even after it goes out of scope!

Automatic duration (default):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

void incrementAndPrint()
{
    int value{ 1 }; // automatic duration by default
    ++value;
    std::cout << value << '\n';
} // value is destroyed here

int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();

    return 0;
}

/**
 * Outputs:
 * 2
 * 2
 * 2
 */

Each time incrementAndPrint() is called, a variable named value is created and assigned the value of 1. incrementAndPrint() increments value to 2, and then prints the value of 2. When incrementAndPrint() is finished running, the variable goes out of scope and is destroyed.

Static duration (using static keyword):

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include <iostream>

void incrementAndPrint()
{
    static int s_value{ 1 }; // static duration via static keyword.  This initializer is only executed once.
    ++s_value;
    std::cout << s_value << '\n';
} // s_value is not destroyed here, but becomes inaccessible because it goes out of scope

int main()
{
    incrementAndPrint();
    incrementAndPrint();
    incrementAndPrint();

    return 0;
}

/**
 * Outputs:
 * 2
 * 3
 * 4
 */

Static local variables that are zero initialized or have a constexpr initializer can be initialized at program start. Static local variables with non-constexpr initializers are initialized the first time the variable definition is encountered (the definition is skipped on subsequent calls, so no reinitialization happens). Because s_value has constexpr initializer 1, s_value will be initialized at program start.

When s_value goes out of scope at the end of the function, it is not destroyed. Each time the function incrementAndPrint() is called, the value of s_value remains at whatever we left it at previously.

Generating a unique ID number is very easy to do with a static duration local variable:

1
2
3
4
5
int generateID()
{
    static int s_itemID{ 0 };
    return s_itemID++; // makes copy of s_itemID, increments the real s_itemID, then returns the value in the copy
}

The first time this function is called, it returns 0. The second time, it returns 1. Each time it is called, it returns a number one higher than the previous time it was called. You can assign these numbers as unique IDs for your objects. Because s_itemID is a local variable, it can not be “tampered with” by other functions.

Static variables offer some of the benefit of global variables (they don’t get destroyed until the end of the program) while limiting their visibility to block scope. This makes them safer for use even if you change their values regularly.

Best practice: Initialize your static local variables. Static local variables are only initialized the first time the code is executed, not on subsequent calls.

Q: What effect does using keyword static have on a global variable? What effect does it have on a local variable?

A: When applied to a global variable, the static keyword defines the global variable as having internal linkage, meaning the variable cannot be exported to other files.

When applied to a local variable, the static keyword defines the local variable as having static duration, meaning the variable will only be created once, and will not be destroyed until the end of the program.

Static global variables

Identifiers have another property named linkage. An identifier’s linkage determines whether other declarations of that name refer to the same object or not.

Local variables have no linkage, which means that each declaration refers to a unique object.

Global variable and functions identifiers can have either internal linkage or external linkage.

An identifier with internal linkage can be seen and used within a single file, but it is not accessible from other files (that is, it is not exposed to the linker). This means that if two files have identically named identifiers with internal linkage, those identifiers will be treated as independent.

To make a non-constant global variable internal, we use the static keyword.

1
2
3
4
5
6
7
8
9
static int g_x; // non-constant globals have external linkage by default, but can be given internal linkage via the static keyword

const int g_y { 1 }; // const globals have internal linkage by default
constexpr int g_z { 2 }; // constexpr globals have internal linkage by default

int main()
{
    return 0;
}

Thus, when applied to a local variable, the static keyword defines the local variable as having static duration, meaning the variable will only be created once, and will not be destroyed until the end of the program.

To be continued…

References

  1. 6.10 — Static local variables