I am trying to create a program that stores up to 50 players and the amount of wins they have. i.e. one use could be for for keeping track of sports teams and their amount of games won or something. However, I am having an issue when changing the player score. I am a beginner at C++, as you will probably be able to tell from my code. However, I have gone from knowing nothing whatsoever about coding to finishing three different tutorials and making a couple very simple command line programs in about a week. Any help as far as coding tips and how to make the scoreEdit() function work is thankfully accepted! Please find attached the players class, the scoreEdit() function, and my main function.
// 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;
}
}
}
}
EDIT: P.S. all my other functions/cases work. I just can't seem to find the problem with this one! Do I need to use a vector?
EDIT 2: Thanks for all the advice. Now, is there a way to check if the file still has data left to input? Right now, it inputs the lines just so that there are 50 player objects no matter what. Can I make a variable like int linesLeftInFile and use for (int x=0; x<linesLefInFile; x++)? Sorry for all the beginner questions.
You are trying to open the file twice, once for reading, once for writing, but in the same time.
...
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
...
When you open it for writing, like you do, the file content gets erased. The attempt to read the file will then fail, hence the big mess.
Open your stream first only to read, then close the stream, then open for write and put the results. Alternatively, you could consider an fstream with read and write.
By the way, it would be good practice for your players class to foresee a default constructor.
There are essentially two aspects:
A)
Am I creating my object arrays correctly? C++
which is related to persistence implementation and instance life-cycle issues, and
B)
EDIT: [...] Do I need to use a vector?
[...]
Right now, it inputs the lines just so that there are 50 player objects no matter what.
which is related to allocation and containers.
Both aspects of course intersect, e.g. a container like std::vector has much influence on the life-cycle of the instances it hosts.
Both aspects however have little to do with the functionality that is presented to the controller of the application, which is yet realized by the loop in main().
Thus, dealing with those aspects should be delegated to a class that separates them from the rest of the application.
Let's call that class-to-be PlayerDataBase.
PlayerDataBase will hold a container. But which?
Comlile-time fixed size arrays like
Player c_arra_players[50];
// or
std::array<Player,50> std_array_players;
are out of ansatz anyway; there is an external datum of arbitrary entity count which governs the multiplicity; thus the container must support run-time re-sizing.
A std::vector<Player> is plain and simple to use, but there's a seach to be performed
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;
}
}
which would have O(n) complexity on a sequential container like std::vector, whereas the same search on an associative like std::map (with the names used as keys) would be of O(log(n)) and about O(1) if std::unordered_map` would be used along with a perfect hash function.
On the other hand, adding and especially renaming players (use case: fixing typo in a nick) would be become more expensive for associative than sequential containers.
Especially for C++ learners it may be interesting to play a little with different internal storage representations, thus PlayerDataBase could be a template with a container template parameter.
There should only be one PlayerDataBase instance in the application and the controller should have a single point of access to that instance; thus it is meant to be a singleton.
The ifstream/ofstream/fstream issues can be dealt with quiet simple by PlayerDataBase - it's (private) ctor reads, or creates and reads, a stats file from an ifstream which is closed after the ctor completed.
Persisting the data is done by a flush() function, which opens an ofstream by using the _file_name member, writes, and that stream is closed on flush terminating.
///
///#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"
The required traits and adaptor templates could look like this:
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>
I postulated that the business class would be renamed to Player and gets a bunch of (noexcept) ctors and some related custom operators for << >>' forostreamresp.istream` output.
Happy hacking!
Addendum Jul 20th 2014
I don't see much reason for random sequence access anyway, thus if a sequential container would nevertheless be used (what I would not advocate due to the find complexity) then std::list would be more appropriate; that saves costs for possible complete, and expensive, re-allocations std::vector suffers from.
Related
I'm doing an assignment, where I have to create a database in c++ for a sport organization, without using STL (I created my own list and String). The database is keeping the data in a doubly linked list, where besides the nodes, there is the data, which are the teams. Of course there is not just one type of teams, there is currently three. These objects, inherited the teams objects. I have made everything work, except reading the text file, and thus creating objects.
I have already tried, to create a marker, which is the first piece of information the program will read, which will decide which of the three classes needs to be created, then reads the other data, to create the new object, then put it in the end of the doubly linked list. Unfortunately it does'nt work, instead it does nothing, and continue the whole programme, like nothing happened.
void hozzaad(ListaElem *s, team *data) { ///adding to the end
ListaElem *iter = s;
while (iter->kov=NULL)
{
iter = iter->kov;
}
ListaElem *uj = new ListaElem(data);
uj->elozo = iter;
iter->kov = uj;
}
void listaz(ListaElem *s) { //print out all that is in the list
if (s == NULL) {
std::cout << "Ures lista" << std::endl;
return;
}
ListaElem *iter = s;
while (iter!=NULL)
{
iter->adat->kiirt(std::cout);
iter = iter->kov;
}
}
void listament(ListaElem *s, const char *a) { //this one creates the file
std::ofstream file;
file.open(a);
ListaElem *iter = s;
if (file.is_open()) {
while (iter != NULL) {
file << iter->adat->Getclub()<< "\n";
file << iter->adat->Getname() << "\n" << iter->adat->Getmember()<< "\n";
if (iter->adat->Getclub() == 1) {
file << iter->adat->Getsupport() << "\n";
}
if (iter->adat->Getclub() == 2) {
file << iter->adat->Getpompom() << "\n";
}
if (iter->adat->Getclub() == 3) {
file << iter->adat->Getname1() << "\n" << iter->adat->Getname2() << "\n";
}
iter = iter->kov;
}
}
else
{
std::cout << "Nem tudom kinyitni a file-t";
}
file.close();
return;
};
void test4() { // the basic test
Handball c("Kezes HC", 21, 50000);
team adat("", 0);
ListaElem *egyik = new ListaElem(&adat);
hozzaad(egyik,&c);
ListaElem *uj = new ListaElem(&adat);
listament(egyik, "test.txt");
std::ifstream file;
file.open("test.txt");
if (!(file.is_open())) {
std::cout << "hiba\n";
return;
}
int micsoda;
while (file >> micsoda);
{
if (micsoda == 1) {
String beolvas("");
int m;
int d;
getline(file, beolvas);
file >> m;
file >> d;
Handball ujh(beolvas, m, d);
hozzaad(uj, &ujh);
beolvas = "";
}
if (micsoda == 2) {
String fbeolvas("");
int fm;
String e1("");
String e2("");
getline(file, fbeolvas);
file >> fm;
getline(file, e1);
getline(file, e2);
football ujh(fbeolvas, fm, e1, e2);
hozzaad(uj, &ujh);
}
if (micsoda == 3) {
String bbeolvas("");
int bm;
int bd;
getline(file, bbeolvas);
file >> bm;
file >> bd;
Basketball ujh(bbeolvas, bm, bd);
hozzaad(uj, &ujh);
}
}
std::cout << "OK" << std::endl;
listaz(uj);
file.close();
std::cout << "OK" << std::endl;
}
test4() expects to go out like:
OK
Kezes HC 21 50000
OK
Shortening the code to the absolutely necessary:
if (micsoda == 1)
{
Handball ujh;
hozzaad(uj, &ujh);
} // at this point in code, your object ujh runs out of scope!!!
At the time the object runs out of scope, it is destroyed (you will notice if you add some output statement in the class' destructor...) and any pointers to it get invalid; especially, those in the list get invalid (one speaks of dangling pointers – or references, for which the same can happen). Now using them results in undefined behaviour – which means anything could happen. If you're unlucky (or lucky, depending on point of view), your program even crashes.
Be aware that, due to lacking important parts of your code, I am assuming that you only store pointers to the objects in your ListaElem class, which is pretty likely, though, as the classes to be stored are polymorphic...
What you need, though, are objects living longer than just while the programme is in the if block. Assuming we target some real-life scenario, just moving the objects out of the if clauses is no option as we might need more than one single object of the same type. So you will create the objects dynamically. However, then you'll have to deal with memory management and the ownership question as well. Easiest to handle the memory management part is using smart pointers. Ownership? Well, it appears reasonable to me to assume the list being the sole owner of the objects, so you could have:
class ListElem
{
std::unique_ptr<Team> m_data;
public:
ListElem(std::unique_ptr<Team> data) // accepting a unique_ptr already here indicates
// clearly that the item will grab ownership
: m_data(std::move(data)) // unique_ptr is only movable, not copiable!
{ }
}
Then you can change your code above to:
if (micsoda == 1)
{
int n, m; // sample data
hozzaad(uj, std::make_unique<Team>(n, m); // arguments are passed directly to
// the constructor of class Team
}
OK, you are not allowed to use STL; then you'd write your smart pointers on your own, as well as make_unique and move template functions (just as you did for list and string already). On cppreference, you can find a sample implementation, e. g. for std::make_unique. Don't just copy/paste the code, but understand it first, and best re-write it from scratch on your own, otherwise you won't be learning anything (same applies for my code above). Or maybe you ask your teacher if she/he makes an exception on STL for smart pointers.
Final advice: Operating on contiguous memory in general is much faster than operating on memory potentially distributed all over your system RAM. So you might consider rather re-implementing std::vector instead of std::list.
// In my Class A, I have many nodes and every node data is stored in a struct like this:
Class A
{
private:
struct BriteNodeInfo
{
int nodeId;
double xCoordinate;
double yCoordinate;
int inDegree;
int outDegree;
int asId;
std::string type;
};
};
// Each node instance is stored in a vector like this:
typedef std::vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
//And then, here is the function that I want to implent down below
void SaveNodeData (std::string fname);
};
Problem: How do I implent that SaveNodeData() function to save my nodes data in .txt file like this?:
nodeId0 yCoordinate0 xCoordinate0
nodeId1 yCoordinate1 xCoordinate1
nodeId2 yCoordinate2 xCoordinate2
nodeId3 yCoordinate3 xCoordinate3
etc...
I have tried but my iteration syntax is not good enough. Here is my function, please help:
Here is my failed function:
void SaveNodeData (std::string fname)
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList;
BriteNodeInfoList m_briteNodeInfoList;
for (BriteNodeInfoList::Iterator i = m_briteNodeInfoList.Begin(); i != m_briteNodeInfoList.End(); ++i)
{
os << BriteNodeInfo[i].nodeId "\t" << "\t" << BriteNodeInfo[i].yCoordinate; << "\t"BriteNodeInfo[i].xCoordinate<< "\n";
}
os << "\n";
}
Before starting, this code as written will clearly have some compile errors. But assuming you can manage to fix those issues, there's one huge flaw.
Your function SaveNodeData creates an empty BriteNodeInfoList, and then tries to read from it. The for loop you have written will always simply exit.
What you need to do is create and populate a BriteNodeInfoList somewhere that this function reads. You could pass it in as an argument to the function, have it as a private variable for class A (assuming SaveNodeData is made into a member of class A). Or you could make it a static member variable of class A (not really recommended: static member variables of objects have some serious problems).
void SaveNodeData (std::string fname) // file path
{
ofstream os(fname.c_str(), ios::trunc);
vector<BriteNodeInfo> BriteNodeInfoList = m_briteNodeInfoList;
for (std::vector<BriteNodeInfoList>::Iterator it = BriteNodeInfoList.Begin(); it != BriteNodeInfoList.End(); ++it)
{
os << (*it).nodeId << "\t" << (*it).yCoordinate << "\t" << (*it).xCoordinate << "\n";
}
}
I'm new to programming and just come across this assignment
Create a container class family that holds an array of 20 person objects.
I have been looking on the internet as well as in my book, but I still can't figure out the difference between a Container Class and a Class in C++.
How could I create a family class and 20 person objects at the same time?
"Container class" is not some official term; it's just the word "class" with an English describing word next to it. The assignment is asking you to create a class that contains some other stuff; namely, an array of 20 person objects.
At its most basic, the result could be as simple as this:
class family
{
public:
person people[20];
};
In real life, you might do something like this instead:
#include <array>
using family = std::array<person, 20>;
It seems unlikely that every family (or even most families) has precisely 20 people in it, so I would personally end up just going with:
#include <vector>
std::vector<person> family;
… and manipulating the vector as appropriate.
C++ is an object oriented language that encourages you to "encapsulate". The first step of this is to group concepts into objects described in terms of their data values and the operations that can be performed on that data.
So one could define an Account class consisting of an id and balance and functions to deposit or withdraw currency. This definition forms the type, but when you "instantiate" (create an instance of) this type, i.e.
Account a;
then the variable a refers to an object of type Account in this scope.
Sometimes you need a class that can store and track objects of other types. This is what is sometimes referred to as a "container class" and typically it combines a store and a count.
Say we want to store some floats. We could just write:
float store[64];
std::cout << "Enter the first number: ";
std::cin >> store[0];
But how would we track how many floats we have? We would probably need a counter.,
float store[64];
int stored = 0;
std::cout << "Enter the first number: ";
std::cin >> store[0];
stored++;
std::cout << "Enter the second number: ";
std::cin >> store[1];
stored++;
This works, and it's not terribly difficult, but if you are writing a function that expects to take a store and it's size, how do you express that?
void myFunction(std::string label, float* store, int count);
This requires two arguments and it's not exactly explicit.
C++ is about encapsulation: this idea of a "store" with a count of the contents could be encapsulated into a class:
struct Store {
float store_[64] {};
int count_ {0};
};
this is a container. We can now write our function that takes an object that contains other values with a single parameter:
void myFunction(std::string label, Store& store); // & here = by reference
If this was 'C' you would write code that directly manipulated the values in the store:
store.store_[N] = 1;
store.count_++;
but that's messy, we didn't check there was room. In C++, we can encapsulate this into the class description with member functions and hide the member variables so that you have to go through our proscribed interface to manipulate the data.
#include <iostream>
class Store {
enum { MaxCount = 64 };
float store_[MaxCount] {};
size_t count_ = 0;
public:
// return the maximum number of elements we can store
size_t capacity() const { return MaxCount; }
// true/false: is the store empty?
bool empty() const { return count_ == 0; }
// return the current count
size_t size() const { return count_; }
bool add(float value) {
if (count_ >= capacity()) {
std::cerr << "store is full!\n";
return false;
}
store_[count_] = value;
++count_;
}
// reset
void clear() {
count_ = 0; // we don't actually need to change the store
}
// allow array-like usage
const float& operator[](size_t index) const { return store_[index]; }
float& operator[](size_t index) { return store_[index]; }
// provide bounds-checked array-ish access
float at(size_t index) const {
if (index >= count_)
throw std::invalid_argument("array index out of bounds");
return store_[index];
}
};
int main() {
Store store;
for (size_t i = 0; i < store.capacity(); ++i) {
std::cout << "Enter number #" << i << " or -ve to stop: " << std::flush;
float f = -1;
std::cin >> f;
std::cout << "\n" << f << "\n";
if (f < 0)
break;
store.add(f);
}
std::cout << "You entered " << store.size() << " values:";
for (size_t i = 0; i < store.size(); ++i) {
std::cout << ' ' << store[i];
}
std::cout << '\n';
}
Live demo: http://ideone.com/boE3Ki
They are telling you to create a class that acts as a container for an array. In programming, especially when you first start, you will see many elements called containers because your teacher wants you to view variables, classes, and arrays (among other programming tools) as containers for you to store data in.
Viewing programming this way makes is much easier to conceptualize information as you get into more complex ideas like pointers.
So, long answer short, create a class with the array inside it. If you are learning about constructors and overloading make sure you are initializing it based on the data you pass in. If not, it should be only a couple lines of code to complete the project.
I'm working on an assignment that involves creating a linked list of applicants (stored as a class), each having a value for ID, grade, years, and a numerical value (score). I need to write a function that searches this list for the applicant with the highest score and returns it. I was able to locate the highest score, but I would only return that value, and not the entire class of that specific applicant. After changing my code, I now get outputs for each of the 4 values (ID, grade, years, and score) but they all show 0, which is the initialized value. Here is all the relevant code.
#include <iostream>
#include <fstream>
#include <iomanip>
#include "applicant.cpp"
using namespace std;
struct Linkapp
{
Applicant person;
Linkapp *next;
};
class Linkthem
{
protected:
Linkapp *start;
public:
Linkthem(void);
void link(Applicant);
void printthem(void);
Applicant returnbest(void);
};
Applicant best;
Linkthem::Linkthem()
{
start = NULL;
};
void
Linkthem::link(Applicant one)
{
Linkapp *p;
p = new Linkapp;
p->person = one;
p->next = start;
start = p;
}
Applicant Linkthem::returnbest (void)
{
Linkapp *travel;
travel = start;
best = travel->person;
while (travel != NULL)
{
if (travel->person.return_value() > best.return_value())
best = travel->person;
travel = travel->next;
}
return best;
}
int
main()
{
ifstream infile;
Applicant fellow;
Linkthem mylist;
int id, yrs;
char knowledge;
cout.setf(ios::fixed);
cout.precision(2);
infile.open("applicnt.dat");
while ( !infile.eof() )
{
infile >> id >> knowledge >> yrs;
fellow.store_id(id);
fellow.store_skill(knowledge);
fellow.store_years(yrs);
mylist.link(fellow);
}
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
return 0;
}
And from the "applicant.cpp"
class Applicant
{
protected:
int id;
char skill;
int years;
float value;
void calc_value(void); // <--- NOT for general use, called by return_value
public:
Applicant(void);
void store_id(int);
void store_skill(char);
void store_years(int);
int return_id(void);
char return_skill(void);
int return_years(void);
float return_value(void);
};
The problem definitely lies within my returnbest function, I'm just not sure where.
Just used std::max_element and std::list (which is doubly linked) or std::forward_list (which is singly linked), but I would probably default to std::vector instead:
std::max_element(begin(list), end(list), [](Applicant const& a, Applicant const& b) {
return (a.return_value() < b.return_value());
});
If the comparison function is intended for the entire Applicant class, it might make sense to define an operator< for said class.
As you seem to have to write your own list, some hints:
Linkapp appears to be an internal detail of your class Linkthem, so no bad idea to make it a private inner class.
returnbest (and probably printthem, if I may assume analogous implementation) will fail with segmentation fault, if your list is empty. You need to check, if start is null first:
if(start)
{
// rest of your code
}
else
{
// return some appropriate default
// throw exception
// or whatever else seems appropriate to you
}
You have quite uncommon names for your getters/setters. There are two main variants for naming:
int getId(); // or get_id(), but cammel casing is more common these days
void setId(int id);
// vs
int id();
void id(int id);
There is no need for Applicant best to be a global variable. Rather place it within the functions as local variables.
Now your actual problem:
Keep a close look on your main function - where do you call returnbest first? After you wrote out the values of best. This means that you never assigned any other value to best than the default. You have to place fellow = mylist.returnbest(); before the lines where you output best. But then use fellow for output instead of best, and you are free to make best a local variable within returnbest function...
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
I don't know what you do in the printthem() member function, as you did not provide implementation of this function, but I assume you don't set variable best.So, you are printing an unset variable, which likely is initialized to zero.
What you want to do is more likely:
mylist.printthem();
best = mylist.returnbest();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
I have the following functor:
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
It is supposed to be a strict weak ordering, and it's this long (could be one line only) for debug purposes.
I'm using this functor as a comparator functor for a stl::set. Problem being, it only inserts the first element. By adding console output to the comparator function, I learned that it's actually comparing the file name to itself every time.
Other relevant lines are:
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
and
// (FileSet files_;) <- SimulatedDisk private class member
void SimulatedDisk::addFile(SimulatedDiskFile * file) {
files_.insert(file);
positions_calculated_ = false;
}
EDIT: the code that calls .addFile() is:
current_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
current_request++;
}
Where all_requests is a list, and class Request is such that:
class Request {
private:
string file_name_;
int response_code_;
int response_size_;
public:
void setFileName(string file_name);
string getFileName();
void setResponseCode(int response_code);
int getResponseCode();
void setResponseSize(int response_size);
int getResponseSize();
};
I wish I could offer my hypotesis as to what's going on, but I actually have no idea. Thanks in advance for any pointers.
There's nothing wrong with the code you've posted, functionally speaking. Here's a complete test program - I've only filled in the blanks, not changing your code at all.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class SimulatedDiskFile
{
public:
string getFileName() { return name; }
SimulatedDiskFile(const string &n)
: name(n) { }
string name;
};
class ComparatorClass {
public:
bool operator () (SimulatedDiskFile * file_1, SimulatedDiskFile * file_2) {
string file_1_name = file_1->getFileName();
string file_2_name = file_2->getFileName();
cout << file_1_name << " and " << file_2_name << ": ";
if (file_1_name < file_2_name) {
cout << "true" << endl;
return true;
}
else {
cout << "false" << endl;
return false;
}
}
};
typedef set<SimulatedDiskFile *, ComparatorClass> FileSet;
int main()
{
FileSet files;
files.insert(new SimulatedDiskFile("a"));
files.insert(new SimulatedDiskFile("z"));
files.insert(new SimulatedDiskFile("m"));
FileSet::iterator f;
for (f = files.begin(); f != files.end(); f++)
cout << (*f)->name << std::endl;
return 0;
}
I get this output:
z and a: false
a and z: true
z and a: false
m and a: false
m and z: true
z and m: false
a and m: true
m and a: false
a
m
z
Note that the set ends up with all three things stored in it, and your comparison logging shows sensible behaviour.
Edit:
Your bug is in these line:
SimulatedDiskFile temp_file(current_request->getFileName(), current_request->getResponseSize());
disk.addFile(&temp_file);
You're taking the address of a local object. Each time around the loop that object is destroyed and the next object is allocated into exactly the same space. So only the final object still exists at the end of the loop and you've added multiple pointers to that same object. Outside the loop, all bets are off because now none of the objects exist.
Either allocate each SimulatedDiskFile with new (like in my test, but then you'll have to figure out when to delete them), or else don't use pointers at all (far easier if it fits the constraints of your problem).
And here is the problem:
SimulatedDiskFile temp_file(current_request->getFileName(),
current_request->getResponseSize());
disk.addFile(&temp_file);
You are adding a pointer to a variable which is immediately destroyed. You need to dynamically create your SDF objects.
urrent_request = all_requests.begin();
while (current_request != all_requests.end()) {
SimulatedDiskFile temp_file(...blah..blah..); ====> pointer to local variable is inserted
disk.addFile(&temp_file);
current_request++;
}
temp_file would go out of scope the moment next iteration in while loop. You need to change the insert code. Create SimulatedDiskFile objects on heap and push otherwise if the objects are smaller then store by value in set.
Agree with #Earwicker. All looks good. Have you had a look inside all_requests? Maybe all the filenames are the same in there and everything else is working fine? (just thinking out loud here)