C++ Primer 第七章⑥
C++ Primer
第七章 类
类的静态成员
我们目前学到的类的内容都是实例化后每个对象各自拥有的,有时候,有时候,类需要它的一些成员与类本身直接相关。例如,一个银行账户类可能需要一个数据成员来表示当前的基准利率,我们希望利率与类关联,而不是和对象关联,更加重要的是,一旦利率变化,我们希望所有的对象都能使用新值
声明静态成员
我们就来写个银行账户类:
class Account
{
public:
void calculate()
{
amaunt += amount * intereatRate;
}
static double rate(){return interestRate;}
static void rate(double);
private:
string owner;
double amount;
static double interestRate;
static double initRate();
};
类的静态成员存在于对象之外,所以啊,每个Account对象只有两个数据成员:owner和amount,而interestRate是类所有,所有对象共享。 因为静态成员函数不与任何对象绑定,所以没有this指针,不能声明为const的。
使用类的静态成员
使用类作用域运算符::直接访问:
double r;
r = Account::rate();
我们还是可以用对象去访问静态成员,毕竟共享
Account c1;
Account *ac2 = &ac1;
r = ac1.rate();
r = ac2->rate();
以上是在类外访问,如果在类内呢?你看上面的calculate函数就知道了,直接访问。
定义静态成员
突然想起个小问题:类成员函数的声明只能放在类内,对不?(对的,求讨论)
类内部定义前面的代码已经有了,就是加个关键字static;在类外部定义静态成员时,不能重复static关键字,该关键字只能出现在类内部的声明语句。
因为类的静态数据成员不属于任何一个对象,所以它们不是在实例化时被定义的,也就是说,构造函数不负责初始化它们,而且一般来说,我们要在类的外部定义和初始化每一个静态成员。
类似全局变量,静态数据成员定义在任何函数之外,它一旦被定义,就一直存在于程序的整个生命周期中。
我们来定义一个在类内已经声明的static成员:
double Account::interestRate = initRate();
//为什么不用在initRate函数前面加Account::呢
//因为从类名开始,剩下的部分都处于类的作用域之内了。
静态成员的类内初始化
刚刚说过,类的静态成员应该在类外定义初始化,于是C++又来破坏自己定的规矩了:我们可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是constexpr的:
class Account
{
public:
static double rate(){return interestRate;}
static void rate(double);
private:
static constexpr int period = 30;
double daily_tbl[period];
};
为什么这儿要把初始化放在类内呢?因为period的唯一用途就是去定义daily_tbl的维度,那我们就直接在类内定义一下,外面反正不用。值得注意的是,在外面还是要定义一下,相当于声明一下:
constexpr int Account::period; //不带初始值的定义,
我的总结就是,什么狗屁东西,食之无味弃之可惜。
静态成员能用于某些场景,而普通成员不能
class Bar
{
private:
static Bar m1; //这个逆天吧,可以自己的类型
statuc int &m2; //逆天吧,未初始化的引用
};
主要是因为,静态成员一般在外面定义初始化,所以,在类内可以是不完全类型,胡作非为。
还有一个区别是,静态成员可以作为默认实参,666
class Screen
{
public:
Screen& clear(char = bkground); //666
private:
static const char bkground;
};
非静态数据成员不能作为默认实参,因为它是属于对象的,你用对象的值去作为默认实参,结果是无法真正提供一个对象来获取成员的值,会引发错误。
彩蛋
小结:
- 类允许我们为自己的应用特制类型
- 类有两项基本能力:
- 数据抽象,就是定义数据成员和函数成员
- 封装,保护类的成员不被随意访问(private,友元等)
- 构造函数来控制初始化对象的方式
- 静态成员与类绑定,存在于对象之外,是所有对象来共享的。