c++中的静态类型 static
static根据上下文语意有两种含义,一种是在类和结构体内,另一种时类在结构体外。
类外的static在链接阶段是局部的,它只对它的编译单元(.obj)可见,而类内的static表示这个变量将在类内与所有实例共享
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中的static去掉,直接变成int声明变量s_Variable,那么在链接时会报错,因为s_Variable已经在另一个编译单元中被定义了,两个全局变量的名字不能一样,改正方法是使用引用
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的意思是在另外的编译单元中寻找定义,也叫外部链接,此时运行可以看到打印结果是5。加上static有些类似于在类中声明私有类型成员,其他的编译单元(.obj)不能访问s_Variable,函数也是一样。
Static.cpp
void function() {}
main.cpp
#include<iostream> int function() {} int main() { std::cin.get(); }
此时编译会发生错误,因为function被重复定义,如果将Static.cpp中的function前面加上static,那么链接将不会出错。
在头文件中使用静态变量也是同样的道理,头文件相当于在引用头文件的位置将头文件的内容复制粘贴,因此头文件中使用static定义的静态变量即使在两个不同的cpp文件中被调用,也不会引起重复定义,因为它相当于在两个cpp文件中各自建立一个静态变量,互不干扰。
因此尽量让全局变量和函数变成静态类型,除非要在别的cpp文件中调用它
static在类和结构内代表什么
表示类内所有同名变量都代表一个实体,如果这个尸体的值发生了改变,那么类中所有的这个实体都同样会改变。
#include<iostream> 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(); std::cin.get(); }
打印结果是2,3 5,8
#include<iostream> 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(); std::cin.get(); }
此时的打印结果是两个5,8,需要注意的是静态变量无法访问非静态变量,如果代码做如下修改,那么静态函数Print将无法访问非静态的变量x和y
#include<iostream> 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(); std::cin.get(); }
使用访问命名空间的方法来访问类/结构体中的静态成员是因为静态类型是唯一的,无需通过新建类/结构体来实现访问。如果做以下修改,可以正常运行
#include<iostream> struct Entity{ int x,y; static void Print() { std::cout<<x<<","<<y<<std::endl; } }; static void Print(Entity e) { std::cout<<e.x<<","<<e.y<<std::endl; } int main() { Entity e; e.x=2; e.y=3; Entity e1; e1.x=5; e1.y=8; Print(e); std::cin.get(); }
本地作用域中(local scope)的静态变量
需要掌握的变量的生命周期(变量在被删除之前在内存中存储多久)和作用域(在哪里可以访问到这个变量)
局部静态变量允许我们定义一个生命周期是整个程序的变量,但是他的作用域被限制在当前函数中,其实也不一定是函数,可以在任何作用域中声明静态变量,函数中的静态变量和类中的静态变量其实没有很大的区别,他们的生命周期是一样的,唯一区别是类中的静态变量可以被类内的任何变量访问,在函数作用域中声明的静态变量,对于函数来说是局部的,就像类的静态变量对于类来说也是局部的。如果在函数中声明一个静态变量,那么在第一次调用函数时,这个静态变量被创建,后续再次调用此函数时将不会在创建这个变量。
#include<iostream> void func() { int i=0; i++; std::cout<<i<<std::endl; } int main() { func(); func(); func(); std::cin.get(); }
此函数的输出结果是三个1,如果将int i=0改为静态变量,那么结果将输出1,2,3
#include<iostream> void func() { static int i=0; i++; std::cout<<i<<std::endl; } int main() { func(); func(); func(); std::cin.get(); }
将i设在函数外面作为全局变量也是一样的结果,但是当i作为全局变量时,就意味着我在任何地方都可以调用它,所以会有一些意外的发生。
#include<iostream> static int i=0; void func() { i++; std::cout<<i<<std::endl; } int main() { func(); i=10; func(); func(); std::cin.get(); }
此时输出结果是1,11,12,如果不想让程序出现这种效果,此时可以将i变成局部静态变量
同样,局部静态变量可以简化代码
#include<iostream> class Singleton{ private: static Singleton* s_Instance; public: static Singleton& get() {return s_Instance;} void Hello() {} }; Singleton *Singleton::s_Instance=NULL; int main() { Singleton::Get().Hello(); std::cin.get(); }
简化如下
#include<iostream> class Singleton{ public: static Singleton& get() { static Singleton instance; return instance; } void Hello() {} }; int main() { Singleton::Get().Hello(); std::cin.get(); }
如果Singleton instance前面没有static,因为Singleton是在堆栈上建立的,在运行到结束花括号时被销毁,函数结束,在返回引用时这将会是个严重的错误,如果将表示引用的“&”去掉将不会有错,加上了static将会大大延长了instance的生命周期,每次调用get()的时候后面的调用都会返回到第一次构造的Singleton实例。