实验5 继承和多态
1. 实验任务1
验证性实验
知识点:
- 抽象类
// 发行/出版物类:Publisher (抽象类) class Publisher { public: Publisher(const string& s = ""); // 构造函数 //构造函数接受一个string类型的参数s,默认值为空字符串 public: virtual void publish() const = 0; // 纯虚函数,作为接口继承 virtual void use() const = 0; // 纯虚函数,作为接口继承 protected: string name; // 发行/出版物名称 }; Publisher::Publisher(const string& s) : name{ s } { }
- 派生类
// 电影类: Film class Film : public Publisher { public: Film(const string& s = "", const string& d = ""); // 构造函数 public: void publish() const override; // 接口 void use() const override; // 接口 private: string director; // 导演 }; Film::Film(const string& s, const string& d) : Publisher{ s }, director{ d } { }//Publisher{ s }调用基类Publisher的构造函数,并将参数s传递给基类构造函数,用于初始化基类中的成员变量name void Film::publish() const { cout << "Publishing film: <" << name << "> directed by " << director << endl; } void Film::use() const { cout << "Watching film: " << name << " directed by " << director << endl; }
- 调用
vector<Publisher*> v; //创建了一个名为v的vector,它存储指向 Publisher 类对象的指针 ··· for (auto& ptr : v) { ptr->publish();//通过指针调用当前指向的对象的 publish() 方法 ptr->use(); cout << endl; }
2. 实验任务2
验证性实验
知识点:
- 运算符重载
class Book { public: ··· friend ostream& operator<<(ostream& out, const Book& book); //接受一个ostream对象的引用out和一个const Book对象的引用book,并返回一个ostream对象的引用 private: ··· }; // 友元实现 ostream& operator<<(ostream& out, const Book& book) { out << left; out << setw(15) << "书名:" << book.name << endl << setw(15) << "作者:" << book.author << endl << setw(15) << "译者:" << book.translator << endl << setw(15) << "ISBN:" << book.isbn << endl << setw(15) << "定价:" << book.price; return out; }
值得注意的是,这里只可以将运算符<<重载为类的友元函数。将<<理解为二目运算符,在主函数中调用为
std::cout<<book;
;如果将<<重载为类Book的成员函数,则调用应为book<<std::cout;
,不符合习惯。 - 组合类
class Book { ··· }; class BookSale { public: BookSale(const Book& b, float price, int amount); ··· private: Book rb; ··· }; // 成员函数实现 BookSale::BookSale(const Book& b, ···) : rb{ b }, ···{ ··· }
3. 实验任务3
UML类图:
pets.hpp源代码:
#pragma once
#include <iostream>
#include <string>
class MachinePets {
public:
MachinePets(const std::string& s):nickname(s){}
std::string get_nickname() const { return nickname; }
virtual std::string talk() = 0;
protected:
std::string nickname;
};
class PetCats :public MachinePets {
public:
PetCats(const std::string& s):MachinePets(s){}
std::string talk() override { return "miao wu~"; }
};
class PetDogs :public MachinePets {
public:
PetDogs(const std::string& s) :MachinePets(s) {}
std::string talk() override { return "wang wang~"; }
};
task3.cpp源代码:
#include <iostream>
#include <vector>
#include "pets.hpp"
void test() {
using namespace std;
vector<MachinePets *> pets;
pets.push_back(new PetCats("miku"));
pets.push_back(new PetDogs("da huang"));
for(auto &ptr: pets)
cout <<ptr->get_nickname() << " says " << ptr->talk() << endl;
}
int main() {
test();
}
运行截图:
4. 实验任务4
film.hpp源代码:
#pragma once
#include <iostream>
#include <string>
#include <iomanip>
class Film {
public:
Film(){}
int get_year() const { return this->year; }
friend std::istream& operator>>(std::istream& in, Film& film);
friend std::ostream& operator<<(std::ostream& out, const Film& film);
protected:
std::string name; //片名
std::string director; //导演
std::string nation; //国家
int year; //年份
};
std::istream& operator>>(std::istream& in, Film& film) {
std::cout << "录入片名:";
in >> film.name;
std::cout << "录入导演:";
in >> film.director;
std::cout << "录入制片国家/地区:";
in >> film.nation;
std::cout << "录入上映年份:";
in >> film.year;
return in;
}
std::ostream& operator<<(std::ostream& out, const Film& film) {
out << std::left << std::setw(15) << film.name << std::setw(15) << film.director << std::setw(15) << film.nation << std::setw(15) << film.year;
return out;
}
bool compare_by_year(const Film& f1, const Film& f2) {
return f1.get_year() < f2.get_year();
}
task4.cpp源代码:
#include "film.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
void test() {
using namespace std;
int n;
cout << "输入电影数目: ";
cin >> n;
cout << "录入" << n << "部影片信息" << endl;
vector<Film> film_lst;
for (int i = 0; i < n; ++i) {
Film f;
cout << string(20, '-') << "第" << i + 1 << "部影片录入" << string(20, '-') << endl;
cin >> f;
film_lst.push_back(f);
}
// 按发行年份升序排序
sort(film_lst.begin(), film_lst.end(), compare_by_year);
cout << string(20, '=') + "电影信息(按发行年份)" + string(20, '=') << endl;
for (auto& f : film_lst)
cout << f << endl;
}
int main() {
test();
}
运行截图:
5. 实验任务5
Complex.hpp源代码:
#pragma once
#include <iostream>
#include <string>
template<typename T>
class Complex {
public:
//构造函数
Complex(T r = 0, T i = 0): real(r), imag(i){}
Complex(const Complex& other) :real(other.real), imag(other.imag) {}
//成员函数
T get_real() const { return real; }
T get_imag() const { return imag; }
//运算符重载
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;//this是指针类型Complex*,而返回类型是Complex的引用Complex&,所以要*this,取值
}
bool operator==(const Complex& other) const {
return real == other.real && imag == other.imag;
}
//友元
template<typename T>//在类模板中,友元函数的声明通常也需要加上模板参数的声明!!!!!!!!!!!!
friend std::istream& operator>>(std::istream& in, Complex<T>& C);
template<typename T>
friend std::ostream& operator<<(std::ostream& out, const Complex<T>& C);
private:
T real;
T imag;
};
template <typename T>
std::istream& operator>>(std::istream& in, Complex<T>& C) {
in >> C.real;
in >> C.imag;
return in;
}
template <typename T>
std::ostream& operator<<(std::ostream& out, const Complex<T>& C) {
out << C.real << (C.imag >= 0 ? " + " : " - ") << abs(C.imag) << "i";
return out;
}
task5.cpp源代码:
#include "Complex.hpp"
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
using std::boolalpha;//boolalpha是一个 I/O 操纵符,用于改变bool类型数据的输出格式
void test1() {
Complex<int> c1(2, -5), c2(c1);
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
cout << "c1 + c2 = " << c1 + c2 << endl;
c1 += c2;
cout << "c1 = " << c1 << endl;
cout << boolalpha << (c1 == c2) << endl;//使用 boolalpha后,布尔值将以 “true” 或 “false” 的文本形式输出,而不是默认的数字形式
}
void test2() {
Complex<double> c1, c2;
cout << "Enter c1 and c2: ";
cin >> c1 >> c2;
cout << "c1 = " << c1 << endl;
cout << "c2 = " << c2 << endl;
cout << "c1.real = " << c1.get_real() << endl;
cout << "c1.imag = " << c1.get_imag() << endl;
}
int main() {
cout << "自定义类模板Complex测试1: " << endl;
test1();
cout << endl;
cout << "自定义类模板Complex测试2: " << endl;
test2();
}
运行截图:
6. 实验任务6
date.h源代码:
#pragma once
#ifndef __DATE_H__
#define __DATE_H__
class Date { //日期类
private:
int year; //年
int month; //月
int day; //日
int totalDays; //该日期是从公元元年1月1日开始的第几天
public:
Date(int year, int month, int day); //用年、月、日构造日期
int getYear() const { return year; }
int getMonth() const { return month; }
int getDay() const { return day; }
int getMaxDay() const; //获得当月有多少天
bool isLeapYear() const { //判断当年是否为闰年
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
void show() const; //输出当前日期
//计算两个日期之间差多少天
int operator - (const Date& date) const {
return totalDays - date.totalDays;
}
};
#endif //__DATE_H__
date.cpp源代码:
//date.cpp
#include "date.h"
#include <iostream>
#include <cstdlib>
using namespace std;
namespace { //namespace使下面的定义只在当前文件中有效
//存储平年中某个月1日之前有多少天,为便于getMaxDay函数的实现,该数组多出一项
const int DAYS_BEFORE_MONTH[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
}
Date::Date(int year, int month, int day) : year(year), month(month), day(day) {
if (day <= 0 || day > getMaxDay()) {
cout << "Invalid date: ";
show();
cout << endl;
exit(1);
}
int years = year - 1;
totalDays = years * 365 + years / 4 - years / 100 + years / 400
+ DAYS_BEFORE_MONTH[month - 1] + day;
if (isLeapYear() && month > 2) totalDays++;
}
int Date::getMaxDay() const {
if (isLeapYear() && month == 2)
return 29;
else
return DAYS_BEFORE_MONTH[month] - DAYS_BEFORE_MONTH[month - 1];
}
void Date::show() const {
cout << getYear() << "-" << getMonth() << "-" << getDay();
}
accumulator.h源代码:
#pragma once
#ifndef __ACCUMULATOR_H__
#define __ACCUMULATOR_H__
#include "date.h"
class Accumulator { //将某个数值按日累加
private:
Date lastDate; //上次变更数值的时期
double value; //数值的当前值
double sum; //数值按日累加之和
public:
//构造函数,date为开始累加的日期,value为初始值
Accumulator(const Date& date, double value)
: lastDate(date), value(value), sum(0) { }
//获得到日期date的累加结果
double getSum(const Date& date) const {
return sum + value * (date - lastDate);
}
//在date将数值变更为value
void change(const Date& date, double value) {
sum = getSum(date);
lastDate = date;
this->value = value;
}
//初始化,将日期变为date,数值变为value,累加器清零
void reset(const Date& date, double value) {
lastDate = date;
this->value = value;
sum = 0;
}
};
#endif //__ACCUMULATOR_H__
account.h源代码:
#pragma once
#ifndef __ACCOUNT_H__
#define __ACCOUNT_H__
#include "date.h"
#include "accumulator.h"
#include <string>
class Account { //账户类
private:
std::string id; //帐号
double balance; //余额
static double total; //所有账户的总金额
protected:
//供派生类调用的构造函数,id为账户
Account(const Date& date, const std::string& id);
//记录一笔帐,date为日期,amount为金额,desc为说明
void record(const Date& date, double amount, const std::string& desc);
//报告错误信息
void error(const std::string& msg) const;
public:
const std::string& getId() const { return id; }
double getBalance() const { return balance; }
static double getTotal() { return total; }
//存入现金,date为日期,amount为金额,desc为款项说明
virtual void deposit(const Date& date, double amount, const std::string& desc) = 0;
//取出现金,date为日期,amount为金额,desc为款项说明
virtual void withdraw(const Date& date, double amount, const std::string& desc) = 0;
//结算(计算利息、年费等),每月结算一次,date为结算日期
virtual void settle(const Date& date) = 0;
//显示账户信息
virtual void show() const;
};
class SavingsAccount : public Account { //储蓄账户类
private:
Accumulator acc; //辅助计算利息的累加器
double rate; //存款的年利率
public:
//构造函数
SavingsAccount(const Date& date, const std::string& id, double rate);
double getRate() const { return rate; }
virtual void deposit(const Date& date, double amount, const std::string& desc);
virtual void withdraw(const Date& date, double amount, const std::string& desc);
virtual void settle(const Date& date);
};
class CreditAccount : public Account { //信用账户类
private:
Accumulator acc; //辅助计算利息的累加器
double credit; //信用额度
double rate; //欠款的日利率
double fee; //信用卡年费
double getDebt() const { //获得欠款额
double balance = getBalance();
return (balance < 0 ? balance : 0);
}
public:
//构造函数
CreditAccount(const Date& date, const std::string& id, double credit, double rate, double fee);
double getCredit() const { return credit; }
double getRate() const { return rate; }
double getFee() const { return fee; }
double getAvailableCredit() const { //获得可用信用
if (getBalance() < 0)
return credit + getBalance();
else
return credit;
}
virtual void deposit(const Date& date, double amount, const std::string& desc);
virtual void withdraw(const Date& date, double amount, const std::string& desc);
virtual void settle(const Date& date);
virtual void show() const;
};
#endif //__ACCOUNT_H__
account.cpp源代码:
#include "account.h"
#include <cmath>
#include <iostream>
using namespace std;
double Account::total = 0;
//Account类的实现
Account::Account(const Date& date, const string& id)
: id(id), balance(0) {
date.show();
cout << "\t#" << id << " created" << endl;
}
void Account::record(const Date& date, double amount, const string& desc) {
amount = floor(amount * 100 + 0.5) / 100; //保留小数点后两位
balance += amount;
total += amount;
date.show();
cout << "\t#" << id << "\t" << amount << "\t" << balance << "\t" << desc << endl;
}
void Account::show() const {
cout << id << "\tBalance: " << balance;
}
void Account::error(const string& msg) const {
cout << "Error(#" << id << "): " << msg << endl;
}
//SavingsAccount类相关成员函数的实现
SavingsAccount::SavingsAccount(const Date& date, const string& id, double rate)
: Account(date, id), rate(rate), acc(date, 0) { }
void SavingsAccount::deposit(const Date& date, double amount, const string& desc) {
record(date, amount, desc);
acc.change(date, getBalance());
}
void SavingsAccount::withdraw(const Date& date, double amount, const string& desc) {
if (amount > getBalance()) {
error("not enough money");
}
else {
record(date, -amount, desc);
acc.change(date, getBalance());
}
}
void SavingsAccount::settle(const Date& date) {
if (date.getMonth() == 1) { //每年的一月计算一次利息
double interest = acc.getSum(date) * rate
/ (date - Date(date.getYear() - 1, 1, 1));
if (interest != 0)
record(date, interest, "interest");
acc.reset(date, getBalance());
}
}
//CreditAccount类相关成员函数的实现
CreditAccount::CreditAccount(const Date& date, const string& id, double credit, double rate, double fee)
: Account(date, id), credit(credit), rate(rate), fee(fee), acc(date, 0) { }
void CreditAccount::deposit(const Date& date, double amount, const string& desc) {
record(date, amount, desc);
acc.change(date, getDebt());
}
void CreditAccount::withdraw(const Date& date, double amount, const string& desc) {
if (amount - getBalance() > credit) {
error("not enough credit");
}
else {
record(date, -amount, desc);
acc.change(date, getDebt());
}
}
void CreditAccount::settle(const Date& date) {
double interest = acc.getSum(date) * rate;
if (interest != 0)
record(date, interest, "interest");
if (date.getMonth() == 1)
record(date, -fee, "annual fee");
acc.reset(date, getDebt());
}
void CreditAccount::show() const {
Account::show();
cout << "\tAvailable credit:" << getAvailableCredit();
}
8_8.cpp源代码:
//8_8.cpp
#include "account.h"
#include <iostream>
using namespace std;
int main() {
Date date(2008, 11, 1); //起始日期
//建立几个账户
SavingsAccount sa1(date, "S3755217", 0.015);
SavingsAccount sa2(date, "02342342", 0.015);
CreditAccount ca(date, "C5392394", 10000, 0.0005, 50);
Account* accounts[] = { &sa1, &sa2, &ca };
const int n = sizeof(accounts) / sizeof(Account*); //账户总数
cout << "(d)deposit (w)withdraw (s)show (c)change day (n)next month (e)exit" << endl;
char cmd;
do {
//显示日期和总金额
date.show();
cout << "\tTotal: " << Account::getTotal() << "\tcommand> ";
int index, day;
double amount;
string desc;
cin >> cmd;
switch (cmd) {
case 'd': //存入现金
cin >> index >> amount;
getline(cin, desc);
accounts[index]->deposit(date, amount, desc);
break;
case 'w': //取出现金
cin >> index >> amount;
getline(cin, desc);
accounts[index]->withdraw(date, amount, desc);
break;
case 's': //查询各账户信息
for (int i = 0; i < n; i++) {
cout << "[" << i << "] ";
accounts[i]->show();
cout << endl;
}
break;
case 'c': //改变日期
cin >> day;
if (day < date.getDay())
cout << "You cannot specify a previous day";
else if (day > date.getMaxDay())
cout << "Invalid day";
else
date = Date(date.getYear(), date.getMonth(), day);
break;
case 'n': //进入下个月
if (date.getMonth() == 12)
date = Date(date.getYear() + 1, 1, 1);
else
date = Date(date.getYear(), date.getMonth() + 1, 1);
for (int i = 0; i < n; i++)
accounts[i]->settle(date);
break;
}
} while (cmd != 'e');
return 0;
}
运行截图:
总结:与7-10相比,本次将基类Account声明为抽象类,其中的一些公共操作被声明为虚函数和纯虚函数,实现了运行时多态;其次,本次还新增了运算符重载,使计算天数差等变得更为方便;本次也在主函数中新增了操作(存入现金、提取现金等)选择,方便用户自主输入选择相应操作,更加符合实用性。
更多改进思路:应允许用户自主新建账户,可以通过new来动态新建对象,这样,用户也可以使用delete来删除账户信息;更多的,可以思考如何进行保存或信息导入,方便程序关闭后,用户仍可以继续上一次的操作。
7. 实验任务7
container.h源代码:
#pragma once
//物品栏
#ifndef _CONTAINER // Conditional compilation
#define _CONTAINER
class container {
protected:
int numOfHeal; // number of heal 治疗物品的数量
int numOfMW; // number of magic water 魔法药水的数量
public:
container(); // constuctor
void set(int heal_n, int mw_n); // set the items numbers
int nOfHeal(); // get the number of heal
int nOfMW(); // get the number of magic water
void display(); // display the items;
bool useHeal(); // use heal
bool useMW(); // use magic water
};
#endif
player.h源代码:
#pragma once
//玩家
#ifndef _PLAYER
#define _PLAYER
#include <iomanip> // use for setting field width
#include <time.h> // use for generating random factor
#include "container.h"
enum job{sw,ar,mg}; //枚举类型 定义3个职业 剑士、弓箭手、法师
class player{
friend void showinfo(player& p1, player& p2);
friend class swordsman;
protected:
int HP, HPmax, MP, MPmax, AP, DP, speed, EXP, LV;//当前生命值、最大生命值、当前魔法值、最大魔法值、攻击力、防御力、速度、经验值、等级
string name; //角色名称
job role; //职业
container bag; //物品
public:
virtual bool attack(player& p) = 0; // normal attack
virtual bool specialatt(player& p) = 0; // special attack
virtual void isLevelUp() = 0; // level up judgement
void reFill(); // character's HP and MP resume
bool death(); // report whether character is dead
void isDead(); // check whether character is dead
bool useHeal(); // consume heal, irrelevant to job
bool useMW(); // consume magic water, irrelevant to job
void transfer(player& p); // possess opponent's items after victory
void showRole(); // display character's job
private:
bool playerdeath; // whether character is dead, doesn't need to be accessed or inherited
};
#endif
swordsman.h源代码:
#pragma once
//剑士派生类
#include "player.h"
#include "container.h"
class swordsman : public player{
public:
swordsman(int lv_in = 1, string name_in = "Not Given");
void isLevelUp();//纯虚函数重写
bool attack(player& p);//纯虚函数重写
bool specialatt(player& p);//纯虚函数重写
void AI(player& p); //额外的成员函数
};
container.cpp源代码:
#include <iostream>
#include <string>
using namespace std;
#include "container.h"
container::container()
{
set(0, 0);
}
// set the item numbers
void container::set(int heal_n, int mw_n)
{
numOfHeal = heal_n;
numOfMW = mw_n;
}
// get the number of heal
int container::nOfHeal()
{
return numOfHeal;
}
// get the number of magic water
int container::nOfMW()
{
return numOfMW;
}
// display the items;
void container::display()
{
cout << "Your bag contains: " << endl;
cout << "Heal(HP+100): " << numOfHeal << endl;
cout << "Magic Water (MP+80): " << numOfMW << endl;
}
// use heal
bool container::useHeal()
{
numOfHeal--;
return 1; // use heal successfully
}
// use magic water
bool container::useMW()
{
numOfMW--;
return 1; // use magic water successfully
}
main.cpp源代码:
#include <iostream>
#include <string>
using namespace std;
#include "swordsman.h"
int main()
{
string tempName;
bool success = 0; // flag for storing whether operation is successful
cout << "Please input player's name: ";
cin >> tempName; // get player's name from keyboard input
player* human; // use pointer of base class, convenience for polymorphism
int tempJob; // temp choice for job selection
human = nullptr;
do
{
cout << "Please choose a job: 1 Swordsman, 2 Archer, 3 Mage" << endl;
cin >> tempJob;
system("cls"); // clear the screen
switch (tempJob)
{
case 1:
human = new swordsman(1, tempName); // create the character with user inputted name and job
success = 1; // operation succeed
break;
default:
break; // In this case, success=0, character creation failed
}
} while (success != 1); // so the loop will ask user to re-create a character
int tempCom; // temp command inputted by user
int nOpp = 0; // the Nth opponent
for (int i = 1; nOpp < 5; i += 2) // i is opponent's level
{
nOpp++;
system("cls");
cout << "STAGE" << nOpp << endl;
cout << "Your opponent, a Level " << i << " Swordsman." << endl;
system("pause");
swordsman enemy(i, "Warrior"); // Initialise an opponent, level i, name "Junior"
human->reFill(); // get HP/MP refill before start fight
while (!human->death() && !enemy.death()) // no died
{
success = 0;
while (success != 1)
{
showinfo(*human, enemy); // show fighter's information
cout << "Please give command: " << endl;
cout << "1 Attack; 2 Special Attack; 3 Use Heal; 4 Use Magic Water; 0 Exit Game" << endl;
cin >> tempCom;
switch (tempCom)
{
case 0:
cout << "Are you sure to exit? Y/N" << endl;
char temp;
cin >> temp;
if (temp == 'Y' || temp == 'y')
return 0;
else
break;
case 1:
success = human->attack(enemy);
human->isLevelUp();
enemy.isDead();
break;
case 2:
success = human->specialatt(enemy);
human->isLevelUp();
enemy.isDead();
break;
case 3:
success = human->useHeal();
break;
case 4:
success = human->useMW();
break;
default:
break;
}
}
if (!enemy.death()) // If AI still alive
enemy.AI(*human);
else // AI died
{
cout << "YOU WIN" << endl;
human->transfer(enemy); // player got all AI's items
}
if (human->death())
{
system("cls");
cout << endl
<< setw(50) << "GAME OVER" << endl;
cout << "菜就多练!" << endl;
system("pause");
return 0;
}
}
}
cout << "有点东西" << endl;
system("cls");
cout << "Congratulations! You defeated all opponents!!" << endl;
system("pause");
return 0;
}
player.cpp源代码:
#include <iostream>
#include <string>
using namespace std;
#include "player.h"
void player::reFill()
{
HP = HPmax; // HP and MP fully recovered
MP = MPmax;
}
// report whether character is dead
bool player::death()
{
return playerdeath;
}
// check whether character is dead
void player::isDead()
{
if (HP <= 0) // HP less than 0, character is dead
{
std::cout << name << " is Dead." << endl;
system("pause");
playerdeath = 1; // give the label of death value 1
}
}
// consume heal, irrelevant to job
bool player::useHeal()
{
if (bag.nOfHeal() > 0)
{
HP = HP + 100;
if (HP > HPmax) // HP cannot be larger than maximum value
HP = HPmax; // so assign it to HPmax, if necessary
std::cout << name << " used Heal, HP increased by 100." << endl;
bag.useHeal(); // use heal
system("pause");
return 1; // usage of heal succeed
}
else // If no more heal in bag, cannot use
{
std::cout << "Sorry, you don't have heal to use." << endl;
system("pause");
return 0; // usage of heal failed
}
}
// consume magic water, irrelevant to job
bool player::useMW()
{
if (bag.nOfMW() > 0)
{
MP = MP + 100;
if (MP > MPmax)
MP = MPmax;
std::cout << name << " used Magic Water, MP increased by 100." << endl;
bag.useMW();
system("pause");
return 1; // usage of magic water succeed
}
else
{
std::cout << "Sorry, you don't have magic water to use." << endl;
system("pause");
return 0; // usage of magic water failed
}
}
// possess opponent's items after victory
void player::transfer(player& p)
{
std::cout << name << " got" << p.bag.nOfHeal() << " Heal, and " << p.bag.nOfMW() << " Magic Water." << endl;
system("pause");
// set the character's bag, get opponent's items
bag.set(bag.nOfHeal() + p.bag.nOfHeal(), bag.nOfMW() + p.bag.nOfMW());
}
// display character's job
void player::showRole()
{
switch (role)
{
case sw:
std::cout << "Swordsman";
break;
case ar:
std::cout << "Archer";
break;
case mg:
std::cout << "Mage";
break;
default:
break;
}
}
// display character's job
void showinfo(player& p1, player& p2) {
system("cls");
cout << "##############################################################" << endl;
cout << "# Player" << setw(10) << p1.name << " LV. " << setw(3) << p1.LV
<< " # Opponent" << setw(10) << p2.name << " LV. " << setw(3) << p2.LV << " #" << endl;
cout << "# HP " << setw(3) << (p1.HP <= 999 ? p1.HP : 999) << '/' << setw(3) << (p1.HPmax <= 999 ? p1.HPmax : 999)
<< " | MP " << setw(3) << (p1.MP <= 999 ? p1.MP : 999) << '/' << setw(3) << (p1.MPmax <= 999 ? p1.MPmax : 999)
<< " # HP " << setw(3) << (p2.HP <= 999 ? p2.HP : 999) << '/' << setw(3) << (p2.HPmax <= 999 ? p2.HPmax : 999)
<< " | MP " << setw(3) << (p2.MP <= 999 ? p2.MP : 999) << '/' << setw(3) << (p2.MPmax <= 999 ? p2.MPmax : 999) << " #" << endl;
cout << "# AP " << setw(3) << (p1.AP <= 999 ? p1.AP : 999)
<< " | DP " << setw(3) << (p1.DP <= 999 ? p1.DP : 999)
<< " | speed " << setw(3) << (p1.speed <= 999 ? p1.speed : 999)
<< " # AP " << setw(3) << (p2.AP <= 999 ? p2.AP : 999)
<< " | DP " << setw(3) << (p2.DP <= 999 ? p2.DP : 999)
<< " | speed " << setw(3) << (p2.speed <= 999 ? p2.speed : 999) << " #" << endl;
cout << "# EXP" << setw(7) << p1.EXP << " Job: " << setw(7);
p1.showRole();
cout << " # EXP" << setw(7) << p2.EXP << " Job: " << setw(7);
p2.showRole();
cout << " #" << endl;
cout << "--------------------------------------------------------------" << endl;
p1.bag.display();
cout << "##############################################################" << endl;
}
swordsman.cpp源代码:
#include <iostream>
#include <string>
using namespace std;
#include "swordsman.h"
swordsman::swordsman(int lv_in, string name_in)
{
role = sw; // enumerate type of job
LV = lv_in;
name = name_in;
// Initialising the character's properties, based on his level
HPmax = 150 + 8 * (LV - 1); // HP increases 8 point2 per level
HP = HPmax;
MPmax = 75 + 2 * (LV - 1); // MP increases 2 points per level
MP = MPmax;
AP = 25 + 4 * (LV - 1); // AP increases 4 points per level
DP = 25 + 4 * (LV - 1); // DP increases 4 points per level
speed = 25 + 2 * (LV - 1); // speed increases 2 points per level
playerdeath = 0;
EXP = LV * LV * 75;
bag.set(lv_in, lv_in);
}
void swordsman::isLevelUp()
{
if (EXP >= LV * LV * 75)
{
LV++;
AP += 4;
DP += 4;
HPmax += 8;
MPmax += 2;
speed += 2;
cout << name << " Level UP!" << endl;
cout << "HP improved 8 points to " << HPmax << endl;
cout << "MP improved 2 points to " << MPmax << endl;
cout << "Speed improved 2 points to " << speed << endl;
cout << "AP improved 4 points to " << AP << endl;
cout << "DP improved 5 points to " << DP << endl;
system("pause");
isLevelUp(); // recursively call this function, so the character can level up multiple times if got enough exp
}
}
bool swordsman::attack(player& p)
{
double HPtemp = 0; // opponent's HP decrement
double EXPtemp = 0; // player obtained exp
double hit = 1; // attach factor, probably give critical attack
srand((unsigned)time(NULL)); // generating random seed based on system time
// If speed greater than opponent, you have some possibility to do double attack
if ((speed > p.speed) && (rand() % 100 < (speed - p.speed))) // rand()%100 means generates a number no greater than 100
{
HPtemp = (int)((1.0 * AP / p.DP) * AP * 5 / (rand() % 4 + 10)); // opponent's HP decrement calculated based their AP/DP, and uncertain chance
cout << name << "'s quick strike hit " << p.name << ", " << p.name << "'s HP decreased " << HPtemp << endl;
p.HP = int(p.HP - HPtemp);
EXPtemp = (int)(HPtemp * 1.2);
}
// If speed smaller than opponent, the opponent has possibility to evade
if ((speed < p.speed) && (rand() % 50 < 1))
{
cout << name << "'s attack has been evaded by " << p.name << endl;
system("pause");
return 1;
}
// 10% chance give critical attack
if (rand() % 100 <= 10)
{
hit = 1.5;
cout << "Critical attack: ";
}
// Normal attack
HPtemp = (int)((1.0 * AP / p.DP) * AP * 5 / (rand() % 4 + 10));
cout << name << " uses bash, " << p.name << "'s HP decreases " << HPtemp << endl;
EXPtemp = (int)(EXPtemp + HPtemp * 1.2);
p.HP = (int)(p.HP - HPtemp);
cout << name << " obtained " << EXPtemp << " experience." << endl;
EXP = (int)(EXP + EXPtemp);
system("pause");
return 1; // Attack success
}
bool swordsman::specialatt(player& p)
{
if (MP < 40)
{
cout << "You don't have enough magic points!" << endl;
system("pause");
return 0; // Attack failed
}
else
{
MP -= 40; // consume 40 MP to do special attack
// 10% chance opponent evades
if (rand() % 100 <= 10)
{
cout << name << "'s leap attack has been evaded by " << p.name << endl;
system("pause");
return 1;
}
double HPtemp = 0;
double EXPtemp = 0;
// double hit=1;
// srand(time(NULL));
HPtemp = (int)(AP * 1.2 + 20); // not related to opponent's DP
EXPtemp = (int)(HPtemp * 1.5); // special attack provides more experience
cout << name << " uses leap attack, " << p.name << "'s HP decreases " << HPtemp << endl;
cout << name << " obtained " << EXPtemp << " experience." << endl;
p.HP = (int)(p.HP - HPtemp);
EXP = (int)(EXP + EXPtemp);
system("pause");
}
return 1; // special attack succeed
}
// Computer opponent
void swordsman::AI(player& p)
{
if ((HP < (int)((1.0 * p.AP / DP) * p.AP * 1.5)) && (HP + 100 <= 1.1 * HPmax) && (bag.nOfHeal() > 0) && (HP > (int)((1.0 * p.AP / DP) * p.AP * 0.5)))
// AI's HP cannot sustain 3 rounds && not too lavish && still has heal && won't be killed in next round
{
useHeal();
}
else
{
if (MP >= 40 && HP > 0.5 * HPmax && rand() % 100 <= 30)
// AI has enough MP, it has 30% to make special attack
{
specialatt(p);
p.isDead(); // check whether player is dead
}
else
{
if (MP < 40 && HP > 0.5 * HPmax && bag.nOfMW())
// Not enough MP && HP is safe && still has magic water
{
useMW();
}
else
{
attack(p); // normal attack
p.isDead();
}
}
}
}
一些游玩中的截图:
设计中,GAME OVER后可以打印玩家死亡前的等级、经验等信息,思路是在player类中新增Print成员函数以访问保护成员。另外的,在已给的程序中,main.cpp中先前缺少human = nullptr;
,导致出现野指针的报错情况,可能是出题者在出题时遗漏,补充上后,程序方可正确运行。
实验总结:
- 本次实验熟练运用了抽象类、虚函数、纯虚函数、运算符重载的相关知识,对多态有了更深的理解与掌握。
- 在应用新知识的同时,也对以往学过的知识有了新的注意点:在task5中,对模板类的应用更熟练了,并且知晓了在类模板中,友元函数的声明前也必须要加上模板参数的声明!这是在之前没有注意到,而在频繁报错后才了解到的。如下:
template<typename T> class Complex { ··· //友元 template<typename T>//在类模板中,友元函数的声明通常也需要加上模板参数的声明!!!!!!!!!!!! friend std::istream& operator>>(std::istream& in, Complex<T>& C); template<typename T> friend std::ostream& operator<<(std::ostream& out, const Complex<T>& C); ··· };
- 在task7中,学习到了新的知识:
system("cls");
用于清楚显示屏。