static关键字以及类的静态成员变量和静态成员函数
static关键字声明的变量位于程序内存空间中的全局数据存储空间,所以它存在于程序整个运行期间。
static关键字用来控制变量的持续周期以及可见性,还可以唯一化类的公用成员变量,减少开销。
1.在多个文件中隐藏全局访问性,实现多个文件的全局变量可重复命名。
/*static.cpp文件*/
int a = 'no static';
void print()
{
cout << "no static" << endl;
}
/*main.cpp文件*/
int
main()
{
extern int a;
cout << a << endl;
void print();
print();
return 0;
}
联合编译这两个文件(g++ static.cpp mian.cpp -o main.out),结果显示可以跨文件访问 extern声明的变量,以及跨文件访问函数。
/*static.cpp文件*/
static int a = 'no static';
static void
print()
{
cout << "no static" << endl;
}
/*main.cpp文件*/
int
main()
{
extern int a;
cout << a << endl;
void print();
print();
return 0;
}
会出现编译错误,显示在main.cpp文件内对变量a和函数print()未定义。
2.实现在程序整个运行期间存在
static可以控制变量的可见范围,全局变量则不行。
但如果将函数的局部变量声明为static,它仍然能在整个程序周期存在,但作用域和局部变量一致,在函数退出后,不能再访问它,即使它还存在。并且重复调用该函数,该局部变量不会被重新初始化。
int
print()
{
/*重复调用此函数,a不会被重复初始化*/
static int a = 0;
return a++;
}
int a = 0;
int
main()
{
for (; a < 10; a++)
{
cout << a << print() << endl;
}
return 0;
}
上例的输出结果为
00
11
22
33
44
55
66
77
88
99
3.static声明的变量和全局变量默认初始化为0
4.static声明静态成员变量
按照static关键字的特性,静态成员变量被分配到全局数据内存区,同全局变量一致,在编译时就被分配内存。所以可以在对象创建之前就可以访问静态成员变量,并且它不会随对象的销毁而释放。
类只是描述对象的结构和功能,在实例化类之前,都不占用内存,所以通过类内部的普通成员函数和构造函数都无法初始化静态成员变量。因为static成员变量是属于类的成员,而不是属于对象的成员。所以它是该类的所有对象共有的,它的值对于所有对象都一致。
- 静态成员变量内存位置在全局变量区,在对象中并不占用存储空间
- 静态成员变量是属于类的成员,而不是属于对象的成员,所以静态数据成员不能在类的内部初始化。类只有实例化为对象才会分配内存,而静态变量在程序编译时就会分配内存,所以需要在类的外部像全局变量那样定义并初始化静态成员变量。
1)类中的普通成员函数或者构造函数无法初始化静态成员变量,会报错“未定义的引用”
class test
{
private:
static int a;
public:
/*以下初始化是非法的*/
void init()
{
a = 1;
}
};
2)静态成员变量只能在类的外部初始化。初始化格式为:
数据类型 类名::静态成员变量名 = 初值
class test
{
private:
static int a;
};
int test::a = 1;
3)在对象创建之前,就可以通过类名加作用域访问符访问静态成员变量。静态成员变量并不会打破面向对象的封装。仍然服从public, private, protected的访问限制
class test
{
public:
static int a;
};
int test::a = 1;
int
main()
{
cout << test::a << endl;
return 0;
}
5.静态成员函数
static声明的成员函数为类的静态成员函数。普通的成员函数在传递参数时编译器会默认传递一个this指针,用以在多个对象时指明该函数来自于哪个对象。但是在静态成员函数中没有this指针。
- 由于静态成员函数没有this指针,因为当有多个对象存在时,无法判断是来自于哪一个对象,对于变量的访问会产生歧义。静态成员函数与静态成员变量一样,在程序编译时就分配内存空间。而普通成员变量在实例化之后才会分配空间。所以静态成员函数无法访问类中的普通成员变量。
class test
{
private:
int b;
public:
static void init()
{
b = 1;
}
};
在编译时会报错。
- 在实例为对象之前,可以直接通过类加上作用域访问符调用静态成员函数
class test
{
public:
static void init()
{
cout << "未创建任何对象" << endl;
}
};
int
main()
{
/*可以直接通过类调用静态成员函数*/
test::init();
return 0;
}
- 静态成员函数可以访问类的静态成员变量以及静态成员函数。对于非静态成员变量的访问,只能通过实例化对象通过参数传递
class test
{
public:
static int a;
static void init(int x)
{
/*可以直接访问静态成员变量*/
cout << a << endl;
/*可以直接访问静态成员函数*/
print();
cout << x << endl;
}
static void print()
{
cout << 2 << endl;
}
int b = 3;
};
int test::a = 1;
int
main
{
test test1;
/*可通过对象传参访问非静态成员变量*/
test::init(test1.b);
return 0;
}
/*
输出结果为
1
2
3
*/
- 对于静态成员函数的定义,可以在类内部,也可以在类外部通过作用域访问符,但是在类外部定义时,如初始化静态成员变量一样,需要去掉static关键字。
- 注意:静态成员函数虽然生存周期大于对象,但是仍然不能初始化静态成员变量。静态成员变量的初始化只能在类外部。