Static是C++中一个常见的关键字,它被用来控制变量的存储方式和可见性。
在类定义中,它的成员(包括数据成员和成员函数)可以用关键字static申明为静态成员。静态成员的特性是不管这个类创建了多少个对象,它其中的静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
从原理来分析,我们能清楚的了解,类的数据成员和成员函数都是跟着类的执行,在编译器上为他在堆栈上分配内存空间存储的。静态数据成员和静态成员函数和他们不同,它们是在程序开始运行时候存储在静态存储空间的。
静态数据成员
在一个类中,如果将一个数据成员申明为static,这种成员就被称为静态数据成员。与一般数据成员不同的是,无论建立多少个类的对象,都只有一个静态数据成员拷贝。
首先,我们通过一个例子来分析一下静态数据成员。
#include "stdafx.h"
#include "stdlib.h" class Student{
static int count; // 申明静态数据成员,统计学生总数
int studentNo; // 普通数据成员,表示学生学号 public:
Student()
{
count++; // 每创建一个对象,学生个数加1
studentNo = count; // 给当前学生学号赋值
} void print()
{
printf("Student%d", studentNo);
printf("count = %d\n", count);
}
};
int Student::count = ; // 初始化静态数据成员count int _tmain(int argc, _TCHAR* argv[])
{
Student Student1; // 创建第一个学生对象
Student1.print();
printf("----------------\n"); Student Student2; // 创建第二个学生对象
Student1.print();
Student2.print();
printf("----------------\n"); Student Student3; // 创建第三个学生对象
Student1.print();
Student2.print();
Student3.print(); system("pause");
return ;
}
上述程序的运行结果如下:
从运行结果可以看出,所有对象的count值都是相同的,这个说明他们都共享这个数据,也就是说明所有对象对于count只有一个拷贝,这就是静态数据成员的特性(作用和全局变量类似)。普通数据成员studentNo各个对象的值是不同的,所以它存放的是各个对象的对象号。
说明:
- 静态数据成员属于类,而不像普通的数据成员那样属于某个对象,因此我们可以用“类名::”这样的形式访问静态数据成员。如:Student::count。
- 静态数据成员不能在类中进行初始化,因为类中不给他分配内存空间(前面说的类中存储地址是堆栈,静态数据成员存储地址是静态存储空间),所以必须在其他地方为他提供定义和初始化。默认时,静态成员被初始化为0。
- 静态数据成员和静态变量一样,是在编译时创建并进行初始化的。它在该类的任何对象创建之前就已经存在。因此,公有的静态数据成员可以在对象定义之前就被访问。对象定以后,公有的静态数据成员也可以通过对象进行访问,格式如下:
对象名.静态数据成员名;
对象名->静态数据成员名;
- 静态数据成员也遵循public,protect,private的原则。所以,私有的静态数据成员不能被类外部函数访问,也不能用对象进行访问。
- 静态数据成员的类型可以是该成员所属的类类型。非静态数据成员被限定为申明其自身类的对象的指针或引用。
class A{
private:
static A a1; // OK
A *a1; // OK
A a3; // Error
};
6. 静态数据成员可以用作默认实参,而普通数据成员不可以。
class Example{
public:
static int a;
int b; void fun1(int i = a); // 正确,a为静态数据成员
void fun2(int i = b); // 错误,b为普通数据成员
};
7. C++支持静态数据成员的一个主要原因是可以不必使用全局变量。依赖于全局变量的类很容易违反面向对象程序的封装原理。静态数据成员的主要用途是定义类的各个对象所公用的数据,如:统计数据、平均数等。
静态成员函数
在类定义中,在成员函数之前加上static申明成为静态成员函数。和静态数据成员一样,静态成员函数属于整个类,是该类的所有对象共享的成员函数,而不是属于某个对象特有的函数。定义静态成员函数的格式如下:
Static 返回类型 函数名(实参表)
与静态数据成员一样,它也遵循public,protect和private的原则。调用公有的静态成员函数格式如下:
类名::静态成员函数(实参表)
对象名.静态成员函数(实参表)
对象名->静态成员函数(实参表)
下面我们通过一个例子来了解静态成员函数。
#include "stdafx.h"
#include "stdlib.h" class small_cat{
private:
int weight;
static int total_weight;
static int totel_num;
public:
small_cat(int w)
{
weight = w;
total_weight += w;
totel_num++;
} void display()
{
printf("The small cat weight %d pounds\n", weight);
} static void total_disp()
{
printf("%d small cat weight ", totel_num);
printf(" %d pounds\n", total_weight);
}
};
int small_cat::total_weight = ;
int small_cat::totel_num = ; int _tmain(int argc, _TCHAR* argv[])
{
small_cat w1(),w2(),w3();
w1.display();
w2.display();
w3.display(); small_cat::total_disp(); system("pause");
return ;
}
程序运行结果:
说明:
- 静态成员函数可以定义成内嵌的,也可以在内外定义,在类外定义是,前面不需要加static。
- 一般情况下,静态成员函数主要是用来访问全局变量或者同一个类中的静态数据成员。
- 私有静态成员函数不能被类的外部函数和对象访问。
- 使用静态成员函数的一个原因就是可以用它在建立任何对象之前处理静态数据成员这是普通成员函数不能实现的。
- 编译系统将静态成员函数限定为内部连接,也就是说,与现行的文件相连接的文件中的同名函数不会与该函数发生冲突,维护了该函数的安全性,这是使用静态成员函数的另外一个原因。
- 在一般的成员函数中都隐藏一个this指针,用来指向对象自身,而在静态成员函数中没有这个this指针,因为它不与特定的对象相关联。调用静态成员函数使用如下格式:类名::静态成员函数名();
- 静态成员函数不能被申明为const,因为static成员不是任何对象的组成部分,毕竟,将成员函数申明为const就是承诺不会修改该函数所属的对象。
- 静态成员函数不能被申明为虚函数。
- 一般而言,静态成员函数不可访问类中的非静态成员。如果确实需要,静态成员函数只能通过对象名(或指向对象名的指针)访问该对象的非静态成员。如:
static void display(small_cat &w)
{
printf("The small cat weight %d pounds\n", w.weight);
}
延伸
静态局部变量
静态局部变量属于静态存储方式,它具有以下特点:
(1)静态局部变量在函数内定义,但不像自动变量那样,当调用时就存在,退出函数时就消失。静态局部变量始终存在着,也就是说它的生存期为整个源程序。
(2)静态局部变量的生存期虽然为整个源程序,但是其作用域仍与自动变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。
(3)允许对构造类静态局部量赋初值。若未赋以初值,则由系统自动赋值。数值型变量自动赋初值0,字符型变量赋空字符。
(4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予0值。而对自动变量不赋初值,则其值是不定的。 根据静态局部变量的特点,可以看出它是一种生存期为整个源文件的量。虽然离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。 因此,当多次调用一个函数且要求在调用之间保留某些变量的值时,可考虑采用静态局部变量。虽然用全局变量也可以达到上述目的,但全局变量有时会造成意外的副作用,因此仍以采用局部静态变量为宜。