1.静态数据成员
在类中,有些属性不是类中每个对象分别拥有的,而是共有的。这些共有的属性有些是变化的,如类对象创建的计数值,有些是不变的,如日期类中要用到的12个月的名称。
这些属性不能作为全局变量,因为它们是专属于某个类的。
同时这些属性页不应该是数据成员,因为不能让每个对象都单独拥有它。
对于下列代码:
#include <iostream>
#include <string>
using namespace std;
class Student
{
int n;
string name;
public:
void set(string str)
{
static int number = 0;
name = str;
n = ++number;
}
void print()
{
cout << name << " -> students are " << n << " numbers" << endl;
}
};
void fn()
{
Student s1;
s1.set("Jenny");
Student s2;
s2.set("Randy");
s1.print();
}
int main()
{
Student s;
s.set("Smith");
fn();
s.print();
return 0;
}
输出为:
Jenny -> students are 2 numbers
Smith -> students are 1 numbers
从输出的结果可以看出,结果和我们预期的不同。这主要是由于不同的对象有不同的数据成员n值所致。
那么这个时候需要将学生计数的变量设计为静态成员。这样保证每一个类只有一个实体,每个对象中不再有它的副本。代码实现如下:
#include <iostream>
#include <string>
using namespace std;
class Student
{
static int number;
string name;
public:
void set(string str)
{
name = str;
++number;
}
void print()
{
cout << name << " -> students are " << number << " numbers" << endl;
}
};
int Student::number = 0;
void fn()
{
Student s1;
s1.set("Jenny");
Student s2;
s2.set("Randy");
s1.print();
}
int main()
{
Student s;
s.set("Smith");
fn();
s.print();
return 0;
}
该代码输出如下:
Jenny -> students are 3 numbers
Smith -> students are 3 numbers
数据成员设计成静态数据成员后,该成员的变化仍然会在每次对象创建之后的名称赋值后反映出来。该结果存放在专属于Student类名空间的全局数据区中,而不再属于各个Student对象。整个类中只有一份number拷贝,所有对象共享这份拷贝。因此,访问的是唯一的静态数据成员,它不会因对象而异。
由于静态成员脱离对象而存在的性质,所以该实体应在所有对象产生之前存在,因此,在程序启动的时候,需要先对其进行初始化。
定义静态成员的格式不能重复static关键字(int Student::number = 0;)但必须在成员名钱冠以类名加域操作符,以表示该成员的类属。如果不将其初始化,系统将为该成员清0。
2. 静态成员函数
由于如果把静态成员变量设置为公有类型的话,外部就可以直接访问该静态成员变量,这样有失安全性和可维护性,因此与数据成员一样,将静态成员做成私有的,用静态成员函数去访问静态数据成员是适合的。
#include <iostream>
#include <string>
using namespace std;
class Student
{
static int number;
string name;
public:
void set(string str)
{
name = str;
++number;
}
static void print_number()
{
cout << number << " total numbers" << endl;
}
void print()
{
cout << name << " -> students are " << number << " numbers" << endl;
}
};
int Student::number = 0;
void fn()
{
Student s1;
s1.set("Jenny");
Student s2;
s2.set("Randy");
s1.print_number();
}
int main()
{
Student s;
s.set("Smith");
fn();
Student::print_number();
return 0;
}
输出如下:
3 total numbers
3 total numbers
静态成员函数并不受对象的牵制,可以用对象名调用静态成员函数,也可以用类名加上域操作符调用静态成员函数。
静态成员函数的实现位置与成员函数的实现位置应该是一起的。静态成员函数如果不在类中实现,而在类的外部实现时,类名前应免去static关键字,成员函数的静态性只在第一次声明的时候才是必须的。