因为最近要保研和找实习,所以重新复习了一下C++的知识,static的用法很丰富,就想整理一下。
一、用在局部作用域
首先回顾一下作用域的概念:
作用域是指一个标识符在程序正文中有效的区域。对于函数体内的变量,作用域从声明处起到所在块结束的大括号为止,对于函数的形参,从声明处起到函数体结束为止。
在局部作用域中,如果一个对象声明为static,即在对象的声明前加上“static”关键字,则该对象具有静态生存期,这个对象不会随着多次函数调用而产生一个副本,也不会因为函数的返回而失效。
由于函数返回之后,在函数外部实际上无法访问static的对象,所以static的函数用法多出现在递归的函数中。
int factorial(int n){
static int result=1;
if(n==0){
return result;
}else{
return n * factorial(n-1);
}
}
比如上面的函数,result变量为static,在多次调用中只会初始化第一次,这里初始化为1。需要注意的是,动态基本类型变量未初始化会被赋予随机值,静态基本类型变量则默认初值为0。
二、用在全局作用域
在全局作用域中的变量,在整个文件中都有效。但是不加static的变量和函数,则默认是extern变量,在该模块以外的所有模块中都是可见的。这带来的问题是,不同人开发的模块很可能出现变量重名,当多文件工程很大需要引用多个模块时更是如此,所以我们需要隐藏一些不必要外部可见的变量。
static正是提供了这一功能。被static修饰的变量或函数,无法被其他编译单元引用。
在ISO C++ 2.0标准中不再鼓励这种用法,如果想要隐藏该模块的变量和函数不为外部可见,鼓励使用匿名命名空间的方式。由于每个源文件的匿名空间是不同的,所以在一个源文件中没有办法访问其他源文件的匿名命名空间。
以下为一个多文件项目:
a.cpp
#ifndef UNTITLED_A_H
#define UNTITLED_A_H
void func1();
static void func2();
#endif //UNTITLED_A_H
a.cpp
#include <iostream>
#include "a.h"
using namespace std;
void func1(){
cout << "func1" << endl;
}
static void func2(){
cout << "func2" << endl;
}
main.cpp
#include <iostream>
#include "a.h"
using namespace std;
int main() {
func1();
func2();
return 0;
}
这里func1()是外部可见的,func2()是外部不可见的,因此会提示如下错误:
warning: 'void func2()' used but never defined
三、用在类作用域
这里分为静态数据成员和静态函数成员。
1. 静态数据成员
如果一个类中的数据成员用static修饰,该成员不属于任何一个对象独有,而是整个类所共有。该变量将具有静态生存期,而且可以不通过对象,而是通过类的方式“类名::标识符”直接访问。
2. 静态函数成员
同理,如果一个函数用static修饰,则该函数为整个类所共有的函数,可以直接通过“类名::函数名(参数)”来调用。当然通过对象名调用也是可以的,等价于通过类名来调用。
下面举个例子来说明两种静态成员。
#include <iostream>
using namespace std;
class A{
public:
A(){
cout << "constructor"<<endl;
count ++;
}
A(A &a){
cout << "copy constructor"<<endl;
count ++;
}
static int getcount(){
return count;
}
private:
static int count;
};
int A::count = 0;
int main() {
cout << A::getcount()<<endl;
A a;
A b = a;
cout << A::getcount()<<endl;
return 0;
}
这里count为static变量,这里注意c++的类静态数据成员变量必须在类外初始化,即使该变量为private类型。getcount是一个类的静态函数成员,可以不依赖类的对象调用。
在主函数中,还没有一个对象存在时,getcount函数就可以返回count值,此时为初始化的0。当声明了两个对象之后,getcount的值将会变成2。
运行结果:
0
constructor
copy constructor
2
四、static变量的存放位置
对于c++来讲,编译运行程序占用内存大体分为数据段和代码段,数据段又可分为:全局存储区,堆区,栈区,常量存储区。
函数存放在代码段中,static修饰的变量和全局变量都放在全局存储区中。全局存储区又可分为初始化的数据段(.data)和未初始化的数据段(.bss)。如果static变量可以在程序运行前确定初值,将放入.data中,如果不能,比如static变量是通过执行构造函数赋予初值,则无法在运行前确定初值,则放入未初始化的数据段.bss中。