C++复习:一个小例子复习基础

前言

经过两周的学习,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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值