C++ Object doesn't save value - c++

Objects I created for the Player class does not change the private variable even tho it does in the function that changes it. There maybe some reference problems with the objects being used in the main() function but I can't figure out where or why.
Specifically the loop at the end of the main file prints the games won by the players in the vector Players but all the scores return 0. While when the player object.matchWon() function correctly parse and print the score and shows the change made to the private variable.
I suspect there are problems with the usage of getPlayer() function in the main code.
Thanks in advance!!
Player.cpp:
#include "Player.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
//constructor
Player::Player(string first, string last) {
Player::first = first;
Player::last = last;
gWins = 0;
gLoss = 0;
mWins = 10;
mLoss = 2;
}
//setters
void Player::addLoss(int increment) {
this->gLoss+=increment;
}
void Player::addWins(int increment) {
this->gWins+=increment;
}
void Player::matchWon(vector<string> scores) {
for (int i = 5;i<scores.size()-1;i++){
cout<<(int)scores[i][0]-'0'<<"-";
cout<<(int)scores[i][2]-'0'<<endl;
gWins+=scores[i][0]-'0'; //add games won
cout<<gWins<<endl;
gLoss+=(int)scores[i][2]-'0'; //add games lost
}
this->mWins++;
}
void Player::matchLost(vector<string> scores) {
this->mLoss++;
}
double Player::winPercentage() {
return (double)mWins / mLoss;
}
//accessors
string Player::getFirstname() {
return this->first;
}
string Player::getLastname() {
return this->last;
}
int Player::getGameswon() {
//cout<<gWins;
return this->gWins;
}
main.cpp:
#include <iostream>
#include "player.h"
#include <fstream>
#include <vector>
#include <sstream>
using namespace std;
vector<string> split(const string &s) {
vector<string> elems;
istringstream iss(s);
do
{
string sub;
iss >> sub;
elems.push_back(sub);
//cout << "Substring: " << sub << endl;
} while (iss);
return elems;
}
Player &getPlayer(vector<Player> &players, const string &first, const string &last){
for (int i=0;i<players.size();i++){
if (players[i].getFirstname()==first&&players[i].getLastname()==last){
return players[i];
}
}
players.push_back(Player(first,last));
return (players[players.size()-1]);
}
int main(int argc, char *argv[]) {
ifstream file(argv[1]);
ofstream ofile(argv[2]);
if (!file.is_open()){
cerr <<"Could not open file\n";
return 0;
}
string line;
vector<Player> players;
while (getline(file,line).good()){
vector<string> lineParsed = split(line);
vector<string> matchData = split(line);
Player p1 = getPlayer(players,lineParsed[0],lineParsed[1]);
Player p2 = getPlayer(players,lineParsed[3],lineParsed[4]);
p1.matchWon(lineParsed);
cout<<p1.getFirstname()<<"!"<<p1.getGameswon()<<endl;
}
for (int i=0;i<players.size();i++){
//cout<<players.size();
cout<<players[i].getFirstname()<<":"<<players[i].getGameswon()<<endl;
}
return 0;
}

You're copying your players:
Player p1 = getPlayer(players,lineParsed[0],lineParsed[1]);
Player p2 = getPlayer(players,lineParsed[3],lineParsed[4]);
so when you p1.matchWon(), it happens to the local Player object, not the one in the vector.
Although the return type of getplayer() is Player&, that doesn't matter if you're assigning it to a non-reference variable. If you want to be modifying the Player instances in the vector, you should have
Player& p1 = getPlayer(players,lineParsed[0],lineParsed[1]);
Player& p2 = getPlayer(players,lineParsed[3],lineParsed[4]);
By the way, getPlayer() is unsafe. The vector might resize itself when you push_back to it, invalidating references. You may want to store a vector of pointers instead (or just make sure through e.g. resize() that the vector definitely has room for all the players you want to push to it).

It would help if you included the class definition. There seems to be something strange going on:
Player::first = first;
Player::last = last;
Are these static fields? If they are, the issue is that you only store the first and last (names?) of the last player you create.

Related

How to transfer objects from an array of objects to another in C++?

I have an array of objects and I want to transfer the objects to another array. I've written the code bellow to do so but it did not work. It is basically a code where 52 card objects are created in one array and distributed between two arrays.Can anyone help me?
class card
{
public:
string suit;
string value;
void setValue(string v);
void setSuit(string s);
};
void card::setValue(string v)
{
value=v;
}
void card::setSuit(string s)
{
suit=s;
}
int main()
{
string suites[]={"Spades","Hearts","Diamonds","Clubs"};
string values[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
card cards[52];
int i=0;
for(int j=0;j<4;j++){
for(int k=0;k<13;k++){
cards[i].setSuit(suites[j]);
cards[i].setValue(values[k]);
i++;
}
}
card player1_cards[26];
card player2_cards[26];
for(int a=0;a<52;a++){
if(a%2==0){
player1_cards[a]=cards[a];
}
else{
player2_cards[a]=cards[a];
}
}
return 0;
}
If you want every 2nd card to be added to player1_cards and the others to be added to player2_cards you can change your for-loop to:
for(int a=1;a<26;a++) {
player1_cards[a] = cards[2*a];
player2_cards[a] = cards[2*a-1];
}
As Eljay said in the comment to your question your index went past the end of the array.
I tried to compile the code myself and it seems like the following code splits the cards in to two different arrays, althought it is not random (which you might want to add if you are doing a card game).
#include <iostream>
#include <string>
class card
{
public:
std::string suit;
std::string value;
void setValue(std::string v);
void setSuit(std::string s);
std::string printSV();
};
void card::setValue(std::string v)
{
value=v;
}
void card::setSuit(std::string s)
{
suit=s;
}
std::string card::printSV() {
return suit + ": " + value + "\n";
}
int main()
{
std::string suites[]={"Spades","Hearts","Diamonds","Clubs"};
std::string values[]={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
card cards[52];
int i=0;
for(int j=0;j<4;j++){
for(int k=0;k<13;k++){
cards[i].setSuit(suites[j]);
cards[i].setValue(values[k]);
i++;
}
}
card player1_cards[26];
card player2_cards[26];
for(int a=1;a<26;a++){
player1_cards[a] = cards[2*a];
player2_cards[a] = cards[2*a-1];
// prints the suite and value for each card in player1's and player2's hand.
std::cout << player1_cards[a].printSV();
std::cout << player2_cards[a].printSV();
}
return 0;
}
Outputs Suite: Value for each of the cards in cards[52].
As #Eljay notes, you are trying to access elements past the end of the arrays. If you were to use a debugger to step through your program, you would see this.
However, this is also a lesson for you to be careful of writing raw loops, with a lot of "magic-number" indices, and prefer using pre-existing patterns from the libraries (especially std::algorithm) when relevant. See this talk by Sean Parent about this general principle.
Specifically, you could have written your program as follows:
#include <range/v3/view.hpp>
#include <string>
#include <array>
#include <iostream>
struct card {
std::string suite;
std::string value;
};
std::ostream& operator<<(std::ostream& os, const card& c)
{
return os << c.value << " of " << c.suite;
}
int main()
{
using namespace ranges;
std::array suites {"Spades","Hearts","Diamonds","Clubs"};
std::array values {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
auto cards =
views::cartesian_product(suites, values) |
views::transform([](const auto& suite_and_value) -> card {
return { std::get<0>(suite_and_value), std::get<1>(suite_and_value) };
});
auto num_players { 2 };
auto player1_cards =
cards | views::stride(num_players);
auto player2_cards =
cards | views::drop(1) | views::stride(num_players);
std::cout << "Player 1 got: " << player1_cards << '\n';
std::cout << "Player 2 got: " << player2_cards << '\n';
}
in which case you don't use any loops and any index variables. This uses Eric Niebler's ranges-v3 library. See also this quick reference to understand faster what's going on in this code if you're not familiar with the terms.
See this Live on GodBolt.

Passing a dynamic array of structs c++

I need to read a .txt file and use the first number as the array size in a function called getData.
In my code, I am able to read the file and assign it to the array size as listSize. I am also able to fill the rest of the array with the .txt information. When I print out what is in my array WITHIN the getData function, it works.
The problem is that when I try to access the array outside of the getData function my program crashes. I am new to pointers, and c++ in general. I don't think I am passing it or calling it correctly. I have had a hard time finding information to assist me in the problem.
How can I access the arrays I created in getData?
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
using namespace std;
struct menuItemType
{
string menuItem;
double menuPrice;
};
void getData(int& listSize, menuItemType menuList[], int orderList[]);
int main()
{
menuItemType *menuList = 0; //-----pointers
int *orderList = 0;
int listSize;
getData(listSize, menuList, orderList);
cout << menuList[0].menuItem; //-----This is what crashes the program
return 0;
}
//-----Get Menu Function
void getData(int& listSize, menuItemType menuList[], int orderList[])
{
//-----Declare inFile
ifstream inFile;
string price, size;
//-----Open inFile
inFile.open("Ch9_Ex5Data.txt");
//-----Get Amount of Items, Convert to int
getline(inFile, size);
listSize = stoi(size);
//-----Set Array Size
menuList = new menuItemType[listSize];
orderList = new int[listSize];
//-----Get Menu
for (int x = 0; x < listSize; x++)
{
//-----Get menuItem
getline(inFile, menuList[x].menuItem);
//-----Get menuPrice convert to double
getline(inFile, price);
menuList[x].menuPrice = stod(price);
}
//------PRINT WORKS HERE ----- This print made me think i created the
//arrays correctly
for (int x = 0; x < listSize; x++)
{
cout << menuList[x].menuItem << endl
<< menuList[x].menuPrice
<< endl;
}
inFile.close();
}
The contents of the .txt
8
Plain Egg
1.45
Bacon and Egg
2.45
Muffin
0.99
French Toast
1.99
Fruit Basket
2.49
Cereal
0.69
Coffee
0.50
Tea
0.75
Setting menuList and orderList in getData does not update the pointers in main. It would if you used references to pointers:
void getData(int& listSize, menuItemType*& menuList, int*& orderList);
Even better, use references to std::vector and quit mucking around with owning pointers and new and delete.
Let's Rewrite your code for a better C++ness, with explanations. :)
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
Don't do using namespace std just because you can type less things, namespaces helps you by telling you where this particular thing you invoked came from. if you really wanna write string instead of std::string, pull that particular thing, not the whole namespace, like this:
using std::string;
Your struct seems right, but you need to choose if your types will start with capitals or not, I always start my types with capitals but this is a choice:
struct MenuItemType
{
string menuItem;
double menuPrice;
};
Now, your getData should, well, get your data. so the type matters. your data is not 'void' as you declared, it's an array of MenuItemType, you can then declare them as vector and not even care about pointers.
Other thing: all of your parameters in getData shouldn't be parameters - they are all things that you would get from the text file that your program will parse, so the only thing that matters for the getData is the text file, so this is your variable.
std::vector<MenuItemType> getData(const std::string& textFile)
{
std::ifstream inFile;
std::string price, size, item;
inFile.open(textFile);
getline(inFile, size);
int listSize = stoi(size);
std::vector<MenuItemType> result;
result.reserve(listSize);
for (int x = 0; x < listSize; x++)
{
getline(inFile, item);
getline(inFile, price);
result.push_back(MenuItemType{item, stod(price)});
}
return result;
}
See how I didn't closed the file? It will be closed as soon as you leave the function, there's no need to call that unless you need the file to close before the function finishes.
Thumbs up rule: don't deal with pointers unless you have to.
As for your main function:
int main()
{
std::vector<MenuItemType> menuList = getData("Ch9_Ex5Data.txt");
cout << menuList[0].menuItem;
return 0;
}
You could rewrite that in a faster way if you are sure what types are you using, the code above is equivalent to this one:
int main()
{
auto menuList = getData("Ch9_Ex5Data.txt");
cout << menuList[0].menuItem;
return 0;
}

Passing an array of pointers to a Class Object

Hoping someone can help a beginner out here. I searched but some of the solutions were out of scope my knowledge. I'm trying to create a function which will take in a an array of pointers to a class object and output a getName() function for each object. But I keep getting a "no instance of overloaded function". I can't figure out what the problem is. Please take a look at following code.
#include <iostream>
#include "Student.hpp"
#include <string>
using namespace std;
void getName(Student *studentArray[], int); //prototype
int main()
{
//Creates class objects with Constructors Name and Score
//Class object has a function getName() to return name
Student stud1("John", 100);
Student stud2("Mary", 90);
Student stud3 ("Jack", 85);
//Create an array of student objects
Student myArray[3]= {stud1, stud2, stud3};
//ERROR HERE NO INSTANCE OF OVERLOADED FUNCTON
getName(myArray, 3)
return 0;
}
void getName(Student *studentsArray[], int arraySize)
{
for(int index=0; index < arraySize; index++)
{
cout << studentsArray[index]->getName() << endl;
}
}
Student *studentsArray[] is an array of pointers to students:
[-> "John", -> "Mary", -> "Jack"]
While Student *studentsArray is a pointer to a student (e.g. the first of an array):
-> [ "John", "Mary", "Jack" ]
using the [] operator you can jump to the offset relative to a pointer
studentsArray[0] == (*studentsArray)
studentsArray[1] == *(studentsArray+1)
studentsArray[2] == *(studentsArray+2)
and the -> operator is syntactic sugar for (*var).
The problem is that you created an array of students instead of an array of pointers of students:
Student myArray[3] = {stud1, stud2, stud3};
should actually be
Student* myArray[3] = { &stud1, &stud2, &stud3 };
Here is a full working example:
#include <iostream>
#include "Student.hpp"
#include <string>
using namespace std;
void getName(Student *studentsArray[], int); //prototype
int main()
{
//Creates class objects with Constructors Name and Score
//Class object has a function getName() to return name
Student stud1("John", 100);
Student stud2("Mary", 90);
Student stud3("Jack", 85);
//Create an array of student objects
Student* myArray[3] = { &stud1, &stud2, &stud3 };
getName(myArray, 3);
return 0;
}
void getName(Student *studentsArray[], int arraySize)
{
for (int index = 0; index < arraySize; index++)
{
cout << studentsArray[index]->getName() << endl;
}
}
The most important thing is that your pointer doesn't actually point any existing variable or its address. In this case, pointer will instead point a randomly chosen memory cell address, so it's really dangerous because you will never know what it will cause, including data lose, memory leak and so on.
With std::vector, it would be something like:
#include "Student.hpp"
#include <iostream>
#include <string>
#include <vector>
void displayNames(const std::vector<Student>& students);
int main()
{
Student stud1("John", 100);
Student stud2("Mary", 90);
Student stud3("Jack", 85);
std::vector<Student> students = {stud1, stud2, stud3};
displayNames(students);
return 0;
}
void displayNames(const std::vector<Student>& students)
{
for (const auto& student : students)
{
std::cout << student.getName() << std::endl;
}
}

c++ segmentation fault for dynamic arrays

I want to add a theater object into a boxoffice object in a C++ code. When I try to add it in main code, first one is added successfully. But a segmentation fault occurs for second and obvioulsy other theater objects. Here is the add function;
#include <iostream>
#include <string>
#include "BoxOffice.h"
using namespace std;
BoxOffice::BoxOffice()
{
sizeReserv = 0;
sizeTheater = 0;
theaters = new Theater[sizeTheater];
reserv = new Reservation[sizeReserv];
}
BoxOffice::~BoxOffice(){}
void BoxOffice::addTheater(int theaterId, string movieName, int numRows, int numSeatsPerRow){
bool theaterExist = false;
for(int i=0; i<sizeTheater; i++)
{
if(theaters[i].id == theaterId)
{
theaterExist=true;
}
}
if(theaterExist)
cout<<"Theater "<<theaterId<<"("<<movieName<<") already exists"<< endl;
else
{
++sizeTheater;
Theater *tempTheater = new Theater[sizeTheater];
if((sizeTheater > 1)){
tempTheater = theaters;
}
tempTheater[sizeTheater-1] = Theater(theaterId,movieName,numRows,numSeatsPerRow);
delete[] theaters;
theaters = tempTheater;
cout<<"Theater "<<theaterId<<"("<<movieName<<") has been added"<< endl;
cout<<endl;
delete[] tempTheater;
}
}
And I get segmentation fault on this line;
tempTheater[sizeTheater-1] = Theater(theaterId,movieName,numRows,numSeatsPerRow);
This is Theater cpp;
#include "Theater.h"
using namespace std;
Theater::Theater(){
id=0;
movieName="";
numRows=0;
numSeatsPerRow=0;
}
Theater::Theater(int TheaterId, string TheaterMovieName, int TheaterNumOfRows, int TheaterNumSeatsPerRow)
{
id = TheaterId;
movieName = TheaterMovieName;
numRows = TheaterNumOfRows;
numSeatsPerRow = TheaterNumSeatsPerRow;
theaterArray = new int*[TheaterNumOfRows];
for(int i=0;i<TheaterNumOfRows;i++)
theaterArray[i]= new int[TheaterNumSeatsPerRow];
for(int i=0; i<TheaterNumOfRows;i++){
for(int j=0;j<TheaterNumSeatsPerRow;j++){
theaterArray[i][j]=0;
}
}
}
This is header file of Theater;
#include <iostream>
#include <string>
using namespace std;
class Theater{
public:
int id;
string movieName;
int numRows;
int numSeatsPerRow;
int **theaterArray;
Theater();
Theater(int TheaterId, string TheaterMovieName, int TheaterNumOfRows, int TheaterNumSeatsPerRow);
};
And this is how i call add functions;
BoxOffice R;
R.addTheater(10425, "Ted", 4, 3);
R.addTheater(8234, "Cloud Atlas", 8, 3);
R.addTheater(9176, "Hope Springs",6,2);
The problematic lines are these:
if((sizeTheater > 1)){
tempTheater = theaters;
}
First you allocate memory and assign it to tempTheater, but here you overwrite that pointer so it will point to the old memory. It does not copy the memory. Since the code is for a homework assignment, I'll leave it up to you how to copy the data, but I do hope you follow the rule of three for the Theater class (as for the BoxOffice class) which will make it very simple.
Also, there's no need to allocate a zero-size "array", just make the pointers be nullptr (or 0).

dynamically allocate memory to struct array c++

let me start by saying I know this is a funky way to program, but my teacher is requiring us to go about it this way.
also:
I CANT use std::string, classes, constructors for this project.
I am required to use this archaic method of c-style strings with dynamic memory allocation occuring outside the struct.. i know its not the best way to go about this, but theres nothign i can go. :(
Im stuck with the structs, I cant figure out whats wrong..
I have a struct
#include <iostream>
#include <fstream>
#include <ctime>
#include <stdlib.h>
#include <string>
using namespace std;
//global constant(s)
const int maxCards = 52;
//Structs
struct card
{
char *suit;
char *rank;
int cvalue;
char location;
};
//Function List
void readPlayers(player *peoplePointer);
void shuffleCards(card *unshuffled, card* shuffled);
//program
int main()
{
//create pointer and set initial value
card * deckPointer = new card[52];
card *deckHome = &deckPointer[0];
for(int i=0;i<maxCards;i++)
{
(*deckPointer).suit=new char[8];
(*deckPointer).rank = new char[7];
deckPointer++;
}
deckPointer = deckHome;
cardInit(deckPointer);
readDeck(deckPointer);
//sets default values for the card arrays
for(int i=0;i<52;i++)
{
strcopy((*deckPointer).suit,"suit");
strcopy((*deckPointer).rank,"rank");
(*deckPointer).cvalue = 0;
deckPointer++;
}
deckPointer = deckHome;
return 0;
}
//Functions
void cardInit(card *deckPointer)
{
card * deckHome = NULL;
deckHome = &deckPointer[0];
//set up card file to be read in
ifstream fin;
char *finName = new char[13];
//get file name from user
cout << "Enter file name...(cardFile.txt)" << endl;;
cin >> *finName;
//open the file
fin.open(finName);
//check if cardFile.txt opens correctly
if(!fin.good())
{
cout << "Error with card file" << endl;
}
else
{
deckPointer = deckHome;
while(fin.good())
{
for(int i=0;i<50;i++)
{
fin >> (*deckPointer).suit;
fin >> (*deckPointer).rank;
fin >> (*deckPointer).cvalue;
deckPointer++;
}
}
}
delete [] finName;
}
//Its a pretty simple program..and my dynamic memory works for
//the file name, but I cant figure out why it doesnt work for structs?
For your code to work, you will need a typename card.
to this end you will need to setup the following:
struct cardstruct {
char *suit;
char *rank;
int cvalue;
};
typedef struct cardstruct card;
otherwise, when you declare the pointers, you would need to use the "struct" keword first. E.G.:
struct card *deckPtr = new struct card[52];