任务描述
本关任务:编写一个能管理多条学生信息的程序。
相关知识
为了完成本关任务,你需要掌握构造函数与析构函数的调用和对象数组的使用。
构造函数与析构函数的调用
构造函数不能直接调用,只能通过声明一个对象或者使用new 运算符动态创建对象时由系统自动调用。
例如:
-
class Test { public: int A; Test(); Test(int a); }; /* 此处省略定义构造函数部分 */ int main() { Test t; // 调用无参构造函数 Test t2(10); // 调用带参构造函数 Test t3 = Test(10); // 同上 Test *t = new Test; // 动态创建对象,调用无参构造函数 Test *t2 = new Test(10); // 动态创建对象,调用带参构造函数 }
而析构函数则不同,它能够通过对象主动调用,并在以下两种情况下它会自动调用:
-
若一个对象被定义在一个函数体内,当这个函数结束时(声明的变量的生命周期结束)会自动调用。
-
若一个对象是使用 new 运算符动态创建,在使用 delete 释放时会自动调用。
例如:
-
/* Test类的声明接上 */ Test::~Test() // 修改一下析构函数,让它打印一条消息 { cout << "Test的析构函数被调用" << endl; } int main() { cout << "p1" << endl; { Test t1(1); // t1 的生命周期就只在这个大括号内 } //因此在这个位置 t1 的析构函数就会被调用 cout << "p2" << endl; Test *t2 = new Test(10); delete t2; // t2 所指对象的析构函数在此被调用 cout<<"p3"<<endl; { Test *t3 = new Test; } // t3 所指对象的析构函数并不会被调用,因为没有使用 delete 运算符 }
输出结果为:
p1
Test的析构函数被调用
p2
Test的析构函数被调用
p3
上述代码中 t1 对象的析构函数调用的位置有点微妙,它是在代码离开大括号}
的那瞬间的位置被调用的,因为一个变量只在直接包含它的那层大括号的范围内存活。
对象数组
数组对象就是大批量实例化对象的一种方法,以往我们都是这样:Student stu
实例化对象,如果有好几百个对象应该怎么办?
这时候就用到了对象数组,顾名思义,就是把所有要实例化的对象都放到一个组里面,然后直接实例化这个组,就像这样:Student stu[100]
,便可一次性实例化100个对象。
对象数组与一般的数组基本一致,只是多了两个过程:
-
在数组创建的时候对数组的每一个元素都调用了构造函数;
-
在数组生命结束的时候对数组的每一个元素都调用了析构函数。
如果使用 new 运算符来动态创建对象数组,也是同样的过程。
注意:在创建数组时如果不使用列表初始化语法对数组中的每一个元素调用构造函数,那么默认调用无参数的构造函数,因此也就要求这个类必须要有无参数的构造函数。
例如:
-
class Test1 { public: Test1(); ~Test1(); }; Test1::Test1() { cout << "Test1的构造函数" <<endl; } Test1::~Test1() { cout << "Test1的析构函数" <<endl; } class Test2 { public: Test2(int a); // 没有无参数的构造函数 ~Test2(); }; Test2::Test2(int a) { cout << "Test2的构造函数" <<endl; } Test2::~Test2() { cout << "Test2的析构函数" <<endl; } int main() { Test1 ts1[2]; // Test1 有无参构造函数,OK Test2 ts2[2]; // 错误,Test2 没有无参构造函数 Test2 ts3[2]={Test2(10)}; // 这个也错误,因为只对第一个元素调用了构造函数,第二个还是会主动调用无参构造函数 Test2 ts4[2]={Test2(10),Test2(20)}; // 正确 }
如果删除那两行错误的声明,那么输出结果为:
Test1的构造函数
Test1的构造函数
Test2的构造函数
Test2的构造函数
Test2的析构函数
Test2的析构函数
Test1的析构函数
Test1的析构函数
为了方便查看,在输出结果中间空了一行,上面是创建数组时对元素调用构造函数产生的输出,下面是数组死亡时对每一个元素调用析构函数产生的输出。
编程要求
在右侧编辑器中的Begin-End
之间补充代码,设计 Student 类并实现用于管理学生信息表(学生表的长度不超过5)的3个函数,成员变量和函数的访问性都为公有的,具体类结构和函数要求如下:
-
学号,
int
类型 -
姓名,
string
类型 -
分数,
float
类型 -
带参构造函数:
Student(int sid,string name,float sco)
,分别用这三个参数设置内部的三个成员。 -
void Add(int sid,string name,float sco)
,函数用于向学生表的末尾添加一条学生记录。 -
void PrintAll()
,输出学生表中所有的记录,格式为:学号 姓名 成绩
。 -
void Average()
,计算学生表中学生的平均成绩并输出,格式为:平均成绩 计算结果
。
提示:学生表可以用全局对象数组来完成,定义全局对象数组和定义全局变量一样,即定义在最外层作用域。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确数值,只有所有数据全部计算正确才能通过测试:
测试输入:0 厉宏富 96 1 冷欣荣 85 2 鲍俊民 76
预期输出:
0 厉宏富 96
1 冷欣荣 85
2 鲍俊民 76
平均成绩 85.6667
#include <string>
#include <iostream>
using namespace std;
/********* Begin *********/
class Student
{
//在此处声明所需的成员
public:
int SID;
string Name;
float Sco;
Student();
Student(int sid,string name,float sco)
{
SID=sid;
Name=name;
Sco=sco;
}
};
Student st[5];
int stcount;
Student::Student()
{}
/********* End *********/
void Add(int sid,string name,float sco)
{
/********* Begin *********/
st[stcount]=Student(sid,name,sco);
stcount++;
/********* End *********/
}
void PrintAll()
{
/********* Begin *********/
//打印出学生表中所有记录
for(int i=0;i<stcount;i++)
{
cout<<st[i].SID<<" "<<st[i].Name<<" "<<st[i].Sco<<endl;
}
/********* End *********/
}
void Average()
{
/********* Begin *********/
//计算并打印出学生表中的平均成绩
float sum=0.00;
float ave;
for(int i=0;i<stcount;i++)
{
sum+=st[i].Sco;
}
ave=sum/stcount;
cout<<"平均成绩 "<<ave<<endl;
/********* End *********/
}