【C++】静态

C++中的静态

在类或结构体外部使用static

  • 意味着声明为static的符号,链接将只是在内部;
  • 它只对你定义的翻译单元可见。

静态变量或函数

当需要将这些变量或函数与实际定义的符号链接时,链接器不会在这个翻译单元的作用域之外寻找这个符号的定义

例1
// static.cpp
static int s_Variable = 5;
// Main.cpp
#include <iostream>
int s_Variable = 10;
int main()
{
std::cout << s_Variable << std::endl;
std::cin.get();
}

打印出来为10, 不会报错,因为static.cpp里面的s_Variable是static的,只会在static.cpp内部链接

  • 用extern链接外部变量
// Main.cpp
#include <iostream>
extern int s_Variable;
int main()
{
std::cout << s_Variable << std::endl;
std::cin.get();
}

无法解析外部符号,也就是说找不到s_Variable这个外部变量,因为s_Variable是静态,只能在翻译单元static.cpp内部链接,对Main.cpp来说,s_Variable是不可见的。

例2
// static.cpp
int s_Variable = 5;
// Main.cpp
#include <iostream>
extern int s_Variable;
int main()
{
std::cout << s_Variable << std::endl;
std::cin.get();
}
  • 两个相同的变量名,可以用extern让main.cpp的变量从外部链接;
  • 在外部翻译单元中寻找s_Variable变量;
  • 注意extern 后面不能赋值

为什么需要用到static

  1. 如果不需要变量是全局变量,则需要更多地使用静态变量
  2. 如果没有设定为static,链接器会跨翻译单元链接
  3. 要让变量和函数标记为static,除非真的需要它们跨单元链接

在类或结构体内部使用static

意味着该变量实际上将与类的所有实例共享内存。

类中的静态变量

  • 该静态变量在类创建的所有实例中,实例只有一个静态变量;
  • 创建一个Entity的类,不断地创建Entity实例,所有实例仍然只有一个变量;
  • 如果某个实例改变了这个静态变量,在所有的实例中都会反映这个变化;
  • 因此,通过类实例引用静态变量是无意义的,因为这就像类的全局实例。
例1
struct Entity
{
 int x, y;

 void Print()
 {
  std::cout << x << ", " << y << std::endl;
 }

};
int main()
{
    Entity e;
    e.x = 2;
    e.y = 3;

    Entity e1 = {5, 8};
    e.Print();
    e1.Print();
}

得到
2, 3
5, 8
正常的结果输出

例2
struct Entity
{
 static int x, y;

 void Print()
 {
  std::cout << x << ", " << y << std::endl;
 }

};
int main()
{
 Entity e;
 e.x = 2;
 e.y = 3;

 Entity e1;
 e1.x = 5;
 e1.y = 8;
 e.Print();
 e1.Print();
}

无法解析外部符号,所以,我们需要在某个地方定义那些静态变量。

例3
struct Entity
{
 static int x, y;

 void Print()
 {
  std::cout << x << ", " << y << std::endl;
 }

};
int Entity::x;
int Entity::y;
int main()
{
 Entity e;
 e.x = 2;
 e.y = 3;

 Entity e1;
 e1.x = 5;
 e1.y = 8;
 e.Print();
 e1.Print();
}

得到
5, 8
5, 8
其实main函数里的代码本质上是这样的:

Entity::x = 2;
Entity::y = 3;

 Entity e1;
Entity::x = 5;
Entity::y = 8;
 Entity::Print();
Entity::Print();
  • 在名为Entiy的命名空间中创建了两个变量,实际上它们并不属于类;
  • 适合于跨类使用变量
  • 静态方法。相当于重复打印了两次,并且根本不需要类实例,因为所有的操作都是静态的
例4

静态方法引用非静态变量:

struct Entity
{
  int x, y;

 static void Print()
 {
  std::cout << x << ", " << y << std::endl;
 }

};
int main()
{
 Entity e;
 e.x = 2;
 e.y = 3;

 Entity e1;
 e1.x = 5;
 e1.y = 8;
 Entity::Print();
 Entity::Print();
}

报错:对非静态变量的非法引用。
实际上对于非静态方法,都会获取当前类的一个实例作为参数,以下是非静态方法在编译时的真实样子:

static void Print(Entity e)
 {
  std::cout << e.x << ", " << e.y << std::endl;
 }

而对于静态方法,没有类实例,即静态方法并没有获取当前实例的隐藏参数:

static void Print()
 {
  std::cout << x << ", " << y << std::endl;
 }

为什么用static变量

想要让某些信息在所有的Entity实例中共享数据。

C++中的局部静态(Local Static)

在局部作用域, 使用static来声明一个变量。

声明一个变量,需要考虑两个情况

  1. 变量的生存期: 变量实际存在的时间,在它被删除之前,它会在内存存在多久
  2. 变量的作用域: 可以访问变量的范围;如果在一个函数内部声明一个变量,我们不能在其他函数中访问它,因为我们声明的变量对于声明的函数是局部的;

静态局部变量

  • 允许我们声明一个变量,它的生存期基本相当于整个程序的生存期;然而它的作用域被限制在这个函数内
  • 可以在任何作用域声明它,不仅仅局限在函数内部。
  • 函数作用域的static和类作用域的static没有区别,这是因为生存期是一样的,唯一区别是,在类作用域中,类中的任何东西都可以访问这个静态变量。
例5
void Function()
{
 static int i = 0;

}

变量i会被初始化为0, 并且在后续对函数的调用中,并不会创建新的变量.

例6
void Function()
{
 static int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
 Function();
 Function();
 Function();
 Function();
 Function();
}
  • 如果没有static,则每次调用函数Function都会创建新变量i,+1,每次都会输出打印1。
  • 正是有static, 不会创建新的变量i,所以每次都会累积,调用2次,则会输出打印2。
例7
static int i = 0;
void Function()
{

i++;
std::cout << i << std::endl;
}
int main()
{
 Function();
i= 10Function();
 Function();
 Function();
 Function();
}
  • 将i定义在全局作用域,可以在main函数里面修改
void Function()
{
static int i = 0;
i++;
std::cout << i << std::endl;
}
int main()
{
 Function();
i= 10Function();
 Function();
 Function();
 Function();
}
  • 定义在函数内部的话,则不能修改i。
  • 如果想每次调用函数的时候,静态变量i都能加1,但是又不希望所有人都可以访问这个变量,则可以将其定义成静态局部变量,只限在函数内部使用

单例类

只存在一个实例的类

class Singleton
{
private:
 static Singleton* s_Instance;  // 创建静态的单例实例
public:
 static Singleton& Get(){ return *s_Instance; }

 void Hello(){}
};

Singleton* Singleton::s_Instance = nullptr;  // 实例的外部定义

int main()
{
 Singleton::Get().Hello();
}
  • 实例s_Instance在初始化的时候就被创建了,并且在后续的创建实例中都还是这个实例。
  • Get()方法是用来创建Singleton类的实例,并且始终返回初始化的那个实例。
class Singleton
{

public:
 static Singleton& Get(){ 
static Singleton* s_instance;
return *s_Instance; 
}

 void Hello(){}
};
int main()
{
 Singleton::Get().Hello();
}

初始化的时候就创建了一个s_Instance, 在后续的类实例创建中,总是会返回这个s_Instance, 所以整个生命期这个类的实例就只有这个,所以叫单实例.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值