C++学习笔记-局部static

局部静态变量和全局静态变量在生存周期上是一样的,都是全局只存在一份。它们只在作用域上有所不同。不过我们在了解局部静态变量前,先看一个之前困扰我的问题。

困扰我的问题

下面是一个简单的单例模式

 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() 未访问成员变量

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();
}

总结

  1. 局部静态变量和全局静态变量在生存周期上是一样的,都是全局只存在一份。它们只在作用域上有所不同。
updatedupdated2025-03-102025-03-10