局部静态变量和全局静态变量在生存周期上是一样的,都是全局只存在一份。它们只在作用域上有所不同。不过我们在了解局部静态变量前,先看一个之前困扰我的问题。
下面是一个简单的单例模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class Singleton
{
private:
static Singleton* s_Instance; // 不能在这里直接初始化(如 static Singleton* s_Instance = nullptr;)。
// 静态成员变量的定义(即分配内存空间)需要在类的外部进行,即使它在类内部被声明为private。
public:
static Singleton& Get() { return *s_Instance; }
void Hello()
{
std::cout << "Hello" << std::endl;
}
};
// 在C++中,类的静态成员变量不能在类定义内部直接初始化(如 static Singleton* s_Instance = nullptr;)。
// 这是因为类的静态成员变量是属于类本身的,而不是属于类的任何特定对象,它们的存储和初始化需要在类的外部进行。
Singleton* Singleton::s_Instance = nullptr;
int main()
{
Singleton::Get().Hello(); // 调用没报错
}
|
我们在外部初始化s_Instance为nullptr,为什么Singleton::Get().Hello();调用没有报错?还能正常执行?不是应该空指针异常吗?
原因是和编译器有关,这段代码能够正常输出“Hello",是因为未定义行为(Undefined Behavior, UB) 在某些环境下“碰巧”没有引发崩溃,但其逻辑存在根本性问题。
Hello() 是一个普通成员函数,但它没有访问任何成员变量(如 this->x)。在底层,成员函数调用会被编译为普通函数,隐含的 this 指针作为第一个参数传递。如果 this 指针为 nullptr,但函数内部未使用它,某些编译器和运行时环境可能不会立即触发错误,导致代码“看似正常”执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void Function()
{
static int i = 0; // 第一次调用函数时初始化为0,然后每次调用也不会重新创建变量,因为它是全局的,只是作用域是当前函数,也就是只有当前函数能访问它。
i++;
std::cout << i << std::endl;
}
int main()
{
Function();
Function();
Function();
Function();
Function();
}
|
C++11以后,我们可以通过局部静态变量改造一下上面的单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <iostream>
class Singleton {
public:
static Singleton& Get() {
static Singleton instance; // 利用局部静态变量生存周期也是“永久”的特点,这样就不需要在类外部进行初始化了。
return instance;
}
void Hello() {
std::cout << "Hello" << std::endl;
}
private:
Singleton() {} // 私有构造函数
// 删除拷贝构造函数和赋值操作符
Singleton(const Singleton&) = delete;
void operator=(const Singleton&) = delete;
};
int main() {
Singleton::Get().Hello();
}
|
- 局部静态变量和全局静态变量在生存周期上是一样的,都是全局只存在一份。它们只在作用域上有所不同。