7.6 类的静态成员
有时类需要它的一些成员与类本身直接相关,而不是与类的各个对象保持关联,这时需要使用类的静态成员。
声明静态成员
通过在成员的声明之前加上关键字static
使得其与类关联在一起。
静态数据成员可以是public
或者private
的。
静态数据成员的类型可以是常量、引用、指针、类类型等。
举例如下,定义一个类,表示银行的账户记录:
class Account {
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rete(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
};
类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据,因此,每个Account
对象将包含两个数据成员:owner
和amount
。只存在一个interestRate
对象而且它被所有Account
对象共享。
类的静态成员函数也不与任何对象绑定在一起,它们不包含 this 指针。静态成员函数不能声明为 const 的,也不能在 static 函数体内使用 this 指针。这一限制即适用于 this 的显式使用,也对调用非静态成员的隐式使用有效
使用类的静态成员
使用作用域运算符直接访问静态成员:
double r;
r = Account::rate(); // 使用作用域运算符访问静态成员
使用类的对象、引用或者指针访问静态成员:
Account ac1;
Account *ac2 = &ac1;
// 调用静态成员函数 rate 的等价形式
r = ac1.rate(); // 通过 Account 的对象或引用
r = ac2->rate(); // 通过指向 Account 对象的指针
成员函数不用通过作用域运算符就能直接使用静态成员
class Account {
public:
void calculate() { amount += amount * interestRate; }
private:
static double interestRate;
};
定义静态成员
既可以在类的内部也可以在类的外部定义静态成员函数。
当在类的外部定义静态成员时,不能重复static
关键字,该关键字只出现在类内部的声明语句。
void Account::rate(double newRate) {
interestRate = newRate;
}
静态成员不是在创建类的对象时被定义的,它们不是由类的构造函数初始化的。
不能在类的内部初始化静态成员,必须在类的外部定义和初始化每个静态成员。一个静态成员只能定义一次。
定义静态数据成员的方式和在类的外部定义数据成员函数差不多,需要指定对象的类型名,然后是类名、作用域运算符以及成员的名字:
// 定义并初始化一个静态成员
double Account::interestRate = initRate();
要想确保对象只定义一次,最好的办法是把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。
静态成员的类内初始化
类的静态成员不应该在类的内部初始化,但是,可以为静态成员提供 const 整数类型的类内初始值,此时要求静态成员必须是字面值常量类型的 constexpr。初始值必须是常量表达式。
class Account {
public:
static double rate() { return interestRate; }
static void rete(double);
private:
static constexpr int period = 30; // period 是常量表达式
double daily_tbl[period];
};
如果在类的内部提供了一个初始值,则成员的定义不能再指定一个初始值了:
// 一个不带初始值的静态成员的定义
constexpr int Account::period; // 初始值在类的定义内提供
即使一个常量静态数据成员在类的内部被初始化了,通常情况下也应该在类的外部定义一下该成员。
静态成员能用于某些场景,而普通成员不能
静态成员可以用于一些非静态数据成员非法的场合。
静态数据成员可以是不完全类型,例如,静态数据成员的类型可以就是它所属的类类型。
class Bar {
public:
// ,,,
private:
static Bar mes1; // 正确:静态成员可以是不完全类型
Bar *mem2; // 正确:指针成员可以使不完全类型
Bar mem3; // 错误:数据成员必须是完全类型
};
可以使用静态成员作为默认实参,不能使用非静态成员作为默认实参。
class Screen {
public:
// bkground 表示一个在类中稍后定义的静态成员
Screen& clear(char = bkground);
private:
static const char bkground;
};