C++ 静态数据
在 C++ 中,静态数据(Static Data)是指使用 static 关键字修饰的数据,其生命周期与程序的整个运行时相同,而不是局限于某个作用域。静态数据在程序启动时分配内存,在程序结束时释放。与普通的局部变量或动态分配的变量不同,静态数据的生存期是全局的,但其作用域可以是局部的或全局的。
静态数据可以出现的场景:
- 全局/命名空间作用域中的静态变量:在文件级别定义的静态变量,只在定义它的文件中可见。
- 函数中的静态局部变量:在函数内部定义,只初始化一次,且在函数调用之时保持其作用域。
- 类的静态数据成员:属于类本身,而不是类的某个对象,所有对象共享同一份数据。
如何使用静态数据?
C++ 中的静态数据的定义和使用方式包括:
-
全局静态变量
static int globalStaticVar = 10; // 只在当前文件中可见
使用 static 修饰全局变量,限制其作用域为当前文件。
常用于模块化编程,避免与其他文件的同名变量冲突。 -
函数中的静态局部变量
void counter() { static int count = 0; // 只初始化一次 count++; std::cout << "Count: " << count << std::endl; } int main() { counter(); // 输出: Count: 1 counter(); // 输出: Count: 2 counter(); // 输出: Count: 3 return 0; }
函数中的静态变量在函数第一次调用时初始化为0,后续调用不会重新初始化,并且保留之前值,可以看到 count。
-
类的静态数据成员
class MyClass { public: static int staticMember; // 声明静态数据成员 }; // 在类外定义并初始化 int MyClass::staticMember = 100; int main() { MyClass obj1, obj2; obj1.staticMember = 200; // 修改静态成员 std::cout << obj2.staticMember << std::endl; // 输出: 200 std::cout << MyClass::staticMember << std::endl; // 输出: 200 return 0; }
静态数据成员必须在类外定义并初始化。所有对象共享同一份 staticMember,可以通过对象或类名访问。
静态数据的定义方式
- 全局静态变量:static 类型 变量名;
- 函数内静态变量:在函数内使用 static 类型 变量名;
- 类静态数据成员:
- 在类中声明:static 类型 变量名;
- 在类外定义:类型 类名::变量名 = 初始值;
静态数据成员的访问方式
- 通过类名访问:类名::静态成员名(推荐方式)。
- 通过对象访问:对象名.静态成员名(不推荐,因为容易让人误以为是对象的成员)。
从内存层面理解静态数据
在内存中,静态数据存储在程序的数据段(Data Segment) 中。具体在数据段的什么位置:
已初始化数据段(.data):存储已初始化的全局变量和静态变量。
未初始化数据段(.bss):存储未初始化的全局变量和静态变量(默认初始化为 0)。
例如:
static int a = 10; // .data 段
static int b; // .bss 段
在编译阶段由编译器识别到 static 关键字,将标识符标记为静态存储,并在符号表中记录条目,并在最后的目标文件中标记其存储的位置,程序在完成最后的链接操作后就为静态数据分配内存布局。
对于函数中的静态局部变量,编译器会将其存储在数据段,并在第一次执行到定义时进行初始化,如果编译器进行优化会在编译阶段就进行初始化。
类中的静态数据
静态数据成员是类的一个特殊成员,它被类的所有对象共享,而不是每个对象都拥有自己的副本。这意味着无论创建多少个类的实例,静态数据成员在内存中只存在一份。
在 C++98 中,静态数据成员通常需要在类内声明,在类外定义并初始化
class MyClass {
public:
static int count; // 类内声明
static const int VALUE = 10; // const 静态成员可以在类内初始化
};
int MyClass::count = 0; // 类外定义并初始化非const静态成员
C++98 标准允许 const 静态成员在类内初始化。
C++17 引入内联静态成员变量,允许在类内直接初始化非 const 静态成员:
class MyClass {
public:
inline static int count = 0; // 类内直接初始化
inline static std::string name = "Class"; // 复杂类型也可以
};
在类的内部声明相同类型的静态数据成员
类可以包含与自身类型相同的静态成员,这在实现设计模式如单例模式时很有用:
class Node {
private:
int data;
Node* next;
// 静态成员,类型为 Node*
static Node* head;
static Node* tail;
public:
Node(int val) : data(val), next(nullptr) {}
static void add(int val) {
Node* newNode = new Node(val);
if (!head) {
head = tail = newNode;
} else {
tail->next = newNode;
tail = newNode;
}
}
};
// 类外定义
Node* Node::head = nullptr;
Node* Node::tail = nullptr;