前言
经过两周的学习,C++基础部分基本已经有了一定的了解。正好前两天在群里看到一个哥们问了一个很有代表性的需求,于是决定以这个需求为例子,对现阶段所学知识做一个复习巩固。
需求
需求内容
游戏中有一个推荐好友的功能,每推荐成功一个玩家自己都会获得奖励,并且推荐自己的玩家也会获得一定比率的分成,同时推荐他的玩家也会获得他分成的一定比率分成,直至没有推荐者或者最小分成为止。恩,你想的没错,就是传销模式。
需求要求推荐信息以文本保存,格式如下:
1001 1002
1001 1003
1003 1004
……
每一行的前一个是推荐者的编号,后一个是被推荐者的编号。
输出信息要求如下:
1003推荐了1004 1003获得了10金币 1001获得了2金币
也就是说每一行先输出推荐信息,然后从自身开始逐级输出获得的奖励,直至结束
最后,还要求输出所有奖励的金币总数。
需求分析
这个例子虽然简单,但是包含了读取数据,存储,输出,并且数据之间还有一定的关系,并不是简单的一个容器存起来那么简单(当然,一定要这么做也可以,但是使用关联容器可能用起来更顺手)。
数据读取:比较简单,fstream可以实现。
数据存储:由于玩家人数并不固定,并且玩家需要通过编号查询,所以建立一个字典是必须的,C++中是map。当然,在这之前还需要建立一个玩家类。由于玩家可能有一个推荐者(上线),和若干个被推荐者(下线),所以上线用一个string存储,下线用一个string的vecotr容器存储,使用的时候通过该tring去字典里面获取。当然通常我们还需要保存玩家的姓名,id,由于涉及到奖励,还可以将该玩家获得的总金币数保存下来。
数据输出:这一点有点绕。由于输出的时候需要输出玩家及其所有上线的获得的奖励,而玩家有几级上线我们并不清楚,所以可以考虑使用递归来让系统自动追踪最顶级的上线,而系统的上线分成按比率逐级递减也符合这一条件。
实现
静态数据
由上述需求分析可知,推荐者获得的金币数量是固定的,上线递减比率也是固定的,金币递归的最小额度也是固定的,于是可以将其用常量存储起来方便修改。
而所有玩家(people类,下面会给出)需要通过名字进行查询,于是通过一个map<string, people>
来实现。
static const int award = 25;//最大金币获得量
static const double discount = 0.2;//金币获得递减比率
static const int minimum = 1;//最小金币获得数量
static map<string, people> AllPeople = {};//玩家字典
玩家类
玩家类中需要包含姓名,上线下线,添加下线的方法(上线在构造函数中添加,无序动态改变),以及一个递归获取金币的方法:
此处用struct,与c#中struct不同的是,c++中struct与class的唯一区别是在遇到第一个访问控制符之前,struct成员是public的,而class成员是private的,所以怎么方便怎么来,无需考虑性能的区别。
struct people{//玩家
people() = default;
people(string n) :name(n){ money = 0; }
people(string n, string p) :name(n), upPeople(p){ money = 0; }
string name;//姓名
int money;//该玩家总金币数
string upPeople;//上线
vector<string> downPeople;//下线
void addDownPeople(string p){//添加下线
downPeople.push_back(p);
}
int addMoney(int m){//递归获得金币
money += m;
cout << name << "获得了:" << m << "金币 ";
if (upPeople!=""&&(m*discount >= minimum))//递归条件
return m + AllPeople[upPeople].addMoney(static_cast<int>(m*discount));
else{//终结条件
cout << endl;
return m;
}
}
};
玩家管理器类
直接调用玩家数据当然可以实现需求,但是为了方便客户端使用,降低耦合并尽量少的暴露类信息,通常使用一个管理器来对数据进行管理,客户端只需要调用管理器中对应的接口来处理数据即可。
目前管理器仅包含两个方法:添加玩家和输出金币,由于需要统计金币的总消耗量,所以需要返回一个金币值
struct peopleManager//玩家操作管理器
{
public:
static void addPeople(string s){//根据行添加玩家
int f = s.find(" ");
addPeople(string(s,0,f),string(s,f+1));
}
static void addPeople(string up, string down){//根据玩家姓名添加玩家
if (AllPeople.find(up) == AllPeople.end())
AllPeople.insert({ up, people(up) });
AllPeople.insert({ down, people(down, up) });
AllPeople[up].addDownPeople(down);
}
static int appMoney(int money){//按玩家关系给予奖励
int num=0;
for (auto p : AllPeople)
for (auto s : p.second.downPeople){
cout << p.second.name << "推荐了" << s<<" ";
num += p.second.addMoney(money);//递归调用
}
return num;
}
};
数据文件
由于只是练习测试用,创建一个test.txt保存在程序的根目录下,文件内容如下:
1001 1002
1001 1003
1001 1004
1002 1005
1002 1006
1003 1007
1005 1008
使用
现在准备工作做好了,我们可以使用了。
读取文件:
ifstream inFile("test.txt");//文件读取流
录入玩家数据:
string line;
while (getline(inFile, line))//读取一行文件
peopleManager::addPeople(line);
inFile.close();
输出金币数据:
cout <<"总金币数量为:"<< peopleManager::appMoney(award) << endl;
运行结果:
转载请注明出处:http://blog.csdn.net/ylbs110/article/details/50984578
其他
到此为止,需求已经实现了,但是实现需求本身不是目的,目的是为了在实现需求的时候巩固所学知识。
在目前的代码中,对类,函数,语句,关联容器,文件读取都有所涉及,但是对C++中具有特色的引用,指针,迭代器却并未涉及到,于是需要对代码做一些修改。
修改一:
for (auto s : p.second.downPeople){
cout << p.second.name << "推荐了" << s<<" ";
num += p.second.addMoney(money);//递归调用
}
改为
//for_each使用,迭代器使用,lambda使用,引用,指针
for_each(p.second.downPeople.begin(),p.second.downPeople.end(),
[&p, &num, money](string s){cout << p.second.name << "推荐了" << s << " "; num += p.second.addMoney(money); });
这么一改,复习了泛型方法,迭代器,指针,引用。具体用法我就不细细分析了,自己查询一下也可以加深印象。
修改二:
由于每个玩家的下线玩家不会重复,所以可以采取set而不是vector。
vector<string> downPeople;//下线
void addDownPeople(string p){//添加下线
downPeople.push_back(p);
}
改为
set<string> downPeople;//下线
void addDownPeople(string p){//添加下线
downPeople.insert(p);
}
修改三:
将people除默认构造方法外改为私有,通过友元来让玩家管理器调用,这样复习了友元,代码也更加符合开闭原则:
struct people{//玩家
friend class peopleManager;//友元使用
people() = default;
private:
people(string n) :name(n){ money = 0; }
people(string n, string p) :name(n), upPeople(p){ money = 0; }
string name;//姓名
int money;//该玩家总金币数
string upPeople;//上线
vector<string> downPeople;//下线
void addDownPeople(string p){//添加下线
downPeople.push_back(p);
}
int addMoney(int m){//递归获得金币
money += m;
cout << name << "获得了:" << m << "金币 ";
if (upPeople!=""&&(m*discount >= minimum))//递归条件
return m + AllPeople[upPeople].addMoney(static_cast<int>(m*discount));
else{//终结条件
cout << endl;
return m;
}
}
};
所有代码
#include "stdafx.h"
#include <iostream>
#include <string>
#include <map>
#include <set>
#include <vector>
#include <fstream>
#include <algorithm>
using namespace std;
struct people;
struct peopleManager;
static const int award = 25;//最大金币获得量
static const double discount = 0.2;//金币获得递减比率
static const int minimum = 1;//最小金币获得数量
static map<string, people> AllPeople = {};//玩家字典
struct people{//玩家
friend struct peopleManager;//友元使用
people() = default;
~people(){}
private:
people(string n) :name(n){ money = 0; }
people(string n, string p) :name(n), upPeople(p){ money = 0; }
string name;//姓名
int money;//该玩家总金币数
string upPeople;//上线
set<string> downPeople;//下线
void addDownPeople(string p){//添加下线
downPeople.insert(p);
}
int addMoney(int m){//递归获得金币
money += m;
cout << name << "获得了:" << m << "金币 ";
if (upPeople!=""&&(m*discount >= minimum))//递归条件
return m + AllPeople[upPeople].addMoney(static_cast<int>(m*discount));
else{//终结条件
cout << endl;
return m;
}
}
};
struct peopleManager//玩家操作管理器
{
public:
static void addPeople(string s){//根据行添加玩家
int f = s.find(" ");
addPeople(string(s,0,f),string(s,f+1));
}
static void addPeople(string up, string down){//根据玩家姓名添加玩家
if (AllPeople.find(up) == AllPeople.end())
AllPeople.insert({ up, people(up) });
AllPeople.insert({ down, people(down, up) });
AllPeople[up].addDownPeople(down);
}
static int appMoney(int money){//按玩家关系给予奖励
int num=0;
for (auto p : AllPeople)
for_each(p.second.downPeople.begin(),p.second.downPeople.end(),//for_each使用,迭代器使用
[&p, &num, money](string s){cout << p.second.name << "推荐了" << s << " "; num += p.second.addMoney(money); }//lambda使用
);
//for (auto s : p.second.downPeople){
// cout << p.second.name << "推荐了" << s<<" ";
// num += p.second.addMoney(money);//递归调用
//}
return num;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
string line;
ifstream inFile("test.txt");//文件读取流
while (getline(inFile, line))//读取一行文件
peopleManager::addPeople(line);
inFile.close();
cout <<"总金币数量为:"<< peopleManager::appMoney(award) << endl;
return 0;
}