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

Am I creating my object arrays correctly? C++

本文关键字:对象 数组 C++ 创建 是否      更新时间:2023-10-16

我正在尝试创建一个程序,该程序最多可存储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;
    }
}
}
}

编辑:附言,我所有的其他功能/案例都起作用。我就是好像找不出这个有什么问题!我需要使用矢量吗?

第二版:谢谢你的建议。现在,有没有办法检查文件是否还有数据要输入?现在,它输入线条,这样无论发生什么,都有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。

顺便说一句,对于你的玩家类来说,预见一个默认的构造函数是一个很好的做法

本质上有两个方面:

A(

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

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

B(

编辑:[…]我需要使用矢量吗?[…]现在,它输入线条,这样无论发生什么,都有50个玩家对象。

这与分配和容器有关。

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


然而,这两个方面与提供给应用程序的控制器的功能几乎没有关系,该功能尚未通过main()中的循环来实现。

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

让我们把那个类称为PlayerDataBase


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

Complile时间固定大小的阵列,如

Player c_arra_players[50];
// or
std::array<Player,50> std_array_players;

反正都是假的;存在一个任意实体计数的外部基准,该外部基准支配多重性;因此容器必须支持运行时重新调整大小。

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::unordereded_map’将与完美散列函数一起使用,则大约为O(1(。

另一方面,添加,尤其是重命名播放器(用例:修复缺口中的拼写错误(对于关联容器来说将比顺序容器更昂贵。

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


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


ifstream/ofstream/fstream问题可以通过PlayerDataBase非常简单地处理——它的(私有(ctor读取或创建并读取ifstream中的统计文件,该文件在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 resp. istream的输出获得一堆(noexcept(运算符和一些相关的自定义运算符。

黑客快乐!

2014年7月20日补遗无论如何,我看不出有太多理由进行随机序列访问,因此如果仍然使用序列容器(由于查找复杂性,我不主张这样做(,那么std::list将更合适;这节省了std::vector可能遭受的完全且昂贵的重新分配的成本。