繁体   English   中英

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

[英]Am I creating my object arrays correctly? C++

我正在尝试创建一个程序,该程序最多可存储50名玩家及其所获的胜利。 也就是说,一种用途可能是跟踪运动队及其获胜的数量或其他东西。 但是,更改玩家分数时出现问题。 我是C ++的初学者,您可能可以从我的代码中看出来。 但是,我从对编码一无所知,到大约三个星期完成了三个不同的教程并编写了几个非常简单的命令行程序。 令人欣慰的是,就编码技巧以及如何使scoreEdit()函数起作用提供了任何帮助! 请找到附带的players类,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;
    }
}
}
}

编辑:PS我所有其他功能/情况下工作。 我似乎找不到这个问题! 我需要使用向量吗?

编辑2:感谢您的所有建议。 现在,有没有一种方法可以检查文件是否还有剩余数据可输入? 现在,它只是输入行,以便无论如何都可以有50个玩家对象。 我可以像int linesLeftInFile这样的变量并将其for (int x=0; x<linesLefInFile; x++)吗? 对不起所有初学者的问题。

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

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

当您打开它进行写操作时,文件内容将被删除。 读取文件的尝试将失败,从而导致混乱。

首先只打开流以读取,然后关闭流,然后打开以进行写入并放入结果。 或者,您可以考虑具有读写功能的fstream。

顺便说一句,对于您的玩家类来说,预见默认构造函数将是一个好习惯。

本质上有两个方面:

一种)

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

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

B)

编辑:[...]我需要使用向量吗? [...]现在,它只输入行,因此无论如何都存在50个玩家对象。

与分配和容器有关。

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


但是,这两个方面都与呈现给应用程序控制器的功能无关,而功能仍由main()的循环实现。

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

让我们将其称为PlayerDataBase


PlayerDataBase将保存一个容器。 但是哪个呢?

Comlile时间固定大小的数组,例如

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))且约为如果将std :: unordered_map`与完美的哈希函数一起使用,则为O(1)。

另一方面,与连续性容器相比,关联性的增加和添加(尤其是重命名)播放器(用例:修正拼写错误)将变得更加昂贵。

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


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


通过PlayerDataBase可以轻松解决ifstream/ofstream/fstream问题-它是(私有)ctor从ifstream中读取或创建和读取stats文件,该文件在ctor完成后关闭。

持久化数据是通过flush()函数完成的,该函数使用_file_name成员打开一个ofstream ,然后进行写入,并在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并且>>' for ostream的响应获取了一大堆(noexcept)ctor和一些相关的自定义运算符<< resp. istream的输出。

骇客入侵!

附录2014年7月20日我仍然没有太多理由可以进行随机序列访问,因此, 如果仍然使用顺序容器(由于发现的复杂性,我不建议这样做),那么std::list会更合适; 这样可以节省可能的完整且昂贵的重新分配std::vector成本。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM