C/C++之static

239 阅读4分钟

前言,这篇文章是讲解C++中static关键字的用法以及需要注意的地方。那么,static关键词的作用是什么呢?作用是控制变量的存储方式和作用范围,还会影响存储的声明周期和链接特性。

static修饰函数的局部变量,变量只在第一次调用是初始化一次,下次调用该函数不会再次初始化static修饰的变量,这个变量的声明周期是整个程序运行期间,作用域只作用于函数内

#include <iostream>
using namespace std;

#define N 99

void test(){
	static int i = 0;
	cout << i++ << endl;
}

int main()
{
	for(int i = 0; i < N; ++i){
		test();	
	}
   return 0;
}

static修饰成员变量,需要在类外面初始化,修饰成员函数,static修饰的成员变量和成员函数在类的所有实例中是共享的,不依赖类的实例

#include <iostream>
using namespace std;

#define N 9
class MyClass{
public:
	static void Test(){
		cout << "调用Test()函数" << endl;
		cout << MyClass::i++ << endl;
	}
	void PrintValue(){
		cout << "调用PrintValue()函数" << endl;
		cout << MyClass::i++ << endl;
	}
	
private:
	static int i;
};

int MyClass::i = 0;

int main(){
	MyClass myclass_1;
	MyClass myclass_2;
	for(int i = 0; i < N; ++i){
		if(i % 3 == 0){
			myclass_1.PrintValue();
		}else if(i % 3 == 1){
			myclass_2.PrintValue();	
		}else{
			MyClass::Test();	
		}
	}
   return 0;
}

需要注意静态成员函数没有this指针,不能访问任何非静态成员,因为static修饰的没有与特定的对象实例绑定

#include <iostream>
using namespace std;

class MyClass{
public:
	static void Test(){
		cout << "调用Test()函数" << endl;
		i = 0;
	}
	
private:
	int i;
};

int main(){
	MyClass::Test();

   return 0;
}

上面的代码中,静态成员函数访问非静态成员变量,这是不允许的,编译会报错

所以静态成员函数时里面也不能调用非静态成员函数

#include <iostream>
using namespace std;

#define N 9
class MyClass{
public:
	static void Test(){
		cout << "调用Test()函数" << endl;
		//cout << MyClass::i++ << endl;
		PrintValue();
	}
	void PrintValue(){
		cout << "调用PrintValue()函数" << endl;
		cout << MyClass::i++ << endl;
	}
	
private:
	static int i;
};

int MyClass::i = 0;

int main()
   return 0;
}

这样是不允许的,编译时就报错

static修饰成员变量,并且在多线程的情况下,需要注意变量竞态问题,比如下面的这个代码就是有问题的

#include <iostream>
#include <thread>
using namespace std;
const int N = 1000000;

class Counter {
public:
	static int count;
	static void Increment() {
		++count;
	}
	static void PrintValue() {
		cout << "count = " << count << endl;
	}
};

int Counter::count = 0;

void threadFun() {
	for (int i = 0; i < N; ++i) {
		Counter::Increment();
	}
}

int main() {
	thread t_1(threadFun);
	thread t_2(threadFun);

	t_1.join();
	t_2.join();

	Counter::PrintValue();

	return 0;

运行程序发现,结果并没有等于N的2倍,那是因为多线程竞态问题,解决方法是在每次修改静态成员变量时需要加锁,修正的代码如下

#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
const int N = 1000000;


class Counter {
public:
	static int count;
	static mutex mtx;		//声明一个静态互斥锁
	static void Increment() {
		lock_guard<mutex> lock(mtx);
		++count;
	}
	static void PrintValue() {
		cout << "count = " << count << endl;
	}
};

int Counter::count = 0;
mutex Counter::mtx; //定义mtx

void threadFun() {
	for (int i = 0; i < N; ++i) {
		Counter::Increment();
	}
}

int main() {
	thread t_1(threadFun);
	thread t_2(threadFun);

	t_1.join();
	t_2.join();

	Counter::PrintValue();

	return 0;
}

还有需要注意的是,static修饰的全局变量的作用域限制在当前文件中,其他文件是不能对其访问的,这样可以避免全局变量命名冲突的问题。 static修饰的普通函数也一样

总结

1、static修饰函数的局部变量,变量只在第一次调用是初始化一次,下次调用该函数不会再次初始化static修饰的变量,这个变量的声明周期是整个程序运行期间,作用域只作用于函数内 2、static修饰成员变量,需要在类外面初始化,多线程的场景下需要注意竞态问题,在改变变量时需要加锁,static修饰的成员变量在类的所有实例中是共享的,不依赖类的实例 3、静态成员函数没有this指针,不能访问任何非静态成员,因为static修饰的没有与特定的对象实例绑定 4、static修饰的全局变量的作用域限制在当前文件中,其他文件是不能对其访问的,这样可以避免全局变量命名冲突的问题。static修饰的普通函数也一样。 5、static修饰的变量是存储在静态存储区(数据段