static的三种用法#
类外部的 static(作用域限制)#
当你把 static 放在全局变量或函数前面时,它的作用是 “私有化” 这个文件。
- 效果:该变量或函数仅在定义它的 .cpp 文件(编译单元) 内可见。
- 为什么要用它?
- 防止 命名冲突:如果你在 A.cpp 定义了一个 static int x,在 B.cpp 也定义了一个 int x,链接器(Linker)不会报错,因为 A 的 x 是文件私有的。
- Cherno 的建议:除非你需要变量在多个文件间共享,否则尽量加上 static,让它保持局部化。
类内部的 static(成员共享)#
当你把 static 放在类成员变量或函数前面时,它的含义变成了 “属于类,而不属于对象”。
静态变量#
- 效果:无论你创建了多少个该类的对象(实例),这个变量在内存中 只有一份。
- 用法:所有对象共享同一个变量。修改其中一个对象的静态变量,其他对象看到的也是修改后的值。
- 初始化:必须在类外单独进行初始化。
静态函数:#
- 效果:该函数不需要通过对象(obj.Func())调用
可以但不推荐,编译器实际上会忽略这个对象,查看obj类型并转化为Class::Func()),可以直接通过类名调用(Class::Func()。 - 限制:静态函数 没有 this 指针,因此它只能访问类里的其他静态成员,不能访问普通的非静态成员。
局部静态变量#
局部静态变量是指在函数内部声明并带有 static 关键字的变量。
生命周期:它的生命周期贯穿整个程序。从它第一次被声明开始,直到程序结束它才会消失。
作用域:虽然它一直存在,但它只能在声明它的函数内部被访问。
局部静态变量#
声明变量时要考虑两个因素:变量的生命周期 能存在多久 ,变量的作用域 能在哪些地方访问这些变量
作用域:如果在函数内声明一个变量则无法在其他函数中访问他
特点:
- 初始化时机:它在程序执行到该声明语句时第一次且仅初始化一次。
- 销毁时机:它不会在函数运行结束时销毁,而是在整个程序结束时才会被销毁。
局部静态变量可以放在任何局部范围内,比如 if 语句或 for 循环的大括号里。
#include <iostream>
void Test()
{
bool condition = true;
if (condition)
{
// 这里的 static 变量不属于整个函数,只属于这个 if 块
static int i = 0;
i++;
std::cout << i << std::endl;
}
// 这里无法访问变量 i,因为它在 if 的作用域之外
}循环内
#include <iostream>
int main() {
for (int count = 0; count < 5; count++) {
// 这一行在 count == 0 时执行初始化
// 在 count == 1, 2, 3, 4 时,编译器会直接“无视” = 0 这个动作
static int i = 0;
i++;
std::cout << "Loop " << count << ": i = " << i << std::endl;
}
}函数作用域里的静态变量和类作用域的静态变量没太大区别,声明周期一样。唯一的区别是类作用域的那个类里的任何东西都能访问它,而函数内的静态变量则是该函数(某个作用域)的局部变量
不同函数的同名静态变量互不影响#
#include <iostream>
void Function()
{
int i = 0;
i++;
std::cout << i << std::endl;
}
void Function1()
{
//首次运行时设置为0,但是
//之后进来仍然保存上次计算后的值
static int i = 0;
i++;
std::cout << i << std::endl;
}
void Function2()
{
static int i = 2;
i+=2;
std::cout << i << std::endl;
}
int main()
{
Function();
Function();
Function();
/*
1
1
1
*/
Function1();
Function2();
Function1();
Function2();
Function1();
Function2();
/*
1
2
2
4
3
6
*/
std::cin.get();
}
全局变量#
#include <iostream>
int i = 0;
void Function()
{
i++;
std::cout << i << std::endl;
}
int main()
{
Function();
Function();
//任何地方任何人都能访问它
i = 10;
Function();
std::cin.get();
}单例类#
静态单例实例#
不通过静态局部作用域
#include <iostream>
class Singleton
{
private:
//如果用对象:static Singleton s_Instance; 会在程序启动时(main 函数运行前)就尝试调用构造函数来创建对象。
//如果用指针:static Singleton* s_Instance; 在程序启动时只是初始化了一个空的“地址盒子”(nullptr),并没有真正创建对象。这给了你 “按需创建”(Lazy Initialization)的机会,即只有当你第一次调用 Get() 时,对象才会被创建。
static Singleton* s_Instance;
public:
static Singleton& Get()
{
if (!s_Instance)
{
s_Instance = new Singleton;
}
return *s_Instance;
}
void Hello()
{
std::cout << "Hello" << std::endl;
}
};
Singleton* Singleton::s_Instance = nullptr;
int main()
{
Singleton::Get().Hello();
std::cin.get();
}本地静态变量#
#include <iostream>
class Singleton
{
public:
static Singleton& Get()
{
//如果没有static,那么instance就是栈上的
//static:存放在静态存储区中,它和全局变量
// 待在同一个内存区域。
//静态存储区(逻辑概念)里的变量,最终都会被编译器安排住进“数据段(物理实现)”这栋大楼里。数据段包括已初始化静态存储、未初始化静态存储、只读静态存储
//只有在第一次调用时返回instance实例,之后每次
//调用都会只返回那个已有实例
static Singleton instance;
return instance;
}
//返回的是引用,但是instance在
//函数退出后就从栈上消失了,这段代码
//有安全隐患
static Singleton& Get1()
{
Singleton instance;
return instance;
}
void Hello()
{
std::cout << "Hello" << std::endl;
}
};
int main()
{
Singleton::Get().Hello();
std::cin.get();
}其他用途#
在程序里有时得调用静态初始化函数,从而创建所有对象,就能用静态获取方法了
#include <iostream>
#include <string>
class LogSystem {
private:
static LogSystem* s_Instance; // 静态指针,用来指向那个唯一的对象
std::string m_Status;
LogSystem() { m_Status = "已就绪"; } // 私有构造函数
public:
// 1. 静态初始化函数:负责把对象创建出来
static void Init() {
if (!s_Instance) {
s_Instance = new LogSystem();
std::cout << "系统:日志单例已静态初始化完成。\n";
}
}
// 2. 静态获取方法:全程序任何地方都能拿
static LogSystem& Get() {
return *s_Instance;
}
void Print(const std::string& msg) {
std::cout << "[" << m_Status << "] " << msg << std::endl;
}
};
// 在类外定义静态指针
LogSystem* LogSystem::s_Instance = nullptr;
int main() {
// 【关键点】:在程序刚开始时,调用静态初始化函数
// 这一步“创建了所有(必要的)对象”
LogSystem::Init();
// 之后在程序的任何角落,只需要用“静态获取方法”即可
LogSystem::Get().Print("这是一条日志。");
return 0;
}如上,就是说可以利用静态初始化函数,提前把大部分需要的对象提前创建出来