Static关键字总结
static关键字主要起的作用:
- 限定作用域
- 变量持久化
- 默认初始化为0(static变量)
前言
static关键字用法体现在两个方面:
- C中面向过程,主要包括static全局变量、static局部变量和static函数
- C++中面向对象,主要包括static成员变量和static成员函数
额外知识:程序的存储模型
- 堆区:是由程序员手动申请(new)与释放(delete)的内存区域。从低地址向高地址申请;内存空间大、存储地址不连续,一般是链式的;速度较慢。
- 栈区:由编译器自动分配和释放,主要存储 函数的参数值、函数内部的变量的值、函数调用的空间。从高地址向低地址申请;容量有限;速度较快;存储地址连续,会溢出。
- 代码区:又叫文本段(.text),存放着程序的机器代码,可执行指令就是存储在这里的,这里的代码是只读的。
- 全局区(静态区):全局变量和静态变量是存储在这里的。初始化的全局变量和静态变量在一块区域(.data),未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(.bbs)。系统结束后由系统释放。
- 常量区:常量字符串放在这里,程序结束后,由系统进行释放。
经过static修饰的变量,存储在内存的全局静态区,只能在本模块的函数引用。
面向过程的static
静态全局变量
static作用在全局变量上,主要起到限定作用域的作用。
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问,如果加了static,就会对其它源文件隐藏,使得作用域限制在本文件内。
- 内存中的位置:静态存储区,在整个程序运行期间一直存在。
- 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
- 作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。
存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。
如果将static去掉,全局变量具有以下特点:
1)全局变量默认是有外部连接性的,其作用域是整个工程,在一个文件内定义的全局变量可以通过包含其所在头文件或显示调用 extern关键字修饰全局变量的变量名声明来引用;
2)静态全局变量是显式调用static修饰的全局变量,其作用域只在声明此变量的文件中,其他文件即使利用extern关键字修饰其声明也不可使用;
静态局部变量
static作用在局部变量上,主要起到保持变量持久化的作用。
通常,在一个函数作用域内定义一个变量,每次运行到该函数时,系统会给局部变量分配内存。当函数结束时,该变量的内存会被系统回收至栈内存当中。
如果作为static局部变量在函数内定义,它的生存期为整个源程序,但是其作用域仍与自动变量相同,只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
- 内存中的位置:静态存储区
- 初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);
- 作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;
基于以上两点可以得出一个结论:把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。
静态函数
static作用在函数上,主要起到限定作用域的作用。
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问,如果加了static,就会对其它源文件隐藏,使得作用域限制在本文件内。
在函数返回类型前加关键字static,函数就定义成静态函数。函数的定义和生命在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用;
特点:
1)作用域只在声明它的文件当中,不能被其他文件引用,其他文件可以定义同名的全局函数;
2)其他文件想要调用本文件的静态函数,需要显示的调用extern关键字修饰其声明;
面向对象的static
静态成员变量
类内用static关键字声明,必须在类外定义,定义时不加satic关键字。初始化时使用作用域运算符来标明所属类。
特点:
1)静态数据成员的服务对象并非是单个类实例化的对象,而是所有类实例化的对象(这点可以用于设计模式中的单例模式实现);
2)静态数据成员必须显式的初始化分配内存,在其包含类没有任何实例化之前已经有内存分配;
3)静态数据成员与其他成员一样,遵从public,protected,private的访问规则;
4)静态数据成员内存存储在全局数据区,只随着进程的消亡而消亡;
优势:
1)静态数据成员不进入程序全局命名空间,不会与其他全局名称的同名同类型变量冲突;
2)静态数据成员可以实现C++的封装特性,由于其遵守类的访问权限规则,所以相比全局变量更加灵活;
静态成员函数
类的成员函数返回类型之前添加static,此成员函数为静态成员函数。实现的时候也不需要static的修饰,因为static是声明性关键字;
特点:
1)在没有实例化的类对象的条件下可以调用类的静态成员函数;
2)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。
3)不能将静态成员函数定义为虚函数,静态成员函数可以继承和覆盖
类的静态函数是该类的范畴内的全局函数,不能访问类的私有成员,只能访问类的静态成员,不需要类的实例即可调用;实际上,他就是增加了类的访问权限的全局函数;
总结
2)可以在头文件中声明静态全局变量,该头文件被多个cpp文件包含后,包含该头文件的cpp文件实际上会各自拥有独立的同名变量;(最好不要这么做)
3)不能将静态成员函数定义为虚函数;
4)静态成员函数没有this指针;
5)static缩短了子类对父类静态成员访问的时间,相对来说节省了内存空间;
6)如果不想在子类中操作父类的静态成员,则可以在子类中定义一个同名的static成员。这样既可覆盖父类中的静态成员,并且根据C++的多态性变量命名规则,这样做是安全的;
warning
在.h里使用static定义,不会进行编译(.h文件不编译),只会在其每个include的cpp文件中包含编译,相当于在.cpp里使用static定义。
不要在头文件中声明static的全局函数
该文件被多个cpp文件包含后,包含该头文件的cpp文件实际会各自拥有独立的同名函数。
不要在cpp内声明非static的全局函数,为什么?(仅在该模块内的函数请用static修饰)
例子:
example.cpp
1 |
|
main.cpp
1 | void publicFunction(); |
结果:example.cpp内的函数被调用。
1 | 编译 |
参考文章:
https://www.cnblogs.com/jhmu0613/p/7131997.html
https://www.cnblogs.com/biyeymyhjob/archive/2012/07/19/2598815.html