static关键字小结
1. C语言中的static
C语言中的static关键字,可以用来修饰全局变量、局部变量、函数。下面简单讨论一下static在C语言中的作用
1.1 修饰全局变量
所有全局变量存储在静态存储区中,默认情况下是外部的,在一个翻译单元(源文件)中定义的全局变量默认情况下可以被其它源文件引用,只需要在引用的时候加上extern关键字扩展其作用域。使用static修饰全局变量,可以将全局变量的作用域限制在本翻译单元中,使该变量不能够被其他翻译单元引用。
1.2 修饰局部变量
局部变量定义在函数内部或者代码块中。普通局部变量是存储在栈中的,在每次函数调用的时候为局部变量分配存储空间,离开其作用域后,变量所占用的存储空间会被自动回收。使用static修饰的局部变量,是存储在静态存储区中的,其在程序整个生命周期一直存在。static局部变量只被初始化一次,当第一次执行到static局部变量的定义语句的时候。
1.3 修饰函数
默认情况下,函数是外部的,全局可见,在一个翻译单元中定义的函数可以被其他翻译单元引用,只需要在调用之前使用extern对其进行声明,扩展其作用域。使用static修饰函数,可以将函数的作用域限制在本翻译单元中,使其成为内部函数,因此其他翻译单元就不能使用extern来扩展该函数的作用域!这样带来一个好处,即不同翻译单元中可以采用同样的函数名,而不会引发函数重定义错误。
2. C++中的static
C++同样拥有static关键字,和C语言中的static一样,可以修饰全局变量、局部变量、普通函数(区别与类成员函数),作用效果也与之相似,只是C++中可以重载全局函数,因此为全局函数加上static修饰符,可以在不同翻译单元中定义同名同参数列函数,而不会引发函数重定义错误。除此之外,C++还可以使用static修饰类的数据成员以及成员函数。
2.1 修饰类数据成员
static可以修饰类的数据成员,先来看一个例子
// Test.h
class Test{
private:
static int value; // 静态成员变量
static const int ci = 1; // static const整型成员,可以在类内定义并初始化
};
// Test.cpp
int Test::value = 1; // 在类定义体外部进行定义,建议在源文件进行定义,而不在头文件中,这样可以防止重复定义
有几点值得注意的:
- 类的静态数据成员属于整个类,而非属于某个类对象。其存放在静态数据段中,为所有该类实例所共享。
- 类的静态数据成员必须在类定义体外进行初始化,
除了static const整型成员(包括int、short、char、long等)可以在类定义体内部进行定义并初始化。
- 在类定义体外定义静态数据成员,不能加上static,以和全局静态变量区分开来。
类的静态数据成员会在执行main函数之前进行初始化
,无论该类是否被使用过,但是不同翻译单元的静态数据成员初始化顺序未定,因此不能依赖假定初始化顺序进行编程。
2.2 修饰类成员函数
class Utility{
public:
void print()
{
cout << "This is a utility class" << endl;
}
static int max(int a, int b)
{
return a > b ? a : b;
}
};
int main(void)
{
int maxVal = Utility::max(1, 2);
Utility u;
maxVal = u.max(2, 3); // 被编译器转换为Utility::max(2, 3);
u.print();
return 0;
}
- 类的静态成员函数同样不属于类对象,因此它
没有隐含的this指针
。 - 类的静态成员函数可以使用类的static数据成员,但是
不能使用类的non-static数据成员
,因为non-static数据成员属于类对象,需要通过this指针来确定non-static数据成员属于哪个对象的。 类的静态成员函数的地址与C语言函数的地址兼容
。- 不同于类的非静态成员函数,
类的静态成员函数不能有为const、volatile、virtual限定
。
2.3 为什么类的静态成员函数不能有const、volatile、virtual限定?
- const
根据C++的规则,const修饰符用于保证函数不修改成员变量的值,该函数必须是含有this指针的类成员函数;而类中的static函数本质上是全局函数,不含this指针,并且访问的值是其参数、静态数据成员和全局变量,而这些数据都不是对象状态的一部分,因此谈不上“保证函数不修改成员变量的值” - virtual
由于虚函数的运行机制是通过vptr实现的,而编译器会在含有虚函数的类对象的内存空间中安插一个隐含的成员——vptr指针,要访问vptr,还是需要通过this指针!但static成员函数不包含this指针,因此无法实现虚函数的调用!
3. 小结
- static修饰全局变量,改变变量的作用域,而不改变变量的存储位置和生命周期
- static修饰局部变量,改变变量的存储位置和生命周期,但不改变变量的作用域
- static修饰全局函数,将函数作用域限制在当前文件中
- static可以修饰类成员,使其成为类所有的,而不是对象所有的