实验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"); 用于清楚显示屏。
posted @ 2024-12-03 17:52  Sunria  阅读(10)  评论(0编辑  收藏  举报