实验九 多态:虚函数
1 实验目的
(1)学习为什么要使用虚函数;
(2)学习如何声明函数为虚函数;
(3)学习如何声明异类数组(基类指针分别指向不同的派生类对象);
(4)学习如何使用虚函数和异类数组实现多态调用。
2 实验内容
2.1 模拟银行帐户管理程序
问题描述
创建一个银行账户的继承层次,表示银行的所有客户账户。所有的客户都能在他们的银行账户存钱,取钱,并且账户还可以分成更具体的类型。例如,存款账户SavingsAccount依靠存款生利,支票账户CheckingAccount会对每笔交易(即存款或取款)收取费用。
创建一个类层次,以Account作为基类,SavingsAccount和CheckingAccount作为派生类。
基类Account应该包括一个double类型的数据成员balance,表示账户的余额。该类应当提供三个成员函数。成员函数credit可以向当前余额加钱;成员函数debit负责从账户中取钱,并且保证账户不会被透支。如果提取金额大于账户金额,函数将保持balance不变,并打印信息“Debit amount exceeded account balance”;成员函数getBalance则返回当前balance的值。
派生类SavingsAccount不仅继承了基类Account的功能,而且应提供一个附加的double类型数据成员interestRate表示这个账户的比率(百分比)。SavingsAccount的构造函数应接受初始余额值和初始利率值,还应提供一个public成员函数calculateInterest,返回代表账户的利息的一个double值,这个值是balance和interestRate的乘积。类SavingsAccount应继承成员函数credit和debit,不需要重新定义。
派生类CheckingAccount不仅继承基类Account的功能,还提供一个附加的double类型数据成员表示每笔交易的费用。CheckingAccount的构造函数应接受初始余额值和交易费用值。类CheckingAccount需要重新定义成员函数credit和debit,当每笔交易完成时,从balance中减去每笔交易的费用。重新定义这些函数时应分别使用基类Account的相关函数来执行账户余额的更新。CheckingAccount的debit函数只有当钱被成功提取时(即提取金额不超过账户余额时)才应收取交易费。
提示:定义Account的debit函数使它返回一个bool类型值,表示钱是否被成功提取。然后利用该值决定是否需要扣除交易费。如果取款或存款后,账户的余额小于每笔交易的费用,则废弃这次交易,使账户余额恢复到取款或存款之前的值,并打印“Transaction fee exceeded account balance while debiting”或“Transaction fee exceeded account balance while crediting”。
问题要求
要求将每个类的定义和实现分开在不同的文件里,并严格按照上述名称定义成员变量和成员函数,所有类的成员变量均定义为private的。当这个继承层次中的类定义完毕后,编写一个主程序,能够多态地调用不同账户的成员函数。
int main() {
Account *accounts[3];
accounts[0] = new SavingsAccount(100, 3); //余额100元,利息3%
accounts[1] = new CheckingAccount(100, 5); //余额100元,交易费5元
accounts[2] = new CheckingAccount(50, 5); //余额50元,交易费5元
for (int i = 0; i < 3; i++) {
cout << “第” << i + 1 << “次循环的结果:” << endl;
accounts[i]->debit(200); //借款200元
accounts[i]->debit(40);
accounts[i]->credit(50); //存款50元
accounts[i]->debit(49);
accounts[i]->debit(43);
accounts[i]->credit(1);
//将Account指针强制转换为SavingAccount指针
SavingsAccount *derivedPtr =
dynamic_cast<SavingsAccount *>(accounts[i]);
if(derivedPtr != NULL) //如果类型兼容,转换成功
derivedPtr->credit(derivedPtr->calculateInterest());
cout << fixed << setprecision(2); //使用定点数格式,2位小数部分
cout << “账户的余额为:” << accounts[i]->getBalance() << endl;
}
}
程序执行结果
程序执行结果如下:
第1次循环的结果:
Debit amount exceeded account balance
账户的余额为:19.57
第2次循环的结果:
Debit amount exceeded account balance
Transaction fee exceeded account balance while debiting
账户的余额为:42.00
第3次循环的结果:
Debit amount exceeded account balance
Transaction fee exceeded account balance while debiting
Transaction fee exceeded account balance while crediting
账户的余额为:2.00
2.2 继续完善停车场程序
问题描述
请根据题目要求完成简单的停车场管理程序。
1.停车场(Park)有N个停车位(Space),每个停车位可以停放不同类型的汽车(Automobile),包括卡车(Truck)、轿车(Car)、公交车(Bus),但同一时刻一个停车位只能停放0或1辆汽车。如果没有空余停车位,显示提示信息,但不会为车辆安排停车位。
2.程序模拟车辆停车的情况:新来车辆时如果有空位,按顺序为该车分配停车位;车辆开走时,应交纳停车费。
3.停车场可以显示当前停放的车辆的车牌号码,以及当前的全部停车费收入(income)。
4.定义汽车基类Automobile,包括车牌号码(字符串)成员数据。
5.定义派生类Truck、Car、Bus。这些车辆除了拥有车牌号码之外,还各自拥有不同的属性。Truck还包括载重量属性(浮点数,单位吨);Car还拥有品牌属性(字符串),Bus还包括核定载员数量(整型)。
此外,每个派生类中要实现pay()函数,用于显示车辆信息并交纳停车费。其中,Truck收费3元/次,Car收费1元/次,Bus收费2元/次。
问题要求
编写程序,测试上述所要求的各种功能。要求创建新的工程项目ParkManager,添加必要的源文件和头文件,并在程序适当的位置中编写注释。
class Automobile { // 汽车类
public:
void enter(Park *park);
void leave(Park *park);
protected:
virtual void pay(Park &park) = 0; // 向停车场支付停车费,由派生类实现
};
class Park {}; // 停车场类
class Car: public Automobile {
protected:
void pay(Park *park);
}
void Automobile::leave(park *park) {
park.reclaimSpace(this); // 让停车场收回停车位
pay(park); // 向支付支付停车费,由派生类实现本方法
}
void Car::pay(Park *park) {
cout << “车牌号离开停车场,缴纳停车费1元” << endl;
park->getPaid(1);
}
int main() {
cout << “请输入停车位数量:”;
cin >> N;// 输入停车位数量,此处输入2
Park park(N);// 创建一个停车场对象
Automobile *auto1 = new Car(“鲁B-12345”, “奥迪A6”); // 创建轿车对象
Automobile *auto2 = new Truck(“鲁B-23456”, 15); // 创建卡车对象
Automobile *auto3 = new Bus(“鲁B-34567”, 50); // 公交车对象
Automobile *auto4 = new Car(“鲁B-45678”, “宝马320”);// 创建轿车对象
auto1.enter(&park); // car进入停车场,分配停车位
auto2.enter(&park); // truck进入停车场,分配车位
auto1.leave(&park); // car离开停车场,缴纳停车费
auto3.enter(&park); // bus进入停车场,分配车位
/* 显示当前停放的车辆的车牌号码,以及当前的全部停车费收入*/
park.showInfo();
auto4.enter(&park); // car进入停车场,分配停车位
// car进入停车场,分配停车位。因为没有空余停车位,所以无法分配
auto3.leave(&park); // bus离开停车场,缴纳停车费
auto2.leave(&park); // truck离开停车场,缴纳停车费
/* 显示当前停放的车辆的车牌号码,以及当前的全部停车费收入*/
park.showInfo();
return 0;
}
程序执行结果
程序执行结果如下:
请输入停车位数量:2
鲁B-12345进入停车场,分配停车位
鲁B-23456进入停车场,分配停车位
鲁B-12345离开停车场,缴纳停车费1元
鲁B-34567进入停车场,分配停车位
停车场目前停放了2辆汽车:鲁B-23456,鲁B-34567,共收入1元停车费
无法为鲁B-45678分配停车位
鲁B-34567离开停车场,缴纳停车费2元
鲁B-23456离开停车场,缴纳停车费3元
停车场目前停放了0辆汽车,共收入6元停车费
3 代码
2.1
Accont.h
#ifndef ACCOUNT_H
#define ACCOUNT_H
class Account {
private:
double balance;
public:
Account(double a);
virtual void credit(double add);
virtual bool debit(double qu);
double getBalance();
};
#endif
Account.cpp
#include"Account.h"
#include<iostream>
Account::Account(double a) { balance = a; }
void Account::credit(double add) {
balance += add;
}
bool Account::debit(double qu) {
bool ok;
if (balance - qu >= 0) {
balance -= qu;
ok = true;
}
else {
std::cout << "Debit amount exceeded account balance" << std::endl;
ok = false;
}
return ok;
}
double Account::getBalance() { return balance; }
CheckingAccount.h
#ifndef CHECKINGACCOUNT_H
#define CHECKINGACCOUNT_H
#include"Account.h"
class CheckingAccount :public Account {
private:
double fee;
public:
CheckingAccount(int a, int b);
void credit(double add);
bool debit(double qu);
};
#endif
CheckingAccount.cpp
#include"CheckingAccount.h"
#include<iostream>
using namespace std;
CheckingAccount::CheckingAccount(int a, int b) :Account(a), fee(b) {}
void CheckingAccount::credit(double add) {
Account::credit(add);
if (getBalance() < fee) {
std::cout << "Transaction fee exceeded account balance while crediting" << endl;
Account::debit(add);
}
else {
Account::debit(fee);
}
}
bool CheckingAccount:: debit(double qu) {
if (Account::debit(qu)) {
if (getBalance() < fee) {
std::cout << "Transaction fee exceeded account balance while debiting" << endl;
Account::credit(qu);
}
else {
Account::debit(fee);
}
}
return true;
}
SavingAccount.h
#ifndef SAVINGACCOUNT_H
#define SAVINGACCOUNT_H
#include"Account.h"
class SavingsAccount :public Account {
private:
double interestRate;
public:
SavingsAccount(int a, int b);
double calculateInterest();
};
#endif
SavingAccount.cpp
#include"SavingsAccount.h"
SavingsAccount::SavingsAccount(int a, int b) :Account(a), interestRate(b * 1.0 / 100) {}
double SavingsAccount::calculateInterest() { return interestRate * getBalance(); }
源.cpp
#include <iostream>
#include <string>
#include <cstring>
#include<iomanip>
#include"Account.h"
#include"CheckingAccount.h"
#include"SavingsAccount.h"
using namespace std;
int main()
{
Account* accounts[3];
accounts[0] = new SavingsAccount(100, 3); //余额100元,利息3%
accounts[1] = new CheckingAccount(100, 5); //余额100元,交易费5元
accounts[2] = new CheckingAccount(50, 5); //余额50元,交易费5元
for (int i = 0; i < 3; i++)
{
cout << "第" << i + 1 << "次循环的结果:" << endl;
accounts[i]->debit(200); //借款200元
accounts[i]->debit(40);
accounts[i]->credit(50); //存款50元
accounts[i]->debit(49);
accounts[i]->debit(43);
accounts[i]->credit(1);
//将Account指针强制转换为SavingAccount指针
SavingsAccount* derivedPtr =
dynamic_cast<SavingsAccount*>(accounts[i]);
if (derivedPtr != NULL) //如果类型兼容,转换成功
derivedPtr->credit(derivedPtr->calculateInterest());
cout << fixed << setprecision(2); //使用定点数格式,2位小数部分
cout << "账户的余额为:" << accounts[i]->getBalance() << endl;
}
}
2.2
#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>
#include <iomanip>
#include <vector>
#include <map>
using namespace std;
int N;
class Park;
class Automobile {
public:
int cost;
Automobile(string num) : car_num(num) {}
string get_car_num();
void enter(Park *park);
void leave(Park *park);
protected:
virtual void pay(Park &park) = 0;
private:
string car_num;
};
string Automobile::get_car_num() { return car_num; }
class Truck : public Automobile {
public:
Truck(string name, double w) : Automobile(name), weight(w) { cost = 2; }
protected:
void pay(Park &park);
private:
double weight;
};
class Car : public Automobile {
public:
Car(string name, string br) : Automobile(name), brand(br) { cost = 1; }
protected:
void pay(Park &park);
private:
string brand;
};
class Bus : public Automobile {
public:
Bus(string name, int nu) : Automobile(name), num_personnel(nu) { cost = 3; }
protected:
void pay(Park &park);
private:
int num_personnel;
};
class Park {
public:
Park(int n) {
N = n;
spaces = new Automobile *[N];
i = 0;
income = 0;
}
void showInfo() {
cout << "停车场目前停放了" << i << "辆汽车:";
for (int j = 0; j < i; j++) { cout << spaces[j]->get_car_num() << ","; }
cout << "共收入" << income << "元停车费\n";
}
void stop(Automobile *car1) {
if (N > i) {
spaces[i++] = car1;
cout << car1->get_car_num() << "进入停车场,分配停车位\n";
} else { cout << "无法为" << car1->get_car_num() << "分配停车位\n"; }
}
void go(Automobile *car1) {
int j;
for (j = 0; j < i; j++) {
if (spaces[j]->get_car_num() == car1->get_car_num()) {
i--;
spaces[j] = NULL;
for (int k = j; k <= i - 1; k++) { spaces[k] = spaces[k + 1]; }
spaces[N - 1] = NULL;
break;
}
}
}
void getPaid(int x) { income += x; }
~Park() { delete[] spaces; }
private:
Automobile **spaces;
int i;
int N;
int income;
};
void Automobile::enter(Park *park) { park->stop(this); }
void Automobile::leave(Park *park) {
park->go(
this); // 让停车场收回停车位
pay(*park);
}
void Truck::pay(Park &park) {
cout << "车牌号离开停车场,缴纳停车费2元" << endl;
park.getPaid(2);
}
void Car::pay(Park &park) {
cout << "车牌号离开停车场,缴纳停车费1元" << endl;
park.getPaid(1);
}
void Bus::pay(Park &park) {
cout << "车牌号离开停车场,缴纳停车费3元" << endl;
park.getPaid(3);
}
int main() {
cout << "请输入停车位数量:";
cin>> N;
Park park(N); // 创建一个停车场对象
Automobile *auto1 = new Car("鲁B-12345", "奥迪A6"); // 创建轿车对象
Automobile *auto2 = new Truck("鲁B-23456", 15); // 创建卡车对象
Automobile *auto3 = new Bus("鲁B-34567", 50); //
Automobile *auto4 = new Car("鲁B-45678", "宝马320"); //
auto1->enter(&park); // car进入停车场,分配停车位
auto2->enter(&park); // truck进入停车场,分配车位
auto1->leave(&park); // car离开停车场,缴纳停车费
auto3->enter(&park); // bus进入停车场,分配车位 /* 显示当前停放的车辆的车牌号码,以及当前的全部停车费收入*/
park.showInfo();
auto4->enter(&park); // car进入停车场,分配停车位 // car进入停车场,分配停车位。因为没有空余停车位,所以无法分配
auto3->leave(&park); // bus离开停车场,缴纳停车费
auto2->leave(&park); // truck离开停车场,缴纳停车费 /* 显示当前停放的车辆的车牌号码,以及当前的全部停车费收入*/
park.showInfo();
return 0;
}