第15章 静态成员
第15章 程序运行中需要利用前段的数据——静态成员
在第9章中曾经浏览过,当程序运行中需要用到前面运行的数据时,是用静态局部变量来保存这种数据的,而前面也说过,面向过程的变量就是面向对象的数据成员,即原来的数据变量,就是现在的数据成员,那么原来的静态局部变量就是现在的静态数据成员。除此之外,静态成员还包含静态函数成员。不论是静态局部变量还是静态成员,其作用都是一样的,即需要用到前面运行的数据时才采用,以利于数据共享,避免使用全局变量带来的不安全性但它们的书写和使用形式有些区别,下面具体讲解。
15.1 电影院统计入场人数的例题详解
【例 15-1】电影院统计入场人数。
假设电影院用自动控制系统来统计每场入场人数,例如,当有人从入口处进场时,光电传感器或其他传感器就感应一次,并传递一个信号给计算机,使计数单元加1,当入场完毕,该计数单元的数字就是总入场人数。请用C++语言编写程序实现此功能。本题与例9-2相同,但解题方法不同。
1. 用心分析人想通,机随人行写提纲
根据题意,用人按下键 “1” 来模仿自动控制系统的运行,每按键 “1” 一次,入场人数就加 1,入场完毕,按下停止键,显示总入场人数。
2. 对号入座写文章,头脑运行改文章
设计一个电影院入场人数的类。为了防止他人修改该累计人数且为了利于其他共享该累计人数成员的用户使用,把该累计人数变量设置为保护的静态数据成员。静态成员函数 total 做累计入场人数的工作,display 函数做打印工作。
在 total()函数中,接收键盘输入函数 cin.get()接收键盘键入的字符,当接收的字符='1'时,累计人数单元就加1;当接收的字符=EOF(即同时按下〈Ctrl〉键和〈Z〉键)时,入场完毕,再按〈Enter〉键,显示入场总人数,该程序停止运行。
程序代码如下:
//ex15-1-14.cpp
#include<iostream>
using namespace std;
class Num //声明电影院入场人数类的类
{
protected:
static int num;//声明存储入场人数的总数
public:
static void total() //定义统计入场人数的静态函数成员
{
char d; //局部变量 d 用于接收键盘输入的字符
cout<<"如果有人进场看电影时,请按键'1'。"
<<"结束统计先按 Enter 键,再同时按下 Ctrl 键和 z 键,然后再按 Enetr 键'n';
while((d=cin.get())!=EOF) //d 接收从键盘输入的字符,
//并与结束符 EOF 比较决定循环与否
if(d=='1')
num++; //累计入场人数,静态函数成员直接使用静态数据成员,
//前面不加对象名
}
void display(); //声明打印函数成员
};
int Num::num=0; //num 初始化为 0
void Num :: display()//定义打印函数成员
{
cout<<"入场看电影的总人数是:"<<num<<endl; //Num 类中的函数成员 display()
//直接使用本类中的所有成员,
//前面不加对象名
}
void main()
{
Num y;//定义一个对象名为 y 的对象,它的数据类型为 Num 这个类,Num 是类名
y.total(); //通过对象 y 去调用 Num 类中的 total 函数
y.display(); //再通过对象 y 去调用 Num 类中的 display 函数
}
3. C++帮助且裁判,人定胜机凯歌扬
调试成功上述程序后,试着去掉 “int Num::num=0;” 这一句,再运行时会出现如下错误提示:
error LNK2001: unresolved external symbol "protected: static int Num::num" (?num@Num@@1HA) Debug/ex15-1-14.exe : fatal error LNK1120: 1 unresolved externals
因为没有将 num 初始化为 0,还是加回 “int Num::num=0;//num 初始化为 0” 这一句,此错纠正。
运行结果:
如果有人进场看电影时,请按键'1'。结束统计先按 Enter 键,再同时按下 Ctrl 键和 z 键,然后再按 Enter 键
14914108251
2149231
结果正确。
15.2 勤奋实践,总结评分
15.2.1 习题
- 商场中的某种商品的销售跟踪软件要求一般情况下处于运行状态,即不断显示现场的累计销售数量和累计销售金额,只有按下停止按钮时程序才停止运行,用静态函数成员表达销售量和显示销售金额,编写程序实现。
- 用面向对象的方法编写一个能显示系统当前日期与时间的程序。
- 使用对象设计计算楼房造价的程序,其中:
造价(元)=整栋楼的长(米)×整栋楼的宽(米)×层数×价格(元)/每平方米。
用静态函数成员记录所计算的楼房的数量。
输入整栋楼的长(米)、整栋楼的宽(米)、层数和价格(元)/每平方米,计算多栋楼房的造价,然后分别显示所计算楼房的数量、每栋楼房的造价和所有楼房的总造价。
4.用类编写一个学生成绩档案输入程序,档案包括名字、性别、学号、2门课的成绩,并用静态函数成员记录输入的学生人数。按题目要求输入学生档案,并打印学生成绩档案和输入的学生人数。
5.有一个验证身份证号码的程序,其中,由主函数调用验证身份证号码的函数成员,并且在这个函数成员中有一个静态数据成员记录身份证号码输入错误的次数,当输入错误次数大于3次时,就要拒绝再输入号码,且提示用户找有关部门处理。请编程实现。
- 某商场搞优惠活动,凡是该商场的会员均可以按会员价购买 5 袋洗衣粉,当购买的数量累计超过 5 袋时,系统会提示:你购买的洗衣粉已超过 5 袋,5 袋之内按会员价付款,超过部分按原价付款。请设计程序实现该过程。
- 在一个智力竞猜游戏中,当出现题目时,你就输入答案,若答对了,静态数据成员就加1,还有另外一个静态数据成员记录答题的总数。游戏结束时,显示正确答题的数量和正确答题的百分比。
- 有一个工厂某种产品的厚度尺寸标称值是 20mm,当 ±0.1mm(即产品厚度在 19.9~20.1mm 之间)≤公差<±0.5mm 时为合格品,±0.5mm≤公差<±1mm 时为次品,±1mm≤公差时为废品。请编程模拟在线检验及产品分类的工作,并分别显示合格品、次品、废品的数量和合格品率、次品率、废品率。
9.假设本科生保送研究生的条件之一是,在校期间要被评为三好学生二次以上,且无记过及以上的处分,那么,你怎样从前6个学期的成绩当中,统计出评为三好学生的次数和受处分的次数,并将结果显示出来,以协助保研工作的开展。
10.仓库有几种容易缺货且难于购买的材料,你能否在发放材料的过程中,跟踪显示这些材料的库存数量,以提示有关人员提前订货,适时购买呢?请编程实现。
15.2.2 思考题
1.在本章习题第4题中,再加上按学号查询档案的功能,并用静态成员函数记录查询的学生人数。
2.用面向对象的程序设计方法设计一个输入图书馆图书卡片信息的程序,信息包括书名、作者、出版日期和在馆数量。用静态函数成员记录输入的图书卡片数量和在馆数量。
15.2.3 总结评分,继续提高
浏览、体验了例题、习题和思考题之后,说说自己的体会和收获,并认真总结,按表15-1给自己评判一下可以得多少分。
| 成绩 | A | B | C | D | 不足的原因 | 改进的方法 | 备注 |
| C++知识 | 完全理解和会用 | 基本理解和会用 | 肤浅理解和会用 | 基本不理解和不会用 | |||
| 程序原创(独立编程)的比例(%) | ≥90 | 80~89 | 60~79 | <60 |
不论得分多少,大家都要总结经验,扬长避短,勤奋努力,不断提高。
最后浏览一下本章的第4节,可以先考考自己,然后再浏览,以便使重点知识掌握得更牢固。
15.3 静态成员知识
前面提到静态局部变量与静态成员的作用一样,只是编写程序时所用到的称呼和表示不同而已,所以,只要注意到二者在书写和使用时的异同,静态成员很快就会被掌握了。
15.3.1 在什么情况下用到静态成员
当程序运行中需要用到前面运行的数据时,就用静态成员。例如,累计人数、累计对象数、统计生产数量,或利用前面运行的数据作为特殊的标志以供后续程序使用(如篮球赛中犯规次数的累计达到罚出场的标准时,就不允许该队员上场了),等等。静态成员包括静态数据成员和静态函数成员。静态函数成员最好只用静态数据成员的数据来操作,不要使用非静态数据成员,如果还要用到一些变量,则在静态函数成员中再声明一些局部变量,这样编程会方便些,如例15-1所示。
15.3.2 关于静态成员
1. 什么是静态成员
静态成员可以将程序中前面运行的数据保存下来,以供后续程序运行、使用。静态成员属于类的一部分,而不属于某个(些)对象,某个类的静态成员可以被这个类的所属对象共享,即某个(些)对象是属于这个类中的对象,则这个(些)对象就可以用从属于这个类中的所有资源,即数据成员和函数成员,包括静态成员和非静态成员。
2. 静态成员的声明及定义
(1) 静态成员的声明格式
在原来成员(非静态成员)的前面加上关键字 static(英文含义是“静态的”)即可。【例 15-2】假设在类名为 Num 的类中,要声明一个名为 num 的静态数据成员。
声明语句如下:
static int num;//声明静态数据成员 num
(2) 静态成员的定义
1 )静态数据成员的初始化必须在类外进行,格式如下:
数据类型 类名::静态数据成员名=初始值;
初始值可根据实际需要而定,不一定是 0 或 1,但如果未初始化,程序会出错,见例 15-1 的调试记录。
【例 15-3】假设在类名为 Num 的类中,声明了一个名为 num 的静态数据成员,请将 num 初始化为 0。
语句如下:
int Num::num=0;//num 初始化为 0
2)静态函数成员的定义可在类内也可在类外进行,与以前的函数成员定义方法基本相同,只是在使用非静态成员时,前面要加上“对象名.”或“对象名->”,如例15-4中的“s.num=0;”。
3. 静态成员的使用
静态数据成员和静态函数成员的用法与一般的成员的用法基本一样。只要注意上面所讲的静态函数成员使用非静态成员时的特殊情况即可。
【例 15-4】将例 15-1 中的静态数据成员 num 改为一般数据成员,并实现例 15-1 的功能。程序代码如下:
//ex15-1-4.cpp
#include<iostream>
using namespace std;
class Num
{
protected:
int num;//入场人数,这是一般的数据成员(非静态)
public:
static int total();
void display();
};
int Num::total()//统计入场人数
{
int d;//字符在 ASCII 码范围内,类型 char 和 int 可互换使用,但都表示字符 s.num=0;//先将入场人数清为 0,再统计入场人数。静态函数成员 total
//使用非静态的数据成员,前面加对象名 s,可与例 15-1 中的相应句加以比较
cout<="如果有人进场看电影时,请按键'1'。"
<<"结束统计先按 Enter 键,再同时按下 Ctrl 键和 z 键,然后再按 Enter 键'n";
while((d=cin.get())!=EOF)
{
if(d='1')
s.num++;
}
return s.num;
}
void Num :: display()//打印
{
num=total(); //非静态函数成员 display 使用所有成员(如 num、total)与以前一样,//即前面不用加对象名
cout<<"入场看电影的总人数是:"<<num<<endl;
}
void main()
{
Num y;
y.display();
}
运行结果:
如果有人进场看电影时,请按键'1'。结束统计先按 Enter 键,再同时按下 Ctrl 键和 z 键,然后再按 Enter 键
1332432518515151
^z
入场看电影的总人数是:5
果正确。
15.3.3 如何运用静态成员
根据上述的静态数据成员和静态函数成员的声明、定义可知,静态成员的用法与一般的成员的用法基本一样。静态函数成员最好只用静态数据成员的数据来操作,或再加一些局部变量。
【例 15-5】在例 15-1 的基础上多次小计入场人数,最后统计总的入场人数,请编写程序实现。
程序代码如下:
//ex15-1-62.cpp
#include<iostream>
using namespace std;
class Num
{
protected:
static int num;//存储入场人数의静态数据成员
public:
static void total();
void display();
};
int Num::num=0;//num 初始化为0
void Num::total()//统计入场人数
{
int d;
cout<<"如果有人进场看电影时,请按键'1';如暂时小计入场人数就按k键,"
<<"然后按 Enter 键\n";
while((d=cin.get())!=k')
if(d=='1')
num++; //用心观察每次进入此函数时的值
//与上次运行完此函数时的值的特点
}
void Num :: display()//打印输出
{
char c1;
int stanum=0;//存储统计次数的局部变量
do
{
total();
stanum++;
cout<<"第"<<stanum<<"次累计"<<"入场看电影的人数是:"<<num<<endl;
cout<<"您还要统计吗(y/n)?(是就按y键,不是就按n键,然后按Enter键)"<<endl
cin>>c1;
} while(c1==y');
cout<<"最后统计入场看电影的总人数是:"<<num<<endl;
}
void main()
{
Num y;
y.display();
}
}
运行结果:
如果有人进场看电影时,请按键'1';如暂时小计入场人数就按k键,然后按Enter键32`314812305
98147201
k
第1次累计入场看电影的人数是:4
您还要统计吗(y/n)?(是就按 y 键,不是就按 n 键,然后按 Enter 键)
如果有人进场看电影时,请按键'1';如暂时小计入场人数就按k键,然后按Enter键23-12851253=41241511
k
第2次累计入场看电影的人数是:10
您还要统计吗(y/n)?(是就按 y 键,不是就按 n 键,然后按 Enter 键)
最后统计入场看电影的总人数是:10
结果正确。
15.4 静态成员的亮点
1. 在什么情况下用静态成员的亮点
- 静态成员具体知识的亮点
当程序运行中需要用到前面运行的数据时,就用静态成员。
1)静态成员可以将程序中前面运行的数据保存下来,以供后续程序运行、使用。
2)静态成员的声明格式:
在原来成员(非静态成员)的前面加上关键字 static。
3)静态数据成员的初始化必须在类外进行,格式如下:
数据类型 类名::静态数据成员名=初始值;
初始值可根据实际需要而定,不一定是0或1。静态数据成员一定要初始化为某个值,否则调试时会出错,这与静态局部变量不同。
4)静态函数成员的定义可在类内也可在类外进行,与以前的函数成员定义方法基本相同。
5)静态数据成员和静态函数成员的用法与一般的成员的用法基本一样。静态函数成员最好只用静态数据成员的数据来操作。
3. 运用静态成员的亮点
根据上述的静态数据成员和静态函数成员的声明、定义可知,静态成员的用法与一般的成员的用法基本一样。