在局部作用域中使用static来声明一个变量(静态局部Local static变量)
声明一个变量需要考虑两种情况:变量的生存期和变量的作用域
变量的生存期指的是变量实际存在的时间,换句话说,在它被删除之前,它会在我们的内存中存在多久。
变量的作用域是指我们可以访问变量的范围,即如果在一个函数内部声明一个变量,我们不能在其他的函数中访问它,因为我们声明的变量对于我们声明的函数是局部的。
原理解释
静态局部(local static) 变量允许我们声明一个变量,它的生存周期基本上相当于整个程序的生存期,然而它的作用范围被限制在这个函数内,但它其实和函数没有什么关系。即意味着你可以在任何作用域中声明这个,刚才只是用函数举个例子,这并不仅仅局限于函数内部,也可以在if语句中,也可以在任何位置。
这也是为什么函数的作用域中的static和类作用域中的static之间没有太大的区别,因为生存期实际上是相同的,唯一的区别是,在类作用域中,类中的任何东西都可以访问它(这个静态变量)
然而如果你在函数的作用域中声明一个静态变量,那么它将是那个函数的局部变量,对类来说也是局部变量。
代码实现-函数
# include <iostream>
using namespace std;
//int i = 0;将i=0;放到外面成为全局变量,会造成在任意地方都能访问i
void Function()
{
//声明静态变量
static int i = 0;
//加入static即 static int i = 0;相当于将 int i = 0;放到函数外面成为全局变量.
//区别在于加入static仍然是一个局部变量,而放到函数外面就是一个全局变量,导致在任何位置都可以访问
i++;
cout << i << endl;
//不加入static,打印出i的值都是1,每次调用函数都重新对i赋初值
//加入static后,生存周期并不局限于函数而是整个程序
}
int main()
{
Function();
//i = 10; 此时的i是未定义的,因为i是局部变量
Function();
Function();
cin.get();
return 0;
}
在上述的代码中,程序运行的结果就是1、2、3,i是静态局部变量,如果将程序中static去掉,程序运行的结果就是1、1、1,因为此时的i是局部变量。如果将int i = 0;放到函数外面会实现与静态布局变量相同的效果。一旦在主程序中改变i的值,打印的结果就会变化。如果是静态局部变量,改变i的值,会报错,因为i的作用域是在函数内部,是局部变量。
代码实现-类
单例类是指只存在一个实例的类,如果我想创建这个单例类而不使用静态局部作用域,我就需要创建静态的单例实例,可能是一个指针。
使用指针实现单例类
# include <iostream>
using namespace std;
//单例类是指只存在一个实例的类,如果我想创建这个单例类而不使用静态局部作用域,我就需要创建静态的单例实例,可能是一个指针
class Singleton
{
private:
//声明了一个静态指针成员
static Singleton* s_Instance; // s_Instance是Singleton类的一个实例的指针,存储着Singleton类的实例的地址,并且该实例被声明为static,无论创建多少个Singleton的实例,s_Instance都只有一个副本
public:
static Singleton& Get() // 定义了一个静态函数,返回类型是Singleton&,表示返回一个对Singleton类型对象的引用
{
return *s_Instance;
}
//Get()是一个静态函数,因此可以通过类名直接调用,而不需要实例化对象,这个例子中,它充当了获取Singleton实例的全局访问点
//返回类型是Singleton&,即Singleton类型的引用。
// 引用是一个别名,它允许我们使用另一个对象的名称来访问同一对象。通过返回引用,而不是返回指针,我们可以避免在使用返回值时需要进行解引用操作
//函数Get()返回的时s_Instance指针所指向的Singleton实例的引用。
//通过返回引用,可以避免复制对象的开销,并确保在调用方修改实例时,这些修改也会影响到单例模式中的唯一实例。
void Hello() { }
};
//声明这个实例
Singleton* Singleton::s_Instance = nullptr;
//在类外进行静态成员的初始化是因为静态成员不属于类的任何实例,它们属于整个类。因此它们需要在类外进行初始化。通过在类外进行初始化,可以确保它们只有一个实例,而不是在每个文件中都有一个独立的实例
//静态成员变量需要在程序开始之前进行初始化,并且只有一个实例。
int main()
{
//利用单例类
Singleton::Get().Hello();
cin.get();
return 0;
}
Singleton* Singleton::s_Instance = nullptr;指的是静态成员变量的初始化,在类外进行静态成员的初始化是因为静态成员不属于类的任何实例,它们属于整个类。因此它们需要在类外进行初始化。通过在类外进行初始化,可以确保它们只有一个实例,而不是在每个文件中都有一个独立的实例。静态成员变量需要在程序开始之前进行初始化,并且只有一个实例。
static Singleton* s_Instance; 其中 s_Instance 是 Singleton 类的一个实例的指针,存储着 Singleton 类的实例的地址,并且该实例被声明为 static(静态),无论创建多少个Singleton的实例,s_Instance都只有一个副本。如果不了解静态(static),可以查看另一篇文章关于static(静态)在类和结构体中的用法。
static Singleton& Get() { return *s_Instance; } 定义了一个静态函数,返回类型是 Singleton& ,表示返回一个对 Singleton 类型对象的引用。Get()是一个静态函数,因此可以通过类名直接调用,而不需要实例化对象,这个例子中,它充当了获取Singleton实例的全局访问点。
函数Get()返回的时s_Instance指针所指向的Singleton实例的引用。通过返回引用,可以避免复制对象的开销,并确保在调用方修改实例时,这些修改也会影响到单例模式中的唯一实例。
解释一下引用(&):引用是一个别名,它允许我们使用另一个对象的名称来访问同一对象。通过返回引用,而不是返回指针,我们可以避免在使用返回值时需要进行解引用操作。
函数返回引用代码实现
#include <iostream>
using namespace std;
int globalValue = 10;
// 返回类型为 int&,表示返回 globalValue 的引用
int& getGlobalValue() {
return globalValue;
}
int main() {
// 打印当前的 globalValue
cout << "Initial value: " << globalValue << endl; // 输出:Initial value: 10
// 修改引用返回的值
getGlobalValue() = 20; // getGlobalValue 返回 globalValue 的引用,然后将其修改为 20
// 再次打印 globalValue,查看其是否被修改
cout << "Modified value: " << globalValue << endl; // 输出:Modified value: 20
return 0;
}
使用静态局部作用域实现单例类
# include <iostream>
using namespace std;
class Singleton
{
public:
static Singleton& Get()
{
static Singleton s_Instance;
return s_Instance;
}
void Hello() { }
};
int main()
{
//利用单例类
Singleton::Get().Hello();
cin.get();
return 0;
}
静态局部变量不需要在类外初始化,而静态成员变量需要在类外进行初始化。
静态局部变量不需要在类外进行初始化,静态局部变量在函数内部声明,但是只会在第一次进入该函数是进行初始化,没有显示初始化就会被默认构造函数进行初始化,然后在函数生命周期结束时销毁。
静态成员变量需要在程序开始之前进行初始化,并且只有一个实例。