一、声明静态成员
1、通过在成员的声明之前加上关键字static使得其与类关联在一起。和其他成员一样,静态成员可以是public的或private的。静态数据成员的类型可以是常量、引用、指针、类类型等。
二、使用类的静态成员
2、类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据,静态数据成员被所有该类型的对象共享。
3、静态成员函数也不与任何对象绑定在一起,它们不包含this指针。作为结果,静态成员函数不能声明成const的,而且我们也不能在static函数体内使用this指针。这一限制即适用于this的显示使用,也对调用非静态成员的隐式使用有效。
4、具体使用方法
4.1、使用作用域运算符直接访问静态成员
4.2、虽然静态成员不属于类的某个对象,但是我们仍然可以使用类的对象、引用或指针来访问静态成员
4.3、成员函数不用通过作用域运算符就能直接使用静态成员
三、定义静态成员
5、当在类的外部定义静态成员时,不能重复static关键字,该关键字只出现在类内部的声明语句中。
5.1、可以在类的内部或外部定义静态成员函数,定义在外部的静态成员函数,必须指明成员所属的类名。
5.2、一般来说,不能在类的内部初始化静态成员。相反的,必须在类的外部定义和初始化每个静态成员。和其他对象一样,一个静态数据成员只能定义一次。因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的。这意味着它们不是由类的构造函数初始化的。类似于全局变量,静态数据成员定义在任何函数之外。因为一旦它被定义,就将一直存在于程序的整个生命周期中。
class part
{
static int x(); //成员函数声明
static int i; //数据成员的声明
};
int part::i=x(); //数据成员的定义和初始化(一旦遇到类名和作用域运算符,则后面部分均在类的作用域内)
int part::x(){//函数体} //成员函数定义
6、静态数据成员应该保证只定义一次,因此应把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。
7、通常情况下,类的静态成员不应该在类的内部初始化。然而,当声明该静态成员类型为const或constexpr类型,并保证初始值为常量表达式时才允许类内初始化(这里并不代表具有const或constexpr修饰的数据成员必须类内初始化)。带有类内初始值设定项的静态数据成员必须具有不可变的常量整型,也就是说其他例如string类型是不可以的。所以它们能用在所有适合于常量表达式的地方。例如,我们可以用一个初始化了的静态数据成员指定数组成员的维度。
class part
{
static int x();//成员函数声明
static constexpr int i=8;//数据成员的声明
constexpr static int j=8;//数据成员的声明
constexpr static string s="hello"; //错误:带有类内初始值设定项的静态数据成员必须具有不可变的常量整型
int data[i];
};
constexpr int i;
即使一个常量静态数据成员在类内部被初始化了,也应该在类的外部定义一下该成员(虽然一些情况下程序不会出错),并且类内和类外使用的常量修饰符要一致。
四、静态数据成员不同于普通成员
8、静态数据成员可以是不完全类型。特别的,静态数据成员的类型可以就是它所属的类类型。而非静态数据成员则受到限制,只能声明成它所属类的指针或引用。
class part
{
static part i;//静态数据成员可以是不完全类型
part *j;//指针成员可以是不完全类型
part k;//数据成员必须是完全类型
};
9、可以使用静态成员作为默认实参。非静态数据成员则不能作为默认实参,因为它的值本身属于对象的一部分,这么做的结果是无法真正提供一个对象以便从中获取成员的值,最终将引发错误。
class part
{
public:
void print(int j= i) { cout << j << endl; }
private:
const static int i=8;
};