C++:static关键字——修饰变量、函数以及作为类成员详解

static修饰变量

  1. 如果 静态变量在函数外声明:
static int x = 10;
int fun() {}
int main() {}

这种变量在内存的 .data区,但作用域和可见性为本文件,即链接性为内部的,其他文件不可访问。

  1. 如果静态变量在函数内:
int fun(int a)
{
	static int x = a;
	x = a;
	return x;
}

对于这种情况,静态变量x 的作用域为fun()函数,但它仍然存储在.data区,生存期延长至程序结束。函数内第一条语句,是对x的初始化,不管函数执行多少次,初始化只有一次。

  1. 如果在一个文件中定义了一个全局变量,其具有外部链接性,在第二个文件中定义了一个同名的静态变量:
//file 1
int num = 10;
___________________________
//file 2
static int num = 20;
void fun()
{
	cout << num << endl;
}

此时,静态变量隐藏常规的外部变量,所以会打印 静态的变量。

用字面常量和变量初始化静态变量的区别(重点)

  1. 以字面常量初始化静态变量
void funa()
{
	static int x = 10;
	int* ip = &x;
}
int main(void)
{
	funa();
	return 0;
}

通过反汇编查看,并没有发现 static int x = 10这条语句的执行
在这里插入图片描述

对于这种情况,编译器在编译时,会发现funa()函数中有一个拿字面常量初始化的静态变量,那么编译器会直接把这条语句删掉,把静态变量 x 记录起来并初始化为 10;在程序执行到进入主函数时,会为这个静态变量 x 在数据区创建空间。

图示
在这里插入图片描述

  1. 以变量初始化静态变量
void funb(int a)
{
	static int x = a;
}
int main(void)
{
	funb(10);
	funb(20);
	return 0;
}

这种模式的初始化流程为:

  1. 编译器会直接给静态变量 x 在数据区开辟空间,这个空间会有一个标记位置,标记位初始化为0。
  2. 在进入主函数后,调用 funb(10); 这时候会用10来给 静态变量x 初始化,初始化完成后,标记位变为1。
  3. 再调用 funb(20)的时候,会发现 x的标记位已经变成 1 ,则就不能再进行初始化。

反汇编与内存查看:

在执行到静态变量这条语句时,可以发现反汇编有大量的语句,其中就有检查标记位

在这里插入图片描述在这里插入图片描述再次执行第二个funb()函数时,会检查标记位为 1后,就会跳过赋值步骤。

图示:

在这里插入图片描述

第二种相对于第一种情况,在多线程模式下并不安全,因为 两个函数funb()会同时竞争 静态变量x和标记位 flag 的资源,导致 x 不止被初始化一次。

static修饰函数

如果将函数定义为静态,和全局的静态变量一样,其可见性为本文件有效,同一个工程的其他文件不可用。
错误示例:

//file1
static int fun()
{
	//...
}
________________________
//file 2
int test()
{
	int x = fun(); //error
}

static在类中的使用

类的静态变量

设计一个静态变量作为类成员。
示例代码

class Object
{
private:
	int value;
	static int num;
public:
	Object(int x = 0) : value(x)
	{
		num += 1;
	}
	~Object()
	{
		num -= 1;
	}

	Object(const Object& obj) : value(obj.value)
	{
		num += 1;
	}
};
int Object::num = 0;

‘注意点:

  • 类的静态变量必须在类外初始化
  • 类的静态变量,所有该类的对象共用一份

示例:

int main(void)
{
	Object obja(10);
	Object objb(20);

	return 0;
}

在这里插入图片描述

根据监视器可以看出:

  • 两个对象的普通变量都是自己的,而静态变量num共用一份。
  • 在对象中并没有看到静态变量属于谁,这就意味着,无法通过this指针来约束静态变量。

示例:在刚才类的基础上加上一个常方法

void Print() const
{
	num += 10;
	cout << "value = " << this->value << "num = " << this->num << endl;
}
int main(void)
{
	Object obja(10);
	obja.Print();
	Object objb(20);
	objb.Print();
	return 0;
}

这样是可以编译通过的,可以看出对象模型里没有 num,所以即使使用const限制this指针,还是可以对 静态变量修改。
在这里插入图片描述

类的静态函数

示例:如果把非静态函数Print()的内容写成静态函数,那么还能编译通过吗?

static void Show()
{
	num += 10;
	cout << this->value << endl;
	cout << num << endl;
}

注意
静态函数和非静态函数最大的区别就是:
非静态函数有this指针,而静态函数没有this指针,所以若要输出 value,编译器会不知道输出谁的value,编译出错。
在这里插入图片描述

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_索伦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值