C++中的类(期末复习篇)

这篇博客探讨了一个使用C++实现的宠物生长管理系统,涉及面向对象编程中的虚函数和多态。系统管理猫和狗的生长状态,基类`Pet`包含虚函数`display`,子类`Cat`和`Dog`根据各自特征实现不同的生长速率。在主函数中,基类指针`Pet*pt`被用来测试子类对象,通过输入日期计算宠物的当前身长和体重。文章还讨论了日期计算的优化方法,包括判断闰年、计算日期间隔等,提高了代码的可读性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

宠物的生长(虚函数和多态)

题目描述:

需要开发一个系统,对宠物的生长状态进行管理。给出下面的基类框架:

class Pet

{ protected:

  string name;//姓名

float length;//身长

float weight;//体重

CDate current;//开始记录时间

日期类CDate包含年、月、日三个私有数据,其他方法根据需要自拟。

public:

virtual void display(CDate day)=0;//输出目标日期时宠物的身长和体重

}

以Pet为基类,构建出Cat和Dog两个类:

Cat一天身长加0.1,体重加0.2。

Dog一天身长加0.2,体重加0.1。

生成上述类并编写主函数,要求主函数中有一个基类指针Pet *pt,用于测试子类数据。

主函数根据输入的信息,相应建立Cat类对象或Dog类对象,并给出测量日期时宠物的身长和体重。

输入:

第一行为测试次数

第二行是开始记录日期

从第三行起,每个测试用例占一行,每行给出宠物的基本信息:宠物的类型(1为Cat,2为Dog)、名字、身长、体重、最后测量的日期。

输出:

要求输出目标日期宠物姓名、身长和体重(结果要求保留小数点后2位)。若测量日期小于开始日期,输出”error”。

输入样例:

3
2019 5 5
1 tony 10 10 2018 12 30
2 jerry 5 6 2019 5 10
1 tom 3 4 2019 6 1

输出样例:

error
jerry after 5 day: length=6.00,weight=6.50
tom after 27 day: length=5.70,weight=9.40
 

代码(完善):

#include <iostream>
#include <iomanip>
using namespace std;

class CDate {
public:
	CDate() {}
	CDate(int a, int b, int c) :year(a), month(b), day(c) {}
	bool isleapyear() {
		return (year % 4 == 0 && year % 100 != 0) || (year % 400) == 0;
	}
	int getyear(int x, int y) {
		int sum = 0;
		for (int i = x; i < y; i++) {
			CDate c(i, 1, 1);
			if (c.isleapyear()) {
				sum += 366;
			}
			else {
				sum += 365;
			}
		}
		return sum;
	}
	friend int getdayofyear(CDate& p);
	friend int days(const CDate& a, const CDate& b);
	friend int remainday(CDate& q, CDate& p);
private:
	int year;
	int month;
	int day;
};

int getdayofyear(CDate& p) {
	int i;
	int sum = p.day;
	int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (p.isleapyear()) {
		a[2]++;
	}
	for (i = 0; i < p.month; i++) {
		sum += a[i];
	}
	return sum;
}

int days(const CDate& a, const CDate& b) {
	return a.year * 10000 + a.month * 100 + a.day < b.year * 10000 + b.month * 100 + b.day;
}

int remainday(CDate& q, CDate& p) {
	int sum;
	sum = q.getyear(q.year, p.year) - getdayofyear(q) + getdayofyear(p);
	return sum;
}

class Pet {
protected:
	string name;
	float length;
	float weight;
	CDate current;
public:
	Pet() {}
	Pet(string na, float le, float we, CDate current) :current(current) {
		name = na;
		length = le;
		weight = we;
	}
	virtual void display(CDate day) = 0;
};

class Cat :public Pet {
public:
	Cat(string na, float le, float we, CDate current) :Pet(na, le, we, current) {}
	virtual void display(CDate day) {
		if (days(current, day) == 0) {
			cout << "error" << endl;
		}
		else {
			int re = remainday(current, day);
			length += 0.1 * re;
			weight += 0.2 * re;
			cout << name << " after " << re << " day: length=" << fixed << setprecision(2) << length << ",weight=" << fixed << setprecision(2) << weight << endl;
		}
	}
};

class Dog :public Pet {
public:
	Dog(string na, float le, float we, CDate current) :Pet(na, le, we, current) {}
	virtual void display(CDate day) {
		if (days(current, day) == 0) {
			cout << "error" << endl;
		}
		else {
			int re = remainday(current, day);
			length += 0.2 * re;
			weight += 0.1 * re;
			cout << name << " after " << re << " day: length=" << fixed << setprecision(2) << length << ",weight=" << fixed << setprecision(2) << weight << endl;
		}
	}
};

int main()
{
	int t;
	int year1, month1, day1;
	int year2, month2, day2;
	int type;
	float length, weight;
	Pet* pt = NULL;
	string name;
	cin >> t;
	cin >> year1 >> month1 >> day1;
	CDate date1(year1, month1, day1);
	while (t--) {
		cin >> type;
		cin >> name >> length >> weight;
		cin >> year2 >> month2 >> day2;
		CDate date2(year2, month2, day2);
		if (type == 1) {
			pt = new Cat(name, length, weight, date1);
		}
		else if (type == 2) {
			pt = new Dog(name, length, weight, date1);
		}
		pt->display(date2);
	}
}

我的总结:

1、日期的计算:

通常题目要求的是计算一个日期到另一个日期的天数,如到保质期的天数,两人生日相差的距离等等,是期中考试和期末考试的常考点。

刚开始的时候我的代码思路为:1)如果不同年,先计算第一个人(年龄大的)的生日到年末的天数,再计算两人相差的年数的天数,最后计算第二个人年初到他生日的天数,相加即可。2)同年的再次不再进行讨论。

编写的代码为:

friend int remainday(CDate &q, CDate& p) {
	int sum = 0;
	if (q.year != p.year) {
		int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		sum += a[q.month] - 5;
		for (int i = q.month + 1; i <= 12; i++) {
			sum += a[i];
	    }
		for (int i = q.year; i < p.year; i++) {
			CDate c(i, 1, 1);
			if (c.isleapyear()) { sum += 366; }
			else {
				sum += 365;
			}
		}
		if (q.isleapyear()) {
			a[2] = 29;
		}
		for (int i = 1; i < p.month; i++) {
			sum += a[i];
		}
		sum += p.day;
	}
	else {
		int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (q.isleapyear()) {
			a[2] = 29;
		}
		if (q.month != p.month) {
	    	for (int i = q.month+1; i < p.month; i++) {
				sum += a[i];
			}
			sum += a[q.month] - q.day;
			sum += p.day;
		}
		else {
			sum += p.day - q.day;
		}	
	}
	return sum;
}

可以看出,代码的编写非常的长,可读性较差,不太符合代码的编写规范。

通过参考一些资料,我找到了可读性较好的代码。

具体的思路为:1)定义一个函数,如getdayofyear( )来计算一年的年初到每个日期的天数。2)再定义一个函数,如getyear( )来计算两个日期所隔的年数的天数。(第二个日期所在的年数之前的年数)3)再定义一个函数,如remainday( )利用以上所定义的函数来计算两个日期所隔的天数。

所必须的代码如下:

//以下函数均为在类内所定义的

//判断是否为闰年的函数
bool isleapyear() {
	return (year % 4 == 0 && year % 100 != 0) || (year % 400) == 0;
}

//计算一年的年初到每个日期的天数的函数(运用友元函数)
friend int getdayofyear(CDate& p) {  //参数为一个所需计算的类
	int i;
	int sum = p.day;
	int a[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	if (p.isleapyear()) {
		a[2]++;
	}
	for (i = 0; i < p.month; i++) {
		sum += a[i];
	}
	return sum;
}

//计算两个日期所隔的年数的天数的函数(第二个日期所在的年数之前的年数)
int getyear(int x, int y) {  //参数为两个日期
	int sum = 0;
	for (int i = x; i < y; i++) {
		CDate c(i, 1, 1);
		if (c.isleapyear()) {
			sum += 366;
		}
		else {
			sum += 365;
		}
	}
	return sum;
}

//来计算两个日期所隔的天数的函数(主要函数)
int remainday(CDate& q, CDate& p) {  //参数为两个所需计算所隔日期的类
	int sum;
	sum = q.getyear(q.year, p.year) - getdayofyear(q) + getdayofyear(p);
	return sum;
}

//比较函数(可能需要)
bool days(const CDate& a, const CDate& b) {
	return a.year * 10000 + a.month * 100 + a.day > b.year * 10000 + b.month * 100 + b.day;
}

可以看出,这样编写的代码思路清晰,可读性较好。 

2、继承与虚继承:

1)“主函数中有一个基类指针Pet *pt,用于测试子类数据”:

代码如下:

int main()
{
    ...
    Pet* pt = NULL;
    if (type == 1) {
		pt = new Cat(name, length, weight, date1);
	}
	else if (type == 2) {
		pt = new Dog(name, length, weight, date1);
	}
	pt->display(date2);
    ...
}

注意点:在if作用域中,一定要new生成新类来调用构造函数,因为如果不用new生成时,该作用域结束后,生成的类对象会消亡,将无法经行display操作

2)虚函数:

class Pet{
public:
    virtual void display(CDate day)=0;//输出目标日期时宠物的身长和体重 不进行实现!
...
}

class Cat :public Pet{
public:
    virtual void display(CDate day) {
		if (days(current, day) == 0) {
			cout << "error" << endl;
		}
		else {
			int re = remainday(current, day);
			length += 0.1 * re;
			weight += 0.2 * re;
			cout << name << " after " << re << " day: length=" << fixed << setprecision(2) << length << ",weight=" << fixed << setprecision(2) << weight << endl;
		}
	}
...
}

class Dog :public Pet {
public:
    virtual void display(CDate day) {
		if (days(current, day) == 0) {
			cout << "error" << endl;
		}
		else {
			int re = remainday(current, day);
			length += 0.2 * re;
			weight += 0.1 * re;
			cout << name << " after " << re << " day: length=" << fixed << setprecision(2) << length << ",weight=" << fixed << setprecision(2) << weight << endl;
		}
	}
...
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值