我是否正确创建了对象数组? C ++

时间:2014-07-17 18:30:51

标签: c++ arrays function c++11 fstream

我正在尝试创建一个程序,可以存储多达50名玩家以及他们拥有的胜利数量。即一次使用可以用于跟踪运动队及其赢得的比赛数量等。但是,在更改球员得分时我遇到了问题。我是C ++的初学者,你可能会从我的代码中知道。但是,我已经从完全不知道编码到完成三个不同的教程,并在大约一周内制作了几个非常简单的命令行程序。感谢接受任何有关编码技巧以及如何使scoreEdit()函数正常工作的帮助!请找到附加的球员类,scoreEdit()函数和我的主函数。

// for case 1. class that the scoreEdit() function uses!
class players
{
public:
void setName(string x)
{
    name = x;
}
void addWin()
{
    amtOfWins += 1;
}
void setWins(int x)
{
    amtOfWins=x;
}
string getName()
{
    return name;
}
int getWins()
{
    return amtOfWins;
}
private:
string name;
int amtOfWins;
};

|

// for case 1. reads the file then stores each name in it's own player object and associates that with the amt of wins. Then rewrites all names and amtofwins to the file
void scoreEdit()
{
ifstream istats("stats.txt");
ofstream ostats("stats.txt");

if (istats.is_open() && ostats.is_open())
{
    players player[50];

    string tempName;
    int tempWins;

    while (istats >> tempName >> tempWins)
    {
        // reads in the name and amt of wins, and stores them in player object variables.
        for (int x=0; x<50; x++)
        {
            player[x].setName(tempName);
            player[x].setWins(tempWins);
        }
    }
    string winner;

    cout << "Who won?" << endl;
    cin >> winner;

    for (int x=0; x<50; x++)
    {
        if (player[x].getName()==winner)
        {
            player[x].addWin();
            cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
        }
    }
    int x=0;

    while (ostats << player[x].getName() << ' ' << player[x].getWins())
    {
        x++;
    }
}
else
{
    cout << "Quitting program. Stats could not be opened." << endl;
}
}

|

// main function
int main()
{
int x=0;
cout << "\n";

while (x==0) // not really sure if this is needed. Loops until case 4 or 5. Probably didn't need to use x. Oh well.
{
    switch (choices())
{
    case 0: // clears file
    {
        string decision;

        cout << "ARE YOU SURE? This will wipe all data. (Type yes or no)\n" << endl;
        cin >> decision;

        if (decision=="yes")
        {
            clearStats();
            cout << "STATS ARE WIPED.\n" << endl;
            break;
        }
        else if (decision=="no")
        {
            cout << "Ok, stats will not be wiped.\n" << endl;
            break;
        }
        else
        {
            cout << "Your input was not recognized. Stats will not be wiped.\n" << endl;
            break;
        }
    }
    case 1: // they want to add 1 to a player's score
    {
        scoreEdit();
        break;
    }
    case 2: // they want to add a player
    {
        string name;
        cout << "What is their name?" << endl;
        cin >> name;
        addPlayer(name);
        break;
    }
    case 3: // they want to view the stats
    {
        readStats();
        break;
    }
    case 4: // they want to quit. Simple! :)
    {
        return 0;
    }
    default: // means they did not input 1 2 3 or 4
    {
        cout << "Invalid input. Quitting program. Try again." << endl;
        return 1;
    }
}
}
}

编辑:P.S。我所有的其他功能/案例都有效。我似乎无法找到这个问题!我需要使用矢量吗?

编辑2:感谢您的所有建议。现在,有没有办法检查文件是否仍有输入数据?现在,它输入的行只是为了有50个玩家对象,无论如何。我可以制作像int linesLeftInFile这样的变量并使用for (int x=0; x<linesLefInFile; x++)吗?对不起所有初学者问题。

2 个答案:

答案 0 :(得分:3)

您试图打开文件两次,一次用于阅读,一次用于写入,但同时打开。

...
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
...

当您打开它进行写入时,文件内容将被删除。然后尝试读取文件将失败,因此大混乱。

首先打开您的流只读取,然后关闭流,然后打开进行写入并输出结果。或者,您可以考虑使用读写的fstream。

顺便说一下,你的玩家类预见默认构造函数是个好习惯。

答案 1 :(得分:0)

基本上有两个方面:

A)

  

我是否正确创建了对象数组? C ++

与持久性实现和实例生命周期问题有关,

B)

  

编辑:[...]我需要使用矢量吗?   [...]   现在,它输入的行只是为了有50个玩家对象,无论如何。

与分配和容器有关。

当然两个方面相交,例如像std::vector这样的容器对它所托管的实例的生命周期有很大的影响。


然而,这两个方面与提交给应用程序的controller的功能几乎没有关系,这已经通过main()中的循环实现。

因此,处理这些方面应该委托给一个将它们与应用程序的其余部分分开的类。

让我们称该课为PlayerDataBase


PlayerDataBase将容纳一个容器。但是哪个?

Comlile-time固定大小的数组,如

Player c_arra_players[50];
// or
std::array<Player,50> std_array_players;
无论如何,

离开了ansatz;有一个任意实体计数的外部数据,它控制着多样性;因此容器必须支持运行时重新调整大小。

std::vector<Player>简单易用,但是要进行搜索

   for (int x=0; x<50; x++) {
        if (player[x].getName()==winner)
        {
            player[x].addWin();
            cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
        }
    }

在像std::vector这样的顺序容器上会有O(n)复杂度,而对像std::map这样的关联(使用名称作为键)的相同搜索将是O(log( n))和关于O(1),如果std :: unordered_map`将与完美的哈希函数一起使用。

另一方面,添加并特别重命名玩家(用例:修复错误中的拼写错误)对于关联容器而言会变得更加昂贵。

特别是对于C ++学习者来说,使用不同的内部存储表示进行一些操作可能会很有趣,因此PlayerDataBase可以是带有容器模板参数的模板。


应用程序中应该只有一个PlayerDataBase实例,控制器应该只有一个访问该实例的点;因此它意味着是一个单身人士。


ifstream/ofstream/fstream问题可以通过PlayerDataBase安静地处理 - 它(私人)ctor读取或创建和读取来自ifstream的stats文件,该文件在完成了。

保留数据由flush()函数完成,该函数使用ofstream成员打开_file_name,写入,并且该流在flush终止时关闭。

///
///@brief modified singleton pattern
///    
template<template<typename...> class Cont>
class PlayerDataBase : private players_container_adaptor<Cont> {
  private: 
    using Traits  =  players_container_traits<Cont>;

  public: // type interface
    using container_type = typename Traits::players_container_type; 
    using Base =  players_container_adaptor<Cont>;

    struct no_such_player : public std::runtime_error {
        no_such_player(const std::string& msg) : std::runtime_error(msg) {}
    }; 

  public: // creation interface 
    static PlayerDataBase& instance(const std::string& file_name = ::FILE_NAME) 
                                                   throw (std::runtime_error) {
        // automatically dtored 
        static PlayerDataBase _instance_(file_name); // throws
        return _instance_;
    }

  public: // behaviour interface    
    void flush () const throw(std::ios::failure);
    void addWin(const std::string& key) throw (no_such_player);

  private: // types and function name resolution
    using Adaptor = Base;
    using Adaptor::getPlayer;     // throws std::runtime_error 
                                  // container specific lookup,   

    using Adaptor::make_inserter; // std::copy(..,..,,make_inserter(Cont & c));
    using Adaptor::emplace;       // dispatches to container_specific emplace 

    using _asset_type = typename Traits::asset_type; 

    constexpr static auto  BAD_AND_FAIL = std::ios::badbit | std::ios::failbit;
    constexpr static auto  BAD_ONLY     = std::ios::badbit;

    struct no_stats_file : public std::runtime_error {
        no_stats_file(const std::string& msg) : std::runtime_error(msg) {}
    }; 

  private: // lifecycle interface       
    PlayerDataBase(const std::string&) throw (std::runtime_error);
    ~PlayerDataBase() noexcept; 

    // This is a singleton
    PlayerDataBase(const PlayerDataBase&) = delete;
    PlayerDataBase(PlayerDataBase&&) = delete;
    PlayerDataBase& operator=(PlayerDataBase&&) = delete;
    PlayerDataBase& operator=(const PlayerDataBase&) = delete;
    PlayerDataBase& operator=(PlayerDataBase&) = delete;

  private: // helpers 
    void create_data_file() const throw(std::ios::failure);

  private: // state 
    container_type  _players; 
    std::string     _file_name;
}; // class PlayerDataBase

#include "playerdatabase.inl"

所需的特征和适配器模板可能如下所示:

template<template<typename...> class C> struct players_container_traits {};

// [...]

template<> struct players_container_traits<std::map> {
    using players_container_type = std::map<std::string, Player>;
    using player_ckv_type        = players_container_type::value_type;
    using player_kv_type         = std::pair<std::string, Player>;
    using asset_type             = player_kv_type;
}; // struct player_container_traits<std::map>

// [...]

template<template<typename...> class C> struct players_container_adaptor{};

// [...]

template<> struct players_container_adaptor<std::map> {
    using Traits = players_container_traits<std::map>;
    using players_map_type = Traits::players_container_type;

    static void post_ctor(players_map_type&) {
        /* nop  */
    }    

    static Player& getPlayer(players_map_type& m, const std::string& key) 
                                                throw (std::runtime_error) {
        auto it  = m.find(key);
        if (it == m.end())
            throw std::runtime_error(key + " unknown");

        return it->second;
    } // addWin(players_map_t&,...)  

    static auto make_inserter(players_map_type& m)
        -> decltype(std::inserter(m, m.begin())) {
        return std::inserter(m, m.begin());
    }

    template<typename... Targs, typename K, typename... Args>
    static void emplace(std::map<Targs...>& m, K&& k, Args&&... args) {
        K _k = k;
        m.emplace(std::piecewise_construct,
                    std::forward_as_tuple(_k),
                    std::forward_as_tuple(k, args...));
    }

}; // template<> struct players_container_adaptor<std::map> 

我假设业务类将重命名为Player并获得一堆(noexcept)ctors以及<< >>' for ostream resp. istream的一些相关自定义运算符`输出。

快乐的黑客攻击!

附录2014年7月20日 无论如何,我都没有看到随机序列访问的充分理由,因此如果仍然会使用顺序容器(由于查找复杂性我不会主张)然后std::list会更合适;这可以节省可能完成且昂贵的重新分配std::vector的成本。