参考资料道阻且长,行则将至。埋头苦干,不鸣则已,一鸣惊人!加油,骚年!
《C++ Primer 第五版》第 268 页,第 7.6 节:类的静态成员。
《C++ Primer 第五版》第 250 页,第 7.3.3 节:类类型。
正文开始为什么需要类的静态成员?
有时候类需要一些成员与类本身直接相关,而不是与类的各个对象保持关联。
例如:一个银行账户类可能需要一个数据成员来表示当前的基准利率。
在这个例子中,我们希望 利率与类直接关联 ,而并非与类的每个对象关联。
可以从下边两个角度来看:
- 从实现效率角度来看,没有必要每个对象都存储利率信息;
- 更加重要的一点是,一旦利率浮动,我们希望所有的对象都能使用新值。
如何声明静态成员?
在成员的声明之前,加上关键字 static
即可使其与类关联在一起。
需要注意如下几个方面:
-
静态成员可以是
public
或private
。 -
静态数据成员的类型可以是常量、引用、指针、类类型等。
-
类的静态成员存在于任何对象之外,对象中不包含任何与静态数据成员有关的数据。
举例:
- 狗这个类,每只狗都有 4 条腿,那我这个成员就可以和这个类关联起来;
- 由此创建的所有对象都可以使用这个成员(由此创建的每条狗都有 4 条腿);
- 此时就没必要给使用此类的每条狗都创建一个成员,告诉它,你有 4 条腿。
-
静态成员函数也不与任何对象绑定在一起,它们不包含
this
指针。 -
作为结果,静态成员函数不能声明成
const
的,而且我们也不能在static
函数体内使用this
指针。
这一限制既适用于
this
的显式使用,也对调用非静态成员的隐式使用有效。
如何使用类的静态成员?
使用作用域运算符 “::” ,直接访问静态成员。
a::b();
虽然静态成员不属于类的某个对象,但是我们仍然可以使用类的对象、引用或者指针来访问静态成员。
成员函数不用通过作用域运算符就能直接使用静态成员。
如何定义静态成员?
既可以在类的内部,也可以在类的外部定义静态成员函数。
当在类的外部定义静态成员时,不能重复 static
关键字,该关键字只出现在类内部的声明语句。
注意:
和类的所有成员一样,当我们指向类外部的静态成员时,必须指明成员所属的类名。
static
关键字则只出现在类内部的声明语句中。
因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的。这意味着它们不是由类的构造函数初始化的。
一般来说,我们不能在类的内部初始化静态成员,必须在类的外部定义和初始化每个静态成员。和其他对象一样,一个静态数据成员只能定义一次。
类似于全局变量,静态数据成员定义在任何函数之外。因此一旦它被定义,就将一直存在于程序的整个生命周期中。
定义静态数据成员的方式和在类的外部定义成员函数差不多。我们需要指定对象的类型名,然后是类名、作用域运算符以及成员自己的名字。
double Account::interestRate = initRate();
这条语句定义了名为 interestRate
的对象, 该对象是类 Account
的静态成员,其类型为 double
。
从类名开始,这条定义语句的剩余部分就都位于类的作用域之内了。因此,我们可以直接使用 initRate()
函数。
注意:
虽然
initRate
是私有的,我们也能用它初始化interestRate
。和其他成员的定义一样,
interestRate
的定义也可以访问类的私有成员。
要想确保对象只定义一次,最好的办法是把静态数据成员的定义与其他非内联函数的定义放在同一个文件中。
静态成员的类内初始化
通常情况下,类的静态成员不应该在类的内部初始化。然而,可以为静态成员提供 const
整数类型的类内初始值,不过要求静态成员必须是字面值常量类型的 constexpr
。
初始值必须是常量表达式,因为这些成员本身就是常量表达式,所以它们能用在所有适合于常量表达式的地方。
class Account
{
public:
static double rate() {return interestRate;}
static void rate(double);
private:
static constexpr int period = 30; // period 是常量表达式
double daily_tbl[period];
};
即使一个常量静态数据成员在类内部被初始化了,通常情况下也应该在类的外部定义一下该成员。
静态成员能用于某些场景,而普通成员不能
-
静态成员独立于任何对象。
-
在某些非静态数据成员可能非法的场合,静态成员却可以正常地使用。
-
静态数据成员可以是不完全类型(扩展资料中有简单介绍)。
特别的,静态数据成员的类型可以就是它所属的类类型。而非静态数据成员则受到限制,只能声明成它所属类的指针或引用。
class Bar
{
public:
// ...
private:
static Bar mem1; // 正确:静态成员可以是不完全类型
Bar *mem2; // 正确:指针成员可以是不完全类型
Bar mem3; // 错误:数据成员必须是完全类型
};
静态成员和普通成员的另外一个区别是我们可以使用静态成员作为默认实参。
class Screen
{
public:
// bkground 表示一个在类中稍后定义的静态成员
Screen& clear(char = bkground);
private:
static const char bkground;
};
非静态数据成员不能作为默认实参,因为它的值本身属于对象的一部分,这么做的结果是无法真正提供一个对象以便从中获取成员的值,最终将引发错误!
扩展资料不完全类型
类比函数的声明可以和定义分离开,我们也可以仅仅声明类而暂时不定义它,如下所示:
class Screen; // Screen 类的声明
这种声明有时被称作前向声明,它向程序中引入了名字 Screen
并且指明 Screen
是一种类类型。
对于类型 Screen
来说,在它声明之后定义之前是一个不完全类型,也就是说,此时我们已知 Screen
是一个类类型,但是不清楚它到底包含哪些成员。
不完全类型只能在非常有限的情景下使用:可以定义指向这种类型的指针或引用,也可以声明(但是不能定义)以不完全类型作为参数或者返回类型的函数。
如果文章内容有误,麻烦评论/私信多多指教,谢谢!如果觉得文章内容还不错,记得一键三连哦(点赞、收藏、留言),您的支持就是对我最大的鼓励,谢谢您嘞!