(C++)PTA:多态性 虚函数
相关知识点
1、虚函数的工作原理
(引用C++ Primer Plus 13.4小节)
C++规定了虚函数的行为,并将实现方法留给了编译器,使得编写程序的人不需要知道实现方法就可以使用虚函数。但是了解虚函数的工作原理有助于更好地理解概念,因此,这里对其进行介绍。
通常,编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这种数组称为虚函数表( virtual function table, vtbl)。 虚函数表中存储了为类对象进行声明的虚函数的地址。例如,基类对象包含一个指针,该指针指向基类中所有虚函数的地址表。派生类对象将包含一个指向独立地址表的指针。如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址:如果派生类没有重新定义虚函数,该vtbl将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址也将被添加到vtbl中。注意,无论类中包含的虚函数是1个还是10个,都只需要在对象中添加1个地址成员,只是表的大小不同而已。
2、一些特殊函数的虚函数有无情况
- 构造函数:不可以声明为虚函数(派生类构造函数将使用基类构造函数)
- 析构函数:可以声明为虚函数,除非类不用做基类(通常应给基类提供一个虚拟析构函数,即使它并不需要析构函数)
- 友元函数:不可以声明为虚函数(友元函数不是类的成员)
6-1 汽车收费(多态) (10 分)
现在要开发一个系统,管理对多种汽车的收费工作。 给出下面的一个基类框架
class Vehicle
{
protected:
string NO;
public:
Vehicle(string n){
NO = n;
}
virtual int fee()=0;//计算应收费用
};
以Vehicle为基类,构建出Car、Truck和Bus三个类。
Car的收费公式为: 载客数8+重量2
Truck的收费公式为:重量5
Bus的收费公式为: 载客数3
生成上述类并编写主函数
主函数根据输入的信息,相应建立Car,Truck或Bus类对象,对于Car给出载客数和重量,Truck给出重量,Bus给出载客数。假设载客数和重量均为整数
输入格式:第一行输入测试用例数。接着每个测试用例占一行,每行给出汽车的基本信息,第一个数据为当前汽车的类型:1为car,2为Truck,3为Bus。第二个数据为它的编号,接下来Car是载客数和重量,Truck要求输入重量,Bus要求输入载客数。
要求输出各车的编号和收费。
裁判测试程序样例:
#include<iostream>
#include <string>
using namespace std;
class Vehicle
{
protected:
string NO;//编号
public:
Vehicle(string n){ NO = n; }
virtual int fee()=0;//计算应收费用
};
/* 请在这里填写答案 */
int main()
{
Car c("",0,0);
Truck t("",0);
Bus b("",0);
int i, repeat, ty, weight, guest;
string no;
cin>>repeat;
for(i=0;i<repeat;i++){
cin>>ty>>no;
switch(ty){
case 1: cin>>guest>>weight; c=Car(no, guest, weight); cout<<no<<' '<<c.fee()<<endl; break;
case 2: cin>>weight; t=Truck(no, weight); cout<<no<<' '<<t.fee()<<endl; break;
case 3: cin>>guest; b=Bus(no, guest); cout<<no<<' '<<b.fee()<<endl; break;
}
}
return 0;
}
输入样例:
4
1 002 20 5
3 009 30
2 003 50
1 010 17 6
输出样例:
002 170
009 90
003 250
010 148
代码补充如下:
class Car:public Vehicle {
public:
int guest,weight;
Car(string NO1,int guest1,int weight1):Vehicle(NO1) {
weight=weight1;
guest=guest1;
}
int fee() {
return (8*guest+2*weight);
}
};
class Truck:public Vehicle {
public:
int weight;
Truck(string NO1,int weight1):Vehicle(NO1) {
weight=weight1;
}
int fee() {
return 5*weight;
}
};
class Bus:public Vehicle {
public:
int guest;
Bus(string no1,int guest1):Vehicle(no1) {
guest=guest1;
}
int fee() {
return 3*guest;
}
};
6-2 抽象类Shape (10 分)
请编写一个抽象类Shape,包括两个纯虚函数,分别为计算面积getArea()和计算周长getPerim()。通过Shape类派生出矩形类Rectangle和圆类Circle,并计算各自的面积和周长。
测试用例具体要求:输入1表示测试矩形类,之后输入矩形长和宽。输入2表示测试圆类,之后输入圆半径。
Shape类定义如下:
class Shape {
public:
virtual double getArea()=0;
virtual double getPerim()=0;
};
裁判测试程序样例:
#include <iostream>
using namespace std;
const double PI=3.14;
class Shape {
public:
virtual double getArea()=0;
virtual double getPerim()=0;
};
/* ------请在这里填写答案 ------*/
int main() {
Shape *p;
int n;
double w,h,r;
scanf("%d",&n);
switch(n) {
case 1: {
cin>>w>>h;
Rectangle rect(w,h);
cout<<"area="<<rect.getArea()<<endl;
cout<<"perim="<<rect.getPerim()<<endl;
break;
}
case 2: {
cin>>r;
Circle c(r);
cout<<"area="<<c.getArea()<<endl;
cout<<"perim="<<c.getPerim()<<endl;
break;
}
}
return 0;
}
输入样例1:
在这里给出一组输入。例如:
1
4 5
输出样例1:
在这里给出相应的输出。例如:
area=20
perim=18
输入样例2:
在这里给出一组输入。例如:
2
5
输出样例2:
在这里给出相应的输出。例如:
area=78.5
perim=31.4
代码补充如下:
class Circle : public Shape {
public:
Circle(double r) {
radius=r;
}
double getArea() {
return (PI * radius * radius);
}
double getPerim() {
return (2*PI * radius);
}
private:
double radius;
};
class Rectangle : public Shape {
public:
Rectangle(double width1,double height1) {
width=width1;
height=height1;
}
double getArea() {
return (width*height);
}
double getPerim() {
return (2*width+2*height);
}
private:
double width;
double height;
};
6-3 虚函数的应用 (15 分)
补充下列代码,使得程序的输出为:
A:3
A:15
B:5
3
15
5
类和函数接口定义:
参见裁判测试程序样例中的类和函数接口。
裁判测试程序样例:
#include <iostream>
using namespace std;
class CMyClassA {
int val;
public:
CMyClassA(int);
void virtual print();
};
CMyClassA::CMyClassA(int arg) {
val = arg;
printf("A:%d\n", val);
}
void CMyClassA::print() {
printf("%d\n", val);
return;
}
/* 在这里填写代码 */
int main(int argc, char** argv) {
CMyClassA a(3), *ptr;
CMyClassB b(5);
ptr = &a;
ptr->print();
a = b;
a.print();
ptr = &b;
ptr->print();
return 0;
}
输入样例:
None
输出样例:
A:3
A:15
B:5
3
15
5
代码补充如下:
class CMyClassB :public CMyClassA {
public:
int val2;
CMyClassB(int x) :CMyClassA(3 * x), val2(x) {
printf("B:%d\n", val2);
}
void print() {
printf("%d\n", val2);
}
};