C++类:类的静态成员

类的静态成员

​ 有的时候类需要它的一些成员与类本身直接相关,而不是与类的各个成员保持关联。例如,一个银行账户类可能需要一个成员表示当前的基准利率。在此例中,我们希望利率与类关联,而非与类的每个对象关联。从实现效率的角度来看,没必要每个对象都存储利率信息。而且更加重要的是,一旦利率浮动,我们希望所有的对象都能使用新值。

声明静态成员

​ 可以通过在成员的声明之前加上关键词 static 使得其与类关联在一起。和其他成员一样,静态成员可以使 public 或者 private。静态数据成员可以使常量、引用、指针、类类型等。

​ 此处定义一个类,表示银行的账户记录

class Account {
public:
    void calculate() { amount += amount * interestRate; }
    static double rate() { return interestRate; }
    static void rate(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;
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();

这一条语句定义了名为 interestRate 的对象,该对象是类 Account 的静态成员,其类型是 double。从类名开始,这条定义语句的剩余部分就位于类的作用域之内了。因此可以直接使用 initRate 函数。虽然它是私有的,也能用它初始化 interestRate 成员。和其他成员一样,interestRate 的定义也可以访问类的私有成员。

tip:要先确保对象只定义一次,最好的办法是把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。

静态成员的类内初始化

​ 通常情况下,类的静态成员不应该出现在类的内部初始化。然而,我们可以为静态数据成员提供 const 整数类型的类内初始值,不过要求静态成员必须是(字面值常量类型的) constexpr初始值必须是常量表达式,因为这些成员本身就是常量表达式,所以它们能用在所有适合于常量表达式的地方。例如,我们可以用一个初始化了的静态成员指定数组成员的维度。

class Account {
public:
    static double rate() { return interestRate; }
    static void rate(double);
private:
    static constexpr int period = 0;		// period 是常量表达式
    double daily_tbl[period];
};

即使一个常量静态成员在类内部被初始化了,通常情况下也应该在类的外部定义一下该成员

记懵点

// 记一懵点,还不是很明白
class X {
public:
    static const int x = 1;			// ok
    // error:in-class initializer for static member data of type 'const double' requires 'constexpr' specifier
    static const double y = 1.1;		// 应改为 static constexpr double y = 1.1;
};

const int x = 8;
const double y = 1.1;
class Y {
public:
    static constexpr double a = x;		// ok
    // error: constexpr variable 'b' must initialized by a constant expression
    static constexpr double b = y;
};
静态成员能用于某些场景,而普通成员不能

​ 静态成员独立于任何对象。因此,在某些非静态数据成员可能非法的场合,静态成员却可以正常地使用。举个例子,静态数据成员可以是不完全类型。特别的,静态数据成员的类型可以就是它所属的类类型。而非静态数据成员将受到限制,只能声明成它所属的指针或引用:

class Bar {
public:
    // ...
private:
    static Bar mem1;		// 合法,静态成员可以是不完全类型
    Bar *mem2;				// 合法,指针成员可以使不完全类型
    Bar mem3;				// 不合法
};

静态成员和普通成员的另一个区别是我们可以使用静态成员作为默认实参

class Screen {
public:
    Screen& clear(char = bkground);
private:
    static const char bkground;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值