static 关键字
下面来自维基的解释:https://zh.wikipedia.org/zh-cn/%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F
在C语言及由其派生出的C++与Objective-C等程序语言中,“static”是用于控制变量的生命周期和连接方式(即其作用域,亦即可见性)的保留字。确切来说,正如C族语言中的extern,auto与register这些保留字一样,static也是一种存储类(此处的“类”与面向对象语言的“类”的定义不同)标识。每个变量与函数都有以上的一种存储类标识,如果在声明中没有明确标识其存储类,编译时就会根据上下文来选择其默认存储类,如在源文件里的所有文件级变量对应的默认存储类是extern,而在函数体内的变量对应的则是auto,各存储类的属性如下表所列。
存储类名 | 生命周期 | 作用域 |
---|---|---|
extern | 静态(程序结束后释放) | 外部(整个程序) |
static | 静态(程序结束后释放) | 内部(仅翻译单元,一般指单个源文件) |
auto,register | 函数调用(调用结束后释放) | 无 |
static影响变量的生命周期和连接方式
静态全局变量和非静态全局变量
静态变量在内存中布局
用户进程部分分段存储内容如下表所示(按地址递减顺序):
名称 | 存储内容 |
栈 | 局部变量、函数参数、返回地址等 |
堆 | 动态分配的内存 |
BSS段 | 未初始化或初值为0的全局变量和静态局部变量 |
数据段 | 已初始化且初值非0的全局变量和静态局部变量 |
代码段 | 可执行代码、字符串字面值、只读变量 |
静态成员变量
有些成员变量的数据类型比较特别,它们的初始化方式也和普通数据类型的成员变量有所不同。这些特殊的类型的成员变量包括:
a.引用
b.常量
c.静态
d.静态常量(整型)
e.静态常量(非整型)
常量和引用,必须通过参数列表进行初始化。
静态成员变量的初始化也颇有点特别,是在类外初始化且不能再带有static关键字,其本质见文末。
参考下面的代码以及其中注释:
#include <iostream>
using namespace std;
class BClass
{
public:
BClass() : i(1), ci(2), ri(i){} // 对于常量型成员变量和引用型成员变量,必须通过参数化列表的方式进行初始化
//普通成员变量也可以放在函数体里,但是本质其实已不是初始化,而是一种普通的运算操作-->赋值运算,效率也低
private:
int i; // 普通成员变量
const int ci; // 常量成员变量
int &ri; // 引用成员变量
static int si; // 静态成员变量
//static int si2 = 100; // error: 只有静态常量成员变量,才可以这样初始化
static const int csi; // 静态常量成员变量
static const int csi2 = 100; // 静态常量成员变量的初始化(Integral type) (1)
static const double csd; // 静态常量成员变量(non-Integral type)
//static const double csd2 = 99.9; // error: 只有静态常量整型数据成员才可以在类中初始化
};
//注意下面三行:不能再带有static
int BClass::si = 0; // 静态成员变量的初始化(Integral type)
const int BClass::csi = 1; // 静态常量成员变量的初始化(Integral type)
const double BClass::csd = 99.9; // 静态常量成员变量的初始化(non-Integral type)
// 在初始化(1)中的csi2时,根据著名大师Stanley B.Lippman的说法下面这行是必须的。
// 但在VC2003中如果有下面一行将会产生错误,而在VC2005中,下面这行则可有可无,这个和编译器有关。
const int BClass::csi2;
int main()
{
BClass b;
return 0;
}
---------------------------------------------------------------------------------------------
静态成员属于类作用域,但不属于类对象,和普通的static变量一样,程序一运行就分配内存并初始化,生命周期和程序一致。
所以,在类的构造函数里初始化static变量显然是不合理的。
静态成员其实和全局变量地位是一样的,只不过编译器把它的使用限制在类作用域内(不是类对象,它不属于类对象成员),要在类的定义外(不是类作用域外)初始化。
静态成员函数
我们知道,当调用一个对象的成员函数(非静态成员函数)时,系统会把当前对象的起始地址赋给 this 指针。而静态成员函数并不属于某一对象,它与任何对象都无关,因此静态成员函数没有 this 指针。既然它没有指向某一对象,就无法对该对象中的非静态成员进行访问。
可以说,静态成员函数与非静态成员函数的根本区别是:非静态成员函数有 this 指针,而静态成员函数没有 this 指针。由此决定了静态成员函数不能访问本类中的非静态成员。
静态成员函数可以直接引用本类中的静态数据成员,因为静态成员同样是属于类的,可以直接引用。在C++程序中,静态成员函数主要用来访问静态数据成员,而不访问非静态成员。
如果要在类外调用 public 属性的静态成员函数,要用类名和域解析符“::”。如:
Student::getNum();当然也可以通过对象名调用静态成员函数,如:
stu.getNum();
静态成员函数能用const修饰吗?不能!
我们可以用static修饰一个类的成员函数,也可以用const修饰类的成员函数(写在函数的最后表示不能修改成员变量,不是指写在前面表示返回值为常量)。能不能同时用static和const修饰类的称冠函数?
不能。C++编译器在实现const成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时static的用法和static是冲突的。
Static的作用是表示该函数只那个在雷辛格的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。
静态成员函数能是虚函数吗?不能!
静态成员函数能不能同时也是虚函数?
不能,调用静态成员函数不要实例。但是调用虚函数需要从一个实例中指向虚函数表的指针以得到函数的地址,因此调用虚函数需要一个实例。
const成员函数能否改变静态成员变量? 能!
也能调用静态成员函数
Effective C++ 条款4
https://zh.wikipedia.org/zh-cn/%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F