static 是 C++ 中很常用的修饰符,它被用来控制变量的存储方式和可见性。
static 普通变量
static 修饰的普通变量存储在静态区,其生命周期延长至整个程序结束。只有第一次会执行初始化,若无初始值,则用默认值进行初始化。
1class Foo {
2 public:
3 Foo() { std::cout << "Foo\n"; }
4 ~Foo() { std::cout << "~Foo\n"; }
5};
6
7void func() { static Foo f; }
8
9int main() {
10 std::cout << "Start\n";
11 func(); // 初始化 Foo f
12 func(); // 不再对 f 初始化
13 std::cout << "End\n";
14}
15
16// output:
17// Start
18// Foo
19// End
20// ~Foo
特别的,static 修饰全局变量时,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。
static 函数
static 修饰的函数仅在定义该函数的文件内才能使用。一般在多人开发项目时,为了防止与他人命名空间里的函数重名,可以这么做。
static 类成员变量/类成员函数
static 修饰的类成员变量/类成员函数提供一层类共享语义,即该成员变量/函数不属于任一对象,而是属于整个类,在内存中只占用一份空间,可直接通过 类名::成员变量名 或 类名::成员函数名 访问,而不需要先创建对象。根据这一特点,静态函数中也就不存在 this 指针,也无法使用非静态成员变量。
非静态成员变量依附于特定的对象,而静态成员函数在类实例化之前就已经分配空间,此时非静态成员变量连个影子都见不到,更别说使用了。
1class Foo {
2 private:
3 int non_static{0};
4
5 public:
6 // 静态函数既可在类内定义,也可在类外定义
7 static void func1();
8 static void func2() {
9 std::cout << non_static; // ERROR! 不能在静态函数中使用非静态变量
10 func(); // ERROR! 不能在静态函数中调用非静态函数
11 }
12 void func() { func1(); } // OK! 可以在非静态函数中调用静态函数,这里的 func1 并不会隐式使用 this
13
14 static int val1;
15 static const int val2{2}; // 静态常量可以在类内定义
16 static const int val3;
17};
18
19void Foo::func1() {}
20int Foo::val1 = 1; // 静态非常量只能在类外定义
21const int Foo::val3 = 3; // 静态常量也能在类外定义
22
23int main() {
24 Foo::func1(); // OK!
25 Foo::func(); // ERROR! 不能用此法调用非静态函数
26
27 Foo f;
28 f.func1(); // OK! 既然 func1 属于 Foo 类,那么任一对象均可调用该函数
29}
需要注意的是:
- 静态成员变量必须先初始化;
- 构造函数与析构函数、运算符重载这类特殊函数不能用
static进行修饰; - ⚠️如果在类内初始化静态常量,并且采用声明与实现分离的方式编码,则此时在
.cpp文件中访问.h中初始化的static const变量则可能报 「undefined reference」 的链接错误。如果遇到这种情况,有两种解决方案改为类外初始化;
改用
constexpr在类内初始化;之所以说「可能」,是因为在某些情况下编译器可能会直接进行对
.cpp中的static const变量直接用值进行替换,这样就不会用到符号了,也就不会在链接器种报错。