技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1406|回复: 4
收起左侧

【C/C++面向对象方法实践:园丁游戏】

[复制链接]

85

主题

260

帖子

3763

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1365 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
发表于 2015-10-17 20:54:38 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
开更老C的新作品:Practice With OO Method in C/C++: A Game Of Gardener(C/C++面向对象方法实践:园丁游戏)
作者:cyycoish。转载请注明作者出处,否则视为侵权。
这篇文章主要向大家介绍面向对象的实现方法。本文面向于那些对面向对象编程概念摸不着头脑的人们。接下来会使用C(注:C不是面向对象的语言),C++来加以说明。因为本人水平有限,如有错误之处欢迎指正批评共同进步。

第一篇:元始天尊

地球上生命的摇篮是大海,亿年前,蔚蓝的海水中出现的第一个生命的原型:细胞。
始祖鸟化石是人类已知最早的鸟类,虽然化石显示“始祖鸟”的牙齿还未完全退化,但是它已经拥有了羽毛。
面向对象的诞生同样充满了神话般的色彩,让我们通过C语言寻找面向对象诞生之初的影子。
下面来描述一下这个游戏:
A版本:
你现在是一个园丁,拥有一颗树苗。树苗生命值最大100。生命值为0则树苗死亡。
刚刚买回来,树苗生命值85,高度100cm。
以后每一天树苗因为生长生命值-15,而高度+10cm。
每天你可以执行4个操作其中的一个:
0.        退出游戏
1.        浇水
2.        修剪高度
3.        什么也不做
选择浇水,可以输入水量(以公升计算)。每浇水一升,树苗生命值+1 。如果生命值超过100,则进行倒扣(浇水过多植物寿命减少)。如果当天选择浇水,则树苗当天高度立即+6(树苗吸水增长)。
你的目标:使得树苗高度达到可以售卖的标准200cm。同时天数越短越好,树苗生命值越大越好。
注:超过200cm可以使用修剪功能减小树苗高度,但是剪切长度超过树苗高度,树苗即死亡。

以下是C语言描述:
[C] 纯文本查看 复制代码
//代码1-1
#include <stdio.h>

#define HEIGHT_STANDARD 200 //标准高度

void CutPlant(int* health, int* height, int iCuttOffHeightByCentiMeter) //剪切植物
{
	if (*height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		*height -= iCuttOffHeightByCentiMeter; //剪切
	else
		*health = 0; //被剪死了
}

void WaterPlant(int* health, int* height, int iWaterQuantityByLitery) //浇水
{
	int Orghealth = *health; //记录浇水前的生命值
	*health += iWaterQuantityByLitery; //生命值等于浇水水量
	*height += 6; //每次浇水高度+6
	if (*health > 100) //生命值大于100
		*health = Orghealth + 100 - *health; //生命值倒扣
	if (*health < 0) //淹死了
		*health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", *health - Orghealth); //显示浇水后生命值以及高度的变化
}

int main(void)
{
	int iOption;
	int iWQ;
	int iCH;

	int Day = 0; //天数
	int Height = 100; //植物高度
	int Health = 85;  //植物生命值

	printf("Welcome!\nNow you have a sapling.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (Health > 0)
	{
		Day++;
		printf("Days %d\n", Day);
		Health -= 15;
		printf("\tHealth -15.\n");
		if (Health > 20)
		{
			Height += 10;
			printf("\tHeight +10.\n");
		}
		printf("Reports:\n\tHeight: %d/200\n\tHealth: %d/100\n", Height, Health);
		if (Height == HEIGHT_STANDARD)
			break;
		if (Health <= 0)
			break;
		printf("[1]Water.[2]Cut.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &iWQ);
			WaterPlant(&Health, &Height, iWQ);
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &iCH);
			CutPlant(&Health, &Height, iCH);
			break;
		case 0:
			goto Lbl_Exit;
		default:
			printf("You did nothing?\n");
		}
	}
Lbl_Exit:
	if (Health <= 0)
		printf("You bad gardener.The plant was die.\n");
	else if (Height == HEIGHT_STANDARD)
		printf("%d Days,The plant was fit to sale for %d cm!\n", Day, HEIGHT_STANDARD);
	else
		printf("Wont you keep going on it?\n");

	return 0;
}

可以看见,为了满足面向过程程序设计的需要。以上代码将浇水和剪切两个动作从主过程中分离,抽象为两个函数。调用时以引用方式输入植物高度与生命值,分别判断修剪长度与浇水水量后,对高度及生命值做相应变化。
接下来对游戏规则进行修改:
B版本:
你现在是一个园丁,拥有两颗树苗。树苗生命值最大100。生命值为0则树苗死亡。
刚刚买回来,树苗生命值85,高度100cm。
以后每一天树苗因为生长生命值-15,而高度+10cm。
每天你可以对其中一棵树苗执行4个操作其中的一个:
0.        退出游戏
1.        浇水
2.        修剪高度
3.        什么也不做
选择浇水,可以输入水量(以公升计算)。每浇水一升,树苗生命值+1 。如果生命值超过100,则进行倒扣(浇水过多植物寿命减少)。如果当天选择浇水,则树苗当天高度立即+6(树苗吸水增长)。
你的目标:使得全部两颗树苗高度达到可以售卖的标准200cm。同时天数越短越好,树苗生命值越大越好。
注:超过200cm可以使用修剪功能减小树苗高度,但是剪切长度超过树苗高度,树苗即死亡。
其中一棵树苗高度达到200cm即可售卖。
分析:
先前针对A版本的游戏当中,树苗并未作为个体被抽象出来。因为只有一棵树苗,所以分别申请一个叫做Height的变量存放该树苗高度,Health变量存放生命值。现在树苗个数变为两个。我们是不是就应该这样子定义变量呢?int Height1;int Health1; int Height2;int Health2; 。那么如果是三颗树苗,4棵树苗,5棵树苗……。也许你可以这样子操作:int Height[N];int Health[N];。但是这样子不如将树苗作为“对象”抽象出来,定义一个结构体包含height与health属性来得方便些。于是我们有了结构体“Plant”其中有两个int类型变量:health和height。
此时,代码C语言描述如下:
[C] 纯文本查看 复制代码
//代码1-2
#include <stdio.h>
#define HEIGHT_STANDARD 200 //植物标准高度

struct Plant //结构体“植物”
{
	int height; //高度
	int health; //生命值
};

struct Plant MyPlant[2];

void InitAPlant(struct Plant* TargetPlant) //初始化函数
{
	TargetPlant->height = 100;
	TargetPlant->health = 85;
}

void CutPlant(int* health, int* height, int iCuttOffHeightByCentiMeter) //剪切植物
{
	if (*height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		*height -= iCuttOffHeightByCentiMeter; //剪切
	else
		*health = 0; //被剪死了
}

void WaterPlant(int* health, int* height, int iWaterQuantityByLitery) //浇水
{
	int Orghealth = *health; //记录浇水前的生命值
	*health += iWaterQuantityByLitery; //生命值等于浇水水量
	*height += 6; //每次浇水高度+6
	if (*health > 100) //生命值大于100
		*health = Orghealth + 100 - *health; //生命值倒扣
	if (*health < 0) //淹死了
		*health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", *health - Orghealth); //显示浇水后生命值以及高度的变化
}

int main()
{
	int iOption; //操作选择
	int iWQ; //浇水水量
	int iCH; //修剪长度

	int Day = 0; //天数
	InitAPlant(&MyPlant[0]); //初始化植物
	InitAPlant(&MyPlant[1]); //初始化植物
	int PlantsCounter = 2; //待完成养殖的植物个数
	int CurrentPlantIndex; //当前被操作的植物索引

	printf("Welcome!\nNow you have two saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (PlantsCounter) //如果待完成养殖的植物个数为0则表示养殖任务完毕
	{
		Day++;
		printf("Days %d\n", Day);
		MyPlant[0].health -= 15;
		MyPlant[1].health -= 15;
		if (MyPlant[0].health > 20) //如果植物生命在20以上,那么正常生长。
			MyPlant[0].height += 10;
		if (MyPlant[1].health > 20)
			MyPlant[1].height += 10;

		printf("Reports for plant 1:\n\tHeight: %d/200\n\tHealth: %d/100\n", MyPlant[0].height, MyPlant[0].health);
		printf("Reports for plant 2:\n\tHeight: %d/200\n\tHealth: %d/100\n", MyPlant[1].height, MyPlant[1].health);
		if (MyPlant[0].health <= 0) //植物死了一颗,游戏失败
			goto Lbl_Game_Over;
		if (MyPlant[1].health <= 0)
			goto Lbl_Game_Over;
		if (MyPlant[0].height == HEIGHT_STANDARD) //养成一颗
			PlantsCounter--; //待完成养殖的植物个数-1
		if (MyPlant[1].height == HEIGHT_STANDARD)
			PlantsCounter--;

		printf("Choose a plant to operate.[1] for 1st plant.[2] for 2nd plant.[AnyInputing] to pass any operating.\n");
		scanf("%d", &iOption); //选择一颗植物进行操作
		if (iOption != 1 && iOption != 2 || MyPlant[iOption - 1].height == HEIGHT_STANDARD) //没有选择任何一颗未完成之植物。因为C语言编译器对逻辑表达式的“短路计算”,MyPlant[iOption - 1]并不会越界。
			goto Lbl_Pass_Anyway;
		CurrentPlantIndex = iOption - 1; //修改索引
		printf("[1]Water.[2]Cut.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &iWQ);
			WaterPlant(&MyPlant[CurrentPlantIndex].health, &MyPlant[CurrentPlantIndex].height, iWQ);
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &iCH);
			CutPlant(&MyPlant[CurrentPlantIndex].health, &MyPlant[CurrentPlantIndex].height, iCH);
			break;
		case 3:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You did nothing?\n");
		}
	}
Lbl_Exit:
	if (MyPlant[0].health <= 0 || MyPlant[1].health <= 0)
		printf("You bad gardener.The plant was die.\n");
	else if (MyPlant[0].height == HEIGHT_STANDARD && MyPlant[1].height == HEIGHT_STANDARD)
		printf("%d Days,All plants were fit to sale for %d cm!\n", Day, HEIGHT_STANDARD);
	else
		printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

为了进一步说明声明结构体的重要性,我们来改进代码,使之支持达到“100”个植物!当然,随着植物数量增加,游戏难度随之增长。(那么因为时间关系,老C不在此代码上做可行性测试,以及详细的排bug处理,仅作为理论演示。)
改进后的C语言代码:
[C] 纯文本查看 复制代码
//代码1-3
#include <stdio.h>
#define HEIGHT_STANDARD 200 //植物标准高度
#define PLANTS 100 //100个植物

struct Plant //结构体“植物”
{
	int height; //高度
	int health; //生命值
};

struct Plant MyPlant[PLANTS];

void InitAPlant(struct Plant* TargetPlant) //初始化函数
{
	TargetPlant->height = 100;
	TargetPlant->health = 85;
}

void CutPlant(int* health, int* height, int iCuttOffHeightByCentiMeter) //剪切植物
{
	if (*height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		*height -= iCuttOffHeightByCentiMeter; //剪切
	else
		*health = 0; //被剪死了
}

void WaterPlant(int* health, int* height, int iWaterQuantityByLitery) //浇水
{
	int Orghealth = *health; //记录浇水前的生命值
	*health += iWaterQuantityByLitery; //生命值等于浇水水量
	*height += 6; //每次浇水高度+6
	if (*health > 100) //生命值大于100
		*health = Orghealth + 100 - *health; //生命值倒扣
	if (*health < 0) //淹死了
		*health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", *health - Orghealth); //显示浇水后生命值以及高度的变化
}

int main()
{
	int i;
	int iOption; //操作选择
	int iWQ; //浇水水量
	int iCH; //修剪长度

	int Day = 0; //天数
	for (i = 0; i < PLANTS; i++, InitAPlant(&MyPlant[i])); //初始化植物
	int PlantsCounter = PLANTS; //待完成养殖的植物个数
	int CurrentPlantIndex; //当前被操作的植物索引
	int finishedIndex[PLANTS] = { 0 }; //记录完成索引

	printf("Welcome!\nNow you have &d saplings.\n", PLANTS);
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (PlantsCounter) //如果待完成养殖的植物个数为0则表示养殖任务完毕
	{
		Day++;
		printf("Days %d\n", Day);
		for (i = 0; i < PLANTS; i++)
		{
			MyPlant[i].health -= 15; //每天因为
			if (MyPlant[i].health > 20) //如果植物生命在20以上,那么正常生长。
				MyPlant[i].height += 10;
			if (MyPlant[i].health <= 0) //植物死了一颗,游戏失败
				goto Lbl_Game_Over;
			if (MyPlant[i].height == HEIGHT_STANDARD) //养成一颗
			{
				PlantsCounter--; //待完成养殖的植物个数-1
				finishedIndex[i] = 1;
			}
		}
		printf("Choose a plant to operate.Input a number range from 1 to %d\n", PLANTS);
		printf("Here are unfinished plants which you can choose.:\n");
		for (i = 0; i < PLANTS; i++, (!finishedIndex[i]) ? printf("%d ", i) : 1); //打印未完成养殖的植物
		printf("\nIf your choice out of the above-mentioned, Current operating will be passed.PLEASE TREAT IT SERIOUSLY.\n");
		scanf("%d", &iOption); //选择一颗植物进行操作
		for (i = 0; i < PLANTS; i++, CurrentPlantIndex = (!finishedIndex[i]) && iOption - 1 == i ? i : -1); //选择一颗符合上述条件的植物
		if (CurrentPlantIndex == -1)
			goto Lbl_Pass_Anyway;
		printf("[1]Water.[2]Cut.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &iWQ);
			WaterPlant(&MyPlant[CurrentPlantIndex].health, &MyPlant[CurrentPlantIndex].height, iWQ);
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &iCH);
			CutPlant(&MyPlant[CurrentPlantIndex].health, &MyPlant[CurrentPlantIndex].height, iCH);
			break;
		case 3:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You did nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale for %d cm!\n", Day, HEIGHT_STANDARD);
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

这时候大家有没有觉得瞬间,大量的物体被以一种更“集成化”的方式,简约地处理掉了。
至此我们已经知道类的雏形,是的,他就是:结构体(structure)
回复

使用道具 举报

85

主题

260

帖子

3763

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1365 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
 楼主| 发表于 2015-10-17 21:15:33 | 显示全部楼层
第二篇:海纳百川,包罗万象

有古训:有容乃大。一个集大成者的计算机语言必然可以使得使用它的人来描述任何东西。
不可否认,OOP(即面向对象编程)是计算机语言的革命。打赢这一场翻身仗OOP需要经过包罗万象的考验。
我们继续以“园丁游戏A版本”为蓝本修改游戏规则:
C版本:
你现在是一个园丁,拥有两颗树苗。一颗苹果树,一颗橘子树。树苗生命值最大100。生命值为0则树苗死亡。
刚刚买回来,树苗生命值85,高度100cm,都没有结果子。
以后每一天树苗因为生长生命值-15,而高度+10cm。
苹果树在生命值大于等于30的情况下,每天结一颗果子。
橘子树在生命值大于等于60的情况下,每天结一颗果子。

两颗果树每一颗果子每一天消耗生命值2
每天你可以执行4个操作其中的一个:
0.        退出游戏
1.        浇水
2.        修剪高度
3.        摘果子
4.        什么也不做
选择浇水,可以输入水量(以公升计算)。每浇水一升,树苗生命值+1 。如果生命值超过100,则进行倒扣(浇水过多植物寿命减少)。如果当天选择浇水,则树苗当天高度立即+6(树苗吸水增长)。
选择摘果子,可以输入摘果数量,该数量不能大于结果数。
你的目标:使得树苗高度达到可以售卖的标准200cm。同时天数越短越好。达到200cm后的果树:苹果与橘子单个售价均为$1, 卖果子所得的钱将作为你的额外收入。收入越多越好。
注:超过200cm可以使用修剪功能减小树苗高度,但是剪切长度超过树苗高度,树苗即死亡。
这是第一个版本并不完善的C代码:
[C] 纯文本查看 复制代码
//代码2-1
#include <stdio.h>
#define HEIGHT_STANDARD 200 //植物标准高度

struct Plant //结构体“植物”
{
	int height; //高度
	int health; //生命值
	int apple;  //苹果个数
	int orange; //橘子个数
};

struct Plant AppleTree;  //苹果树
struct Plant OrangeTree; //橘子树

void InitAPlant(struct Plant* TargetPlant) //初始化函数
{
	TargetPlant->height = 100;
	TargetPlant->health = 85;
	TargetPlant->apple = 0;
	TargetPlant->orange = 0;
}

void CutPlant(int* health, int* height, int iCuttOffHeightByCentiMeter) //剪切植物
{
	if (*height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		*height -= iCuttOffHeightByCentiMeter; //剪切
	else
		*health = 0; //被剪死了
}

void WaterPlant(int* health, int* height, int iWaterQuantityByLitery) //浇水
{
	int Orghealth = *health; //记录浇水前的生命值
	*health += iWaterQuantityByLitery; //生命值等于浇水水量
	*height += 6; //每次浇水高度+6
	if (*health > 100) //生命值大于100
		*health = Orghealth + 100 - *health; //生命值倒扣
	if (*health < 0) //淹死了
		*health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", *health - Orghealth); //显示浇水后生命值以及高度的变化
}

void PickFruit(struct Plant* fruitTree, int howManyFruits)
{
	int Apple = fruitTree->apple;   //苹果数
	int Orange = fruitTree->orange; //橘子数
	//摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		fruitTree->apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		fruitTree->orange -= howManyFruits;
}

int main()
{
	int i;
	int iOption; //操作选择
	int iWQ; //浇水水量
	int iCH; //修剪长度
	int iFT; //摘果个数

	int Day = 0; //天数

	InitAPlant(&AppleTree);
	InitAPlant(&OrangeTree);

	struct Plant* TargetPlant;

	printf("Welcome!\nNow you have double saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (1)
	{
		Day++;
		printf("Days %d\n", Day);
		AppleTree.health -= 15 + AppleTree.apple * 2;
		OrangeTree.health -= 15 + OrangeTree.orange * 2;
		printf("\tHealth -15.\n");
		if (AppleTree.health > 20)
			AppleTree.height += 10;
		if (OrangeTree.health > 20)
			OrangeTree.height += 10;
		if (AppleTree.health >= 30)
			AppleTree.apple++;
		if (OrangeTree.health >= 60)
			OrangeTree.orange++;
		printf("Reports for apple tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", AppleTree.height, AppleTree.health, AppleTree.apple);
		printf("Reports for orange tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", OrangeTree.height, OrangeTree.health, OrangeTree.orange);

		if (AppleTree.health <= 0 || OrangeTree.health <= 0)
			goto Lbl_Game_Over;

		printf("Choose a plant to operate.[0] for apple tree,[Any inputing] for orange tree.\n");
		scanf("%d", &iOption);
		if (!iOption && AppleTree.height != HEIGHT_STANDARD)
			TargetPlant = &AppleTree;
		else
			TargetPlant = &OrangeTree;

		printf("[1]Water.[2]Cut.[3]Pick Fruits.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &iWQ);
			WaterPlant(&TargetPlant->health, &TargetPlant->height, iWQ);
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &iCH);
			CutPlant(&TargetPlant->health, &TargetPlant->height, iCH);
			break;
		case 3:
			printf("\tInput quantity to pick up:");
			scanf("%d", &iFT);
			PickFruit(TargetPlant, iFT);
			break;
		case 0:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You did nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale! Any you got $%d for pocket money!\n", Day, AppleTree.apple + OrangeTree.orange);
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

现在,我们要对以上代码进行重构,以便达到更好的封装,使代码能被充分“物尽其用”。将植物本身的特性尽可能地赋予植物内部。我们所做的第二步是要简化“上帝”的工作。尽量缩减主函数。
[C] 纯文本查看 复制代码
//代码2-2
#include <stdio.h>
#define HEIGHT_STANDARD 200 //植物标准高度
#define THIS TargetPlant

struct Plant //结构体“植物”
{
	int height; //高度
	int health; //生命值
	int apple;  //苹果个数
	int orange; //橘子个数
	void(*Cut)(struct Plant* TargetPlant, int iCuttOffHeightByMillimeter); //修剪植物
	void(*Water)(struct Plant* TargetPlant, int iWaterQuantityByLitery);   //浇水
	void(*PickFruits)(struct Plant* TargetPlant, int howManyFruits);       //摘果子
};

struct Plant AppleTree;  //苹果树
struct Plant OrangeTree; //橘子树

void CutPlant(struct Plant* TargetPlant, int iCuttOffHeightByCentiMeter) //剪切植物
{
	if (TargetPlant->height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		TargetPlant->height -= iCuttOffHeightByCentiMeter; //剪切
	else
		TargetPlant->health = 0; //被剪死了
}

void WaterPlant(struct Plant* TargetPlant, int iWaterQuantityByLitery) //浇水
{
	int Orghealth = TargetPlant->health; //记录浇水前的生命值
	TargetPlant->health += iWaterQuantityByLitery; //生命值等于浇水水量
	TargetPlant->health += 6; //每次浇水高度+6
	if (TargetPlant->health > 100) //生命值大于100
		TargetPlant->health = Orghealth + 100 - TargetPlant->health; //生命值倒扣
	if (TargetPlant->health < 0) //淹死了
		TargetPlant->health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", TargetPlant->health - Orghealth); //显示浇水后生命值以及高度的变化
}

void PickFruit(struct Plant* fruitTree, int howManyFruits)
{
	int Apple = fruitTree->apple;   //苹果数
	int Orange = fruitTree->orange; //橘子数
	//摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		fruitTree->apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		fruitTree->orange -= howManyFruits;
}

void HangARealDay(void)
{
	AppleTree.health -= 15 + AppleTree.apple * 2;
	OrangeTree.health -= 15 + OrangeTree.orange * 2;
	printf("\tHealth -15.\n");
	if (AppleTree.health > 20)
		AppleTree.height += 10;
	if (OrangeTree.health > 20)
		OrangeTree.height += 10;
	if (AppleTree.health >= 30)
		AppleTree.apple++;
	if (OrangeTree.health >= 60)
		OrangeTree.orange++;
	printf("Reports for apple tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", AppleTree.height, AppleTree.health, AppleTree.apple);
	printf("Reports for orange tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", OrangeTree.height, OrangeTree.health, OrangeTree.orange);
}

void InitAPlant(struct Plant* TargetPlant) //初始化函数
{
	TargetPlant->height = 100;
	TargetPlant->health = 85;
	TargetPlant->apple = 0;
	TargetPlant->orange = 0;
	TargetPlant->Cut = CutPlant;
	TargetPlant->Water = WaterPlant;
	TargetPlant->PickFruits = PickFruit;
}

int main()
{
	int iOption; //操作选择
	int subOption;

	int Day = 0; //天数

	InitAPlant(&AppleTree);
	InitAPlant(&OrangeTree);

	struct Plant* TargetPlant;

	printf("Welcome!\nNow you have double saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (1)
	{
		Day++;
		printf("Days %d\n", Day);
		HangARealDay(); //Hang A Real Day is HARD.

		if (AppleTree.health <= 0 || OrangeTree.health <= 0)
			goto Lbl_Game_Over;

		printf("Choose a plant to operate.[0] for apple tree,[1] for orange tree.\n");
		scanf("%d", &iOption);
		if (!iOption && AppleTree.height != HEIGHT_STANDARD)
			TargetPlant = &AppleTree;
		else if (iOption == 1 && OrangeTree.height != HEIGHT_STANDARD)
			TargetPlant = &OrangeTree;
		else
			goto Lbl_Pass_Anyway;

		printf("[1]Water.[2]Cut.[3]Pick Fruits.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &subOption);
			TargetPlant->Water(THIS, subOption); //THIS就代表TargetPlant
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &subOption);
			TargetPlant->Cut(THIS, subOption); //THIS就代表TargetPlant
			break;
		case 3:
			printf("\tInput quantity to pick up:");
			scanf("%d", &subOption);
			TargetPlant->PickFruits(THIS, subOption); //THIS就代表TargetPlant
			break;
		case 0:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You did nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale! Any you got $%d for pocket money!\n", Day, AppleTree.apple + OrangeTree.orange);
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

消减后的主函数代码67行,比原先减少13行。当然,这是分离出HangARealDay函数带来的结果。但是在加入了THIS宏后,对植物进行的操作变成调用了植物结构体内的函数指针,使得代码可以被更好理解。当然,结构体内所有的成员(包括变量和指向函数的指针)都要在使用之前被InitAPlant函数初始化。
至此,我们发现,一个重大的转变就是将函数放在了结构体内,而不是HangARealDay函数缩减的仅有的13行代码。原先零散的函数瞬间被“收集”起来了。如果将结构体和其中函数指针指向的函数写入另外一个C文件,那么,只要提供函数调用说明,我们就可以随心所欲设置HangARealDay中的游戏规则。当然,修改植物本身的属性也变得极为方便——只要修改植物结构体中函数指针指向的函数内部内容即可。
结构体变得丰富起来了。不再是仅有的变量表示的事物属性,甚至是一个事物可以执行的动作,或者受外界刺激做出的反应。我们不仅仅可以用C语言计算一个“生物”局限的,微观的状态,我们可以造就模拟一整条“食物链”。
In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

85

主题

260

帖子

3763

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1365 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
 楼主| 发表于 2015-10-17 21:25:04 | 显示全部楼层
第三篇:革命日

夏末海边,天气闷热。
忽然间,铅色黑云,风高浪急。
岸边海鸟焦急地盘旋,一场暴风雨前不是宁静的。每一道闪电都点亮天空,随之而来的是雷声的呐喊:迎接秋日的到来吧!换季的时间到了,我们要革命!
解除软件开发人员的枷锁,C已无力胜任。革命家般的C++之父从头撤尾改造了C。
我们现在使用C++第一遍重写版本C的园丁游戏。
C++代码:
[C++] 纯文本查看 复制代码
//代码3-1
#include <stdio.h>
const int HEIGHT_STANDARD = 200; //植物标准高度

class Plant//植物类
{
public: //公开类中所有成员
	int height = 100;
	int health = 85;
	int apple = 0;
	int orange = 0;
	void Cut(int iCuttOffHeightByCentiMeter);  //剪切植物
	void Water(int iWaterQuantityByLitery);    //浇水
	void PickFruit(int howManyFruits);         //摘果子
};

void Plant::Cut(int iCuttOffHeightByCentiMeter) //成员函数
{
	if (height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		height -= iCuttOffHeightByCentiMeter; //剪切
	else
		health = 0; //被剪死了
}

void Plant::Water(int iWaterQuantityByLitery) //成员函数
{
	int Orghealth = health; //记录浇水前的生命值
	health += iWaterQuantityByLitery; //生命值等于浇水水量
	health += 6; //每次浇水高度+6
	if (health > 100) //生命值大于100
		health = Orghealth + 100 - health; //生命值倒扣
	if (health < 0) //淹死了
		health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", health - Orghealth); //显示浇水后生命值以及高度的变化
}

void Plant::PickFruit(int howManyFruits) //成员函数
{
	int Apple = apple;   //苹果数
	int Orange = orange; //橘子数
						 //摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		orange -= howManyFruits;
}

void HangARealDay(Plant* AppleTree, Plant* OrangeTree) //全局函数
{
	AppleTree->health -= 15 + AppleTree->apple * 2;
	OrangeTree->health -= 15 + OrangeTree->orange * 2;
	printf("\tHealth -15\n");
	if (AppleTree->health > 20)
		AppleTree->height += 10;
	if (OrangeTree->health > 20)
		OrangeTree->height += 10;
	if (AppleTree->health >= 30)
		AppleTree->apple++;
	if (OrangeTree->health >= 60)
		OrangeTree->orange++;
	printf("Reports for apple tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", AppleTree->height, AppleTree->health, AppleTree->apple);
	printf("Reports for orange tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", OrangeTree->height, OrangeTree->health, OrangeTree->orange);
}

int main()
{
	int iOption; //操作选择
	int subOption;

	int Day = 0; //天数

	Plant* AppleTree = new Plant;  //以Plant为模板,建造一棵苹果树
	Plant* OrangeTree = new Plant; //以Plant为模板,建造一棵橘子树
	Plant* TargetPlant;

	printf("Welcome!\nNow you have double saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (1)
	{
		Day++;
		printf("Days %d\n", Day);
		HangARealDay(AppleTree, OrangeTree);

		if (AppleTree->health <= 0 || OrangeTree->health <= 0)
			goto Lbl_Game_Over;
		printf("Choose a plant to operate.[0] for apple tree,[1] for orange tree.\n");
		scanf("%d", &iOption);
		if (!iOption && AppleTree->height != HEIGHT_STANDARD)
			TargetPlant = AppleTree;
		else if (iOption == 1 && OrangeTree->height != HEIGHT_STANDARD)
			TargetPlant = OrangeTree;
		else
			goto Lbl_Pass_Anyway;

		printf("[1]Water.[2]Cut.[3]Pick Fruits.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &subOption);
			TargetPlant->Water(subOption); //省略THIS
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &subOption);
			TargetPlant->Cut(subOption); //省略THIS
			break;
		case 3:
			printf("\tInput quantity to pick up:");
			scanf("%d", &subOption);
			TargetPlant->PickFruit(subOption); //省略THIS
			break;
		case 0:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You have done nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale! Any you got $%d for pocket money!\n", Day, AppleTree->apple + OrangeTree->orange);
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

如果大家仔细阅读并且思考了本文上述全部C代码,不难看出:class Plant(植物类)其实是一个结构体,然而,这个结构体内的变量能被直接初始化。类中声明的函数,就是前面C结构体中的函数指针。具体的函数实现就是下面的成员函数。“::”符号是访问限定符,int Plant::XXX()表示int XXX函数属于类Plant。成员函数内部可以直接使用所在类内部的变量而无须使用“THIS”指针。
创建一个以这个类为数据类型的变量使用语句:ClassName* var = new ClassName;,或者ClassName var;。这个过程被称作为“类的实例化”。var就是该类实例化出来的一个对象。
接下来大家需要搞懂以下几个概念:内联成员函数、this指针、构造函数、析构函数。
内联成员函数:
在C的结构体中,函数指针要在结构体外部做实现,那么在C++中,可以在类中直接实现该函数。这个函数被称为“内联成员函数”请看:
[C++] 纯文本查看 复制代码
//代码3-2
class Plant//植物类
{
public: //公开类中所有成员
	int height = 100;
	int health = 85;
	int apple = 0;
	int orange = 0;
	void Cut(int iCuttOffHeightByCentiMeter)       //剪切植物
	{
		if (height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
			height -= iCuttOffHeightByCentiMeter; //剪切
		else
			health = 0; //被剪死了
	}
	inline void Water(int iWaterQuantityByLitery); //浇水
	void PickFruit(int howManyFruits);             //摘果子
};

void Plant::Water(int iWaterQuantityByLitery) //成员函数
{
	int Orghealth = health; //记录浇水前的生命值
	health += iWaterQuantityByLitery; //生命值等于浇水水量
	health += 6; //每次浇水高度+6
	if (health > 100) //生命值大于100
		health = Orghealth + 100 - health; //生命值倒扣
	if (health < 0) //淹死了
		health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", health - Orghealth); //显示浇水后生命值以及高度的变化
}

void Plant::PickFruit(int howManyFruits) //成员函数
{
	int Apple = apple;   //苹果数
	int Orange = orange; //橘子数
						 //摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		orange -= howManyFruits;
}

那么在类Plant中,Cut函数为内联成员函数。而函数名称前加上inline关键字,函数实现在类以外,编译器也会自动视为“内联成员函数”。显然,函数PickFruit既没有加inline关键字在类中的声明之前,也没有在类中实现该函数。所以PickFruit不是“内联成员函数”。内联成员函数的出现是为了增加执行效率,其缺点是增加了类的体积。显然,将一些体积小的,执行次数多的函数内联是一个两全其美的好办法。
下面来说一说“this指针”:
还记得在C中我们如何定义Cut函数的吗?我找来了它的定义,在12页上面的代码:
void CutPlant(struct Plant* TargetPlant, int iCuttOffHeightByCentiMeter);
然后位于代码第三行处有一个THIS宏:#define THIS TargetPlant 。当我们使用Cut函数时是这样子调用的:TargetPlant->Cut(THIS, subOption); //THIS就代表TargetPlant 而在C++中我们调用Cut函数的时候:TargetPlant->Cut(subOption); //省略THIS 。
至此我们不难猜想得到,C++中,this指针用来访问“成员函数所在类中的成员变量”。这样说还不对!请看:
[C++] 纯文本查看 复制代码
//代码3-3
#include <iostream>
using namespace std;

class MyClass
{
public:
	char word[100] = {"Hello nice to meet you!\n"};
	void SayHelloA()
	{
		cout << "Hello glad to see you!" << endl;
	}
	void SayHelloB()
	{
		cout << word;
	}
};

int main()
{
	MyClass* objHelloA = new MyClass;
	MyClass* objHelloB = NULL;
	objHelloA->SayHelloA();
	objHelloA->SayHelloB();

	objHelloB->SayHelloA(); //请将断点下于此处
	objHelloB->SayHelloB(); //运行出错

	return 0;
}

在以上代码中objHelloA->SayHelloA();objHelloA->SayHelloB();能被正常执行并不会被觉得奇怪,但是大家有没有想过objHelloB->SayHelloA();为什么也能被执行?而objHelloB->SayHelloB();立即报错?解释是:类是一个结构体,其中的成员函数都是函数指针。objHelloB指向了一个空的内存地址,然而在申请“结构体”类时,内部所有函数指针已经被指向指定函数。所以,通过类的实例可以调用成员函数,却不能访问成员变量。
[C++] 纯文本查看 复制代码
//代码3-4
#include <iostream>
using namespace std;

class MyClass
{
public:
	int counter = 0;
	void Increase();
};

void MyClass::Increase()
{
	this->counter++;
}

int main()
{
	MyClass* objA = new MyClass;
	MyClass* objB = new MyClass;
	objA->Increase();
	cout << objB->counter << endl;
	return 0;
}

在以上代码中,如果this指针可以通知类这个“模子”本身的话,那么所有由这个模子构造出来的实例的counter变量值都应该+1,输出结果应为:
1
1
而实际上输出结果为:
1
0
所以this指针访问该实例构造出来的,属于该实例“自己”的那个类的内存中的成员变量counter。简而言之,objA在new的时候new出来一个:int counter = 0; 、一个void(*Increase)(void); ,objB在new的时候“又”new出来一个int counter = 0; 、一个void(*Increase)(void); 。如果再new一个objC,那么内存中再多一组int counter = 0; 、void(*Increase)(void); 。此时虽然三个obj都有counter,但是objC中的Increase函数只访问objC内存中的counter。
为了说明构造函数,我们需要改进“园丁游戏C版本”。我们叫改进过的C版本为“园丁游戏C-2”
C-2版本:
你现在是一个园丁,拥有两颗树苗。一颗苹果树,一颗橘子树。树苗生命值最大100。生命值为0则树苗死亡。
刚刚买回来,苹果树苗生命值85,高度100cm,没有结果子。
刚刚买回来,橘子树苗生命值90,高度105cm,没有结果子。
以后每一天树苗因为生长生命值-15,而高度+10cm。
苹果树在生命值大于等于30的情况下,每天结一颗果子。
橘子树在生命值大于等于60的情况下,每天结一颗果子。
两颗果树每一颗果子每一天消耗生命值2
每天你可以执行4个操作其中的一个:
0.        退出游戏
1.        浇水
2.        修剪高度
3.        摘果子
4.        什么也不做
选择浇水,可以输入水量(以公升计算)。每浇水一升,树苗生命值+1 。如果生命值超过100,则进行倒扣(浇水过多植物寿命减少)。如果当天选择浇水,则树苗当天高度立即+6(树苗吸水增长)。
选择摘果子,可以输入摘果数量,该数量不能大于结果数。
你的目标:使得树苗高度达到可以售卖的标准200cm。同时天数越短越好。达到200cm后的果树:苹果与橘子单个售价均为$1, 卖果子所得的钱将作为你的额外收入。收入越多越好。
注:超过200cm可以使用修剪功能减小树苗高度,但是剪切长度超过树苗高度,树苗即死亡。
我们来分析红色变动部分:
两棵树的初始高度和生命值现在都不一样了,显然,不能够在类中对高度和生命值进行统一初始化了,这里我们引入“构造函数”。请看代码:
[C++] 纯文本查看 复制代码
//代码3-5
#include <stdio.h>
const int HEIGHT_STANDARD = 200; //植物标准高度

class Plant//植物类
{
public: //公开类中所有成员
	int height = 0;
	int health = 0;
	int apple = 0;
	int orange = 0;
	Plant(int vHealth, int vHeight);           //构造函数
	void Cut(int iCuttOffHeightByCentiMeter);  //剪切植物
	void Water(int iWaterQuantityByLitery);    //浇水
	void PickFruit(int howManyFruits);         //摘果子
};

Plant::Plant(int vHealth, int vHeight)
{
	health = vHealth;
	height = vHeight;
}

void Plant::Cut(int iCuttOffHeightByCentiMeter) //成员函数
{
	if (height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		height -= iCuttOffHeightByCentiMeter; //剪切
	else
		health = 0; //被剪死了
}

void Plant::Water(int iWaterQuantityByLitery) //成员函数
{
	int Orghealth = health; //记录浇水前的生命值
	health += iWaterQuantityByLitery; //生命值等于浇水水量
	health += 6; //每次浇水高度+6
	if (health > 100) //生命值大于100
		health = Orghealth + 100 - health; //生命值倒扣
	if (health < 0) //淹死了
		health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", health - Orghealth); //显示浇水后生命值以及高度的变化
}

void Plant::PickFruit(int howManyFruits) //成员函数
{
	int Apple = apple;   //苹果数
	int Orange = orange; //橘子数
						 //摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		orange -= howManyFruits;
}

void HangARealDay(Plant* AppleTree, Plant* OrangeTree) //全局函数
{
	AppleTree->health -= 15 + AppleTree->apple * 2;
	OrangeTree->health -= 15 + OrangeTree->orange * 2;
	printf("\tHealth -15\n");
	if (AppleTree->health > 20)
		AppleTree->height += 10;
	if (OrangeTree->health > 20)
		OrangeTree->height += 10;
	if (AppleTree->health >= 30)
		AppleTree->apple++;
	if (OrangeTree->health >= 60)
		OrangeTree->orange++;
	printf("Reports for apple tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", AppleTree->height, AppleTree->health, AppleTree->apple);
	printf("Reports for orange tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", OrangeTree->height, OrangeTree->health, OrangeTree->orange);
}

int main()
{
	int iOption; //操作选择
	int subOption;

	int Day = 0; //天数

	Plant* AppleTree = new Plant(100, 85);  //构建苹果树时使用类的构造函数,给参:生命值、高度
	Plant* OrangeTree = new Plant(90, 105); //构建橘子树时使用类的构造函数,给参:生命值、高度
	Plant* TargetPlant;

	printf("Welcome!\nNow you have double saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (1)
	{
		Day++;
		printf("Days %d\n", Day);
		HangARealDay(AppleTree, OrangeTree);

		if (AppleTree->health <= 0 || OrangeTree->health <= 0)
			goto Lbl_Game_Over;
		printf("Choose a plant to operate.[0] for apple tree,[1] for orange tree.\n");
		scanf("%d", &iOption);
		if (!iOption && AppleTree->height != HEIGHT_STANDARD)
			TargetPlant = AppleTree;
		else if (iOption == 1 && OrangeTree->height != HEIGHT_STANDARD)
			TargetPlant = OrangeTree;
		else
			goto Lbl_Pass_Anyway;

		printf("[1]Water.[2]Cut.[3]Pick Fruits.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &subOption);
			TargetPlant->Water(subOption); //省略THIS
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &subOption);
			TargetPlant->Cut(subOption); //省略THIS
			break;
		case 3:
			printf("\tInput quantity to pick up:");
			scanf("%d", &subOption);
			TargetPlant->PickFruit(subOption); //省略THIS
			break;
		case 0:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You have done nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale! Any you got $%d for pocket money!\n", Day, AppleTree->apple + OrangeTree->orange);
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

以上代码中,我们使用了构造函数来修改初始值。构造函数是和类同名的函数,而且构造函数的类中声明与类外实现不能加返回值说明。
然而“构造函数”不是浪得虚名,我们还可以这样创建Plant类的实例:
        Plant AppleTree(100, 85);
        Plant OrangeTree(90, 105);
在建造AppleTree和OrangeTree时,直接在对象名称后面带参的方式也是调用了构造函数,构造出了AppleTree与OrangeTree。
如果类中成员涉及到了动态内存管理。比如这样一个类:
[C++] 纯文本查看 复制代码
//代码3-6
#include <iostream>
#include <string.h>
using namespace std;

class Str
{
public:
	char* ptrStr = NULL;
	Str(char* vStr);
	~Str();
};

Str::Str(char* vStr)
{
	ptrStr = (char*)malloc(sizeof(char) * (strlen(vStr) + 1));
	strcpy(ptrStr, vStr);
}

Str::~Str()
{
	free(ptrStr);
}

int main()
{
	Str varString = "abcdefg";
	cout << varString.ptrStr << endl;
	return 0;
}

如果我们不使用varString实例,而Str类中ptrStr指针又没有被释放,内存会逐渐堆积起来直到泄露。在与类名相同的函数前加上“~”符号的函数叫做“析构函数”。析构函数在类实例化出来的对象被销毁时自动调用。析构函数是用来“扫尾”的。举个骇人的例子:构造函数“接生”对象。析构函数“打理后事”,“安葬”对象,免得对象死后妨碍后人。
在C++之父的努力下,一个面向对象的C诞生了。它不仅支持C的全部属性用法,而且让C具备了更完善的描述世界的能力。
In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

85

主题

260

帖子

3763

积分

用户组: 管理员

No. 418

UID
418
精华
13
威望
52 点
宅币
1969 个
贡献
1365 次
宅之契约
0 份
在线时间
252 小时
注册时间
2014-8-9
 楼主| 发表于 2015-10-17 21:30:18 | 显示全部楼层
第四篇:人民内部问题

卜算子 OOP
面向过程边
踌躇止不前
已是信息爆炸世
重构来调校
造出结构类
包罗万象妙
待到革命完成时
却有问题闹。
我们来研究代码3-5里边的类:
将代码3-5的类单独拿出来:
[C++] 纯文本查看 复制代码
class Plant//植物类
{
public: //公开类中所有成员
	int height = 0;
	int health = 0;
	int apple = 0;
	int orange = 0;
	Plant(int vHealth, int vHeight);           //构造函数
	void Cut(int iCuttOffHeightByCentiMeter);  //剪切植物
	void Water(int iWaterQuantityByLitery);    //浇水
	void PickFruit(int howManyFruits);         //摘果子
};

我们可以看到,访问植物属性用的是什么办法呢?对了,直接读取公有属性height,health。这样做并不是很好的。因为如果误设了属性变量值,比如将health设置为负数。或者在if判断语句中不小心将==写成了=比如 if (MyPlant.height = 200) 然后导致了bug。怎么解决?将保存属性的变量与属性的读取设置过程分离开来,我们一起改造原先的类:
[C++] 纯文本查看 复制代码
//代码4-1
class Plant//植物类
{
public:
	int height = 0;
	int health = 0;
	int apple = 0;
	int orange = 0;
	Plant(int vHealth, int vHeight);           //构造函数
	void Cut(int iCuttOffHeightByCentiMeter);  //剪切植物
	void Water(int iWaterQuantityByLitery);    //浇水
	void PickFruit(int howManyFruits);         //摘果子
	bool ReduceHeight(int Height);             //消减高度
	bool ReduceHealth(int Health);             //消减生命值
	bool IncreaseFruit(int FruitsNumber);      //结果子
	int GetHeight(void)                        //取得高度
	{
		return height;
	}
	int GetHealth(void)                        //取得生命值
	{
		return health;
	}
	int GetAppleFruit(void)                    //取得苹果数量
	{
		return apple;
	}
	int GetOrangeFruit(void)                   //取得橘子数量
	{
		return orange;
	}
};

这样子修改以后我们可以调用GetXXX函数获取属性值,调用ReduceXXX函数消减高度和生命值,调用IncreaseFruit函数增加果子数量。
好了,问题来了。我们如何隐藏health,height,apple,orange属性避免其被误访问?办法是将其锁入private权限:
[C++] 纯文本查看 复制代码
//代码4-2
#include <stdio.h>
const int HEIGHT_STANDARD = 200; //植物标准高度

class Plant//植物类
{
private:
	int height = 0;
	int health = 0;
	int apple = 0;
	int orange = 0;
public:
	Plant(int vHealth, int vHeight);           //构造函数
	void Cut(int iCuttOffHeightByCentiMeter);  //剪切植物
	void Water(int iWaterQuantityByLitery);    //浇水
	void PickFruit(int howManyFruits);         //摘果子
	bool ReduceHeight(int Height);             //消减高度
	bool ReduceHealth(int Health);             //消减生命值
	bool IncreaseFruit(int FruitsNumber);      //结果子
	int GetHeight(void)                        //取得高度
	{
		return height;
	}
	int GetHealth(void)                        //取得生命值
	{
		return health;
	}
	int GetAppleFruits(void)                    //取得苹果数量
	{
		return apple;
	}
	int GetOrangeFruits(void)                   //取得橘子数量
	{
		return orange;
	}
};

Plant::Plant(int vHealth, int vHeight)
{
	health = vHealth;
	height = vHeight;
}

void Plant::Cut(int iCuttOffHeightByCentiMeter) //成员函数
{
	if (height - iCuttOffHeightByCentiMeter >= 0) //若剪切后的高度大于0
		height -= iCuttOffHeightByCentiMeter; //剪切
	else
		health = 0; //被剪死了
}

void Plant::Water(int iWaterQuantityByLitery) //成员函数
{
	int Orghealth = health; //记录浇水前的生命值
	health += iWaterQuantityByLitery; //生命值等于浇水水量
	health += 6; //每次浇水高度+6
	if (health > 100) //生命值大于100
		health = Orghealth + 100 - health; //生命值倒扣
	if (health < 0) //淹死了
		health = 0;
	printf("\thealth+ %d\n\tHeight+ 6\n", health - Orghealth); //显示浇水后生命值以及高度的变化
}

void Plant::PickFruit(int howManyFruits) //成员函数
{
	int Apple = apple;   //苹果数
	int Orange = orange; //橘子数
						 //摘果
	if (Apple > 0 && Apple - howManyFruits >= 0)
		apple -= howManyFruits;
	if (Orange > 0 && Orange - howManyFruits >= 0)
		orange -= howManyFruits;
}

bool Plant::ReduceHeight(int Height)
{
	if (this->height - Height >= 0)
		return false;
	else
		this->height -= Height;
	return true;
}

bool Plant::ReduceHealth(int Health)
{
	if (this->health - Health >= 0)
		return false;
	else
		this->health -= Health;
	return true;
}

bool Plant::IncreaseFruit(int FruitsNumber)
{
	if (FruitsNumber >= 0)
	{
		apple += FruitsNumber;
		orange += FruitsNumber;
		return true;
	}
	else
		return false;
}

void HangARealDay(Plant* AppleTree, Plant* OrangeTree) //全局函数
{
	AppleTree->ReduceHealth(-(15 + AppleTree->GetAppleFruits() * 2));
	OrangeTree->ReduceHealth(-(15 + OrangeTree->GetOrangeFruits() * 2));
	printf("\t- Health\n");
	if (AppleTree->GetHeight() > 20)
		AppleTree->ReduceHeight(-10);
	if (OrangeTree->GetHealth() > 20)
		OrangeTree->ReduceHeight(-10);
	if (AppleTree->GetHealth() >= 30)
		AppleTree->IncreaseFruit(1);
	if (OrangeTree->GetHealth() >= 60)
		OrangeTree->IncreaseFruit(1);
	printf("Reports for apple tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", AppleTree->GetHeight(), AppleTree->GetHealth(), AppleTree->GetAppleFruits());
	printf("Reports for orange tree:\n\tHeight: %d/200\n\tHealth: %d/100\n\tFruits: %d\n", OrangeTree->GetHeight(), OrangeTree->GetHealth(), OrangeTree->GetOrangeFruits());
}

int main()
{
	int iOption; //操作选择
	int subOption;

	int Day = 0; //天数

	Plant* AppleTree = new Plant(100, 85);  //构建苹果树时使用类的构造函数,给参:生命值、高度
	Plant* OrangeTree = new Plant(90, 105); //构建橘子树时使用类的构造函数,给参:生命值、高度
	Plant* TargetPlant;

	printf("Welcome!\nNow you have double saplings.\n");
	printf("Begin your career as a gardener, and enjoy the game.\n");

	while (1)
	{
		Day++;
		printf("Days %d\n", Day);
		HangARealDay(AppleTree, OrangeTree);

		if (AppleTree->GetHealth() <= 0 || OrangeTree->GetHealth() <= 0)
			goto Lbl_Game_Over;
		printf("Choose a plant to operate.[0] for apple tree,[1] for orange tree.\n");
		scanf("%d", &iOption);
		if (!iOption && AppleTree->GetHeight() != HEIGHT_STANDARD)
			TargetPlant = AppleTree;
		else if (iOption == 1 && OrangeTree->GetHeight() != HEIGHT_STANDARD)
			TargetPlant = OrangeTree;
		else
			goto Lbl_Pass_Anyway;

		printf("[1]Water.[2]Cut.[3]Pick Fruits.[0]Give it up.\n");
		scanf("%d", &iOption);
		switch (iOption)
		{
		case 1:
			printf("\tInput water quantity:");
			scanf("%d", &subOption);
			TargetPlant->Water(subOption); //省略THIS
			break;
		case 2:
			printf("\tInput height to cut:");
			scanf("%d", &subOption);
			TargetPlant->Cut(subOption); //省略THIS
			break;
		case 3:
			printf("\tInput quantity to pick up:");
			scanf("%d", &subOption);
			TargetPlant->PickFruit(subOption); //省略THIS
			break;
		case 0:
			goto Lbl_Exit;
		Lbl_Pass_Anyway:
		default:
			printf("You have done nothing?\n");
		}
	}
	printf("%d Days,All plants were fit to sale! Any you got $%d for pocket money!\n", Day, AppleTree->GetAppleFruits() + OrangeTree->GetOrangeFruits());
	return 0;
Lbl_Exit:
	printf("Wont you keep going on it?\n");
	return 0;
Lbl_Game_Over:
	printf("One of your plants was die.");
	return 0;
}

同样,类内部有函数不想公开的话,也可以锁入private权限。
至此,我们解决的人们内部“资源所有”问题的一半,即“私有财产圣神不可侵犯”。现在我们要在类的众多实例当中实行共产。
请看问题:如果我要让所有的同一个类造出来的实例都能访问一个变量,该怎么做?可能大家会回答:这还不简单,在类外部定义变量即可。那么我要求在类内部定义呢?这时大家打脸:意义何在?回答:如果我有“动物类(class Animal)”和“植物类(class Plant)”我是上帝,定义在一年(365天)的春季初(3月,差不多一年开始第90天)动物发情植物授粉。现在我要把所有的“动物类”的实例动物全部搬到南半球,植物都在北半球。南半球的春天是北半球的冬天。差不多发情期在第270天左右。好了现在设置两个共有变量: int植物授粉期 = 90;int 动物发情期 = 90; 在main函数中改:动物发情期 = 270; XXX;XXX;。如此这般我还不如在两个类中都申明一个静态变量static int Estrus = 90; 然后呢我只要改Animal类的一个实例,比如Cat,Cat.Estrus = 270;这时候Animal的实例不管是老虎还是狼,它们的发情期都变成了270.
为了好理解,我简单写了一下代码:
[C++] 纯文本查看 复制代码
//代码4-3
#include <iostream>
using namespace std;

class Animal
{
private:
	int health = 100;
	int XXX;
public:
	static int estrus;

	void Kill()
	{
		health = 0;
	}
};

class Plant
{
private:
	int health = 100;
	int XXX;
public:
	static int estrus;

	void Kill()
	{
		health = 0;
	}
};

int Animal::estrus = 90; //使用静态成员变量必须用这种方式进行静态成员变量的初始化
int Plant::estrus = 90;  //使用静态成员变量必须用这种方式进行静态成员变量的初始化

int main()
{
	Plant Tree;
	Plant Grass;
	Animal Cat;
	Animal Tiger;
	Animal Wolf;
	Animal Dog;
	
	cout << "Cat: " << Cat.estrus << endl;
	cout << "Dog: " << Dog.estrus << endl;
	cout << "Cat: " << Wolf.estrus << endl;
	Dog.estrus = 270;
	cout << "Cat: " << Cat.estrus << endl;
	cout << "Dog: " << Dog.estrus << endl;
	cout << "Cat: " << Wolf.estrus << endl;
	return 0;
}

为啥要对静态成员变量进行初始化呢?
静态成员变量在实例化的时候并不是每实例化一次就为其分配一次内存。其实静态成员变量/函数就是全局的,只不过作用于这个类,受用于这个类的所有实例对象。
革命成功后随之而来的就是对政权的巩固,然而这个过程并不是一天两天,十年八年。甚至要维持到王朝结束的前一天。我们暂时解决的了人民的内部问题:分配了资产,个人的给个人,大家的大家共享。但是有一句话:One Problem Finished, Another Always Come.接下来的几章中,老C继续带大家探索实践发现“面向对象”。
In the beginning I was not the best.
And the world was also not the best.
But I still know that I am who I am.
Because I think that it is good.
I have been working hard.
I have been keeping growth with the world.
And it was so.

0

主题

41

帖子

45

积分

用户组: 初·技术宅

UID
3351
精华
0
威望
2 点
宅币
0 个
贡献
0 次
宅之契约
0 份
在线时间
0 小时
注册时间
2018-1-14
发表于 2018-1-14 15:35:20 | 显示全部楼层
可以可以!!
回复

使用道具 举报

本版积分规则

QQ|申请友链||Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图  

GMT+8, 2020-7-11 18:56 , Processed in 0.111822 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表