1,静态(static)成员综述
静态成员:不是任意对象的组成部分,但由给定类的全体对象所共享的数据成员或函数成员。
静态成员是与类关联的对象,而不是像普通的成员那样与类的对象相关联。
静态成员可以是数据成员,或成员函数。
静态成员遵循正常的共有/私有访问规则。
通过关键字static声明静态成员。
2,静态数据成员
2.1 定义方式
静态数据成员必须在类定义体的外部定义,且只能定义一次。静态数据成员不能通过类的构造函数初始化,而是应该在定义时进行初始化。
保证对象正好定义一次的方法是,将static数据成员的定义放在包含类的非内联成员函数定义的源文件中。
如下所示:
// test.h
// 定义类
class Box
{
public:
Box() :len(0.0), width(0.0) {}
Box(double new_len, double new_width) :len(new_len), width(new_width) {}
double volume() { return len*width*height; }
private:
double len;
double width;
static double height;
};
//test.c
// 定义、初始化类的静态数据成员
double Box::height = 10;
//在main()函数中使用该类
int main()
{
Box box1(1, 2);
cout << box1.volume() << endl;
}
输出为120,即1*2*10.
我们在类的定义体外将静态成员height初始化为10.
注意:不要将静态成员的定义放在头文件中,除非你能保证该头文件不会被两个不同的源文件包含,不然会报重复定义错误,相当于是在两个源文件中都定义了同一个变量。对于这种同一头文件被不同源文件包含的情况,使用#ifndef或者#pragma once是没用的。这种预编译宏是为了解决一个源文件两次包含同一个头文件的问题。
同时,对静态数据成员的定义也不能放在main()函数里,否则编译报错:error C2655: “Box::height”: 当前范围内的定义或重新声明非法。
正确的做法,就如上面所说,把它放在定义类的成员函数的源文件中。
2.2 const static数据成员
const static数据成员的值一经定义不能修改。
特别地,当该const static成员是
整型时,可以在类的定义体内初始化,但是仍然必须在类的定义体外定义,此时定义时可以不必,也不能再赋值了。如果在类的定义体内和该const static整型成员的定义处都赋值,会报重复初始化错误,哪怕赋的是相同的值。
注意,这种在类的定义体内初始化的写法,只适用于整型!
如下所示:
// test.h
class Box
{
public:
Box() :len(0.0), width(0.0) {}
Box(double new_len, double new_width) :len(new_len), width(new_width) {}
double volume() { return len*width*height; }
static Box box; //该类类型的静态数据成员
private:
double len;
double width;
static double height;
//static const double weight = 50.0; //必须是整型?!!error C2864: 带有类内初始值设定项的静态数据成员必须具有不可变的常量整型
static const double weight;//不在类内给定初始值的static const则不要求必须是整型。好奇葩!!
static const int iNr = 10;
//const static double weight; //const 与static谁前谁后都可以
};
// test.c
double Box::height = 10;
const int Box::iNr;
const double Box::weight = 50.3;
Box Box::box(3, 4);
int main()
{
Box box1(1, 2);
cout << Box::box.volume() << endl;
cout << Box::box.box.volume() << endl;
cout << Box::box.box.box.volume() << endl;
}
为什么整型会有特殊待遇,感觉很奇葩。
另外请注意,静态数据成员可以是本类类型的,而一般的成员最多是本类类型的指针或引用。
上面的例子中几个cout的输出是相同的,都是120, 3*4*10
3,静态成员函数
在声明成员函数时,在前面加static即成为静态成员函数。
注意,只能在声明的时候加,在体外定义的时候,不能再加static限定,否则报错:
error C2724: “Bar::callsFooVal”:“static”不应在文件范围内定义的成员函数上使用
静态成员函数与普通成员函数最大的区别是,其没有this指针,所以它不能直接使用类的非静态数据成员,但可以直接使用类的静态数据成员。如果一定要使用类的非静态数据成员,就一定要指明对象(估计该对象也只能是全局变量了)。但一般而言还是尽量不要这么使用。
由于static成员不是任何对象的组成部分,没有this指针,不可能修改函数所属的对象,所以其也不能被限定为const。
// test.h
class Foo
{
public:
Foo():iVal(0) {}
Foo(int new_val) :iVal(new_val) {}
int get_value() { return iVal; }
private:
int iVal;
};
class Bar
{
public:
int FooVal() { iTimes++; return fool.get_value(); }// 普通的成员函数,也能使用静态数据成员
static int callsFooVal() { return iTimes; } //使用了静态数据成员
private:
static int iTimes;
static int i;
static Foo fool;
};
// test.c
Foo Bar::fool(3132);
int Bar::i = 520;
int Bar::iTimes = 0;
//int Bar::iTimes; //OK,自动赋0值
int main()
{
Foo foo_1(520);
cout << foo_1.get_value() << endl;
Bar bar_1;
Bar bar_2;
cout << bar_1.FooVal() << " " << bar_2.FooVal() << endl;
cout << Bar::callsFooVal() << endl;
}
最终输出:
520
3132 3132
2
注意,静态成员函数只能使用静态数据成员;但是普通的成员函数,既能使用普通的数据成员,也能使用静态数据成员。