C++ Trouble adding objects to a list - c++

I am having trouble adding objects to a list.
I want to make a List of Persons, and I know the size of the list is fixed at 5 people.
Each Person has an age (int) and a gender (string).
I want to add the Persons to the List, but I don't know how, I've only worked with integers now that I think of it.
Below is what I have so far.
The random age and gender is working, however clunky that is.
I'm thinking instead of creating the Persons as I did, maybe somehow I should create them dynamically in a for loop which will somehow generate the age and gender per loop iteration? Maybe the list should be pointers to the Person objects.
#include <iostream>
#include <list>
#include <memory>
using namespace std;
class Person {
private:
const int OLDEST_AGE = 100;
const int YOUNGEST_AGE = 1;
int age;
string gender;
public:
Person()
{
age = generateAge();
gender = generateGender();
}
// Person: generate random age for Person, from 1-100
int generateAge() {
int randomAge;
randomAge = rand() % (OLDEST_AGE - YOUNGEST_AGE + 1) + YOUNGEST_AGE;
return randomAge;
};
// Person: generate random gender for Person, Male or Female
string generateGender() {
int genderNumber;
genderNumber = rand() % (1 - 0 + 1) + 0;
if (genderNumber == 1)
return "Male";
else
return "Female";
};
void getStats() {
cout << "Person Age: " << age << endl;
cout << "Person Gender: " << gender << endl;
};
};
int main()
{
Person P1, P2, P3, P4, P5;
// Just to see if the objects are created
P1.getStats();
P2.getStats();
P3.getStats();
P4.getStats();
P5.getStats();
list<Person> myList;
list<Person>::iterator IT;
for (int i = 1; i <= 5; i++)
//don't know how to add each Person to the list
myList.push_back(P1);
cout << "myList contains: ";
for (IT = myList.begin(); IT != myList.end(); IT++)
// similar to above, how do I get the list to print each object's stats
P1.getStats();
cout << endl;
return 0;
}

Using vector would be better in your case if you want to add a lot of elements one after the other as specified in another response to another question comparing list and vector.
Back to the question, as some comments specified, declaring a variable for each person is not a good practice. What you could do instead is to populate your list with a loop like so:
for(int i = 0; i < 5; i++)
myList.push_back(Person());
Then to access the objects you would just loop through the list again wherever you need to.
for(int i = 0; i < 5; i++)
myList[i].getStats();

You can create a vector, and run through a loop where you instantiate the person, and then push them into the vector. This way, you won't need to create separate person variables like p1, p2, etc., and your vector would hold all the "Person" objects.
vector<Person> people;
for (int i = 0; i < 5; i++) {
people.push_back(Person());
}
for (Person person : people) {
person.getStats();
}

Related

How to call multiple classes in C++

Here is the code I'm trying to create, and yes its messy for now. To give some back story I'm trying to figure out how to call a class multiple times without doing it seperately. What I mean is instead of performing:
Dice diceOne;
Dice diceTwo; and so on, I want to know if it is possible to just put it as Dice dicewhatever(*) and have that be a modifiable variable. This is so that I can set that variable to a number and then decrement it based on a score.
I dont know if this is even possible, but at this point I've beat my head against this so much I'm just pulling at straws to see if it would be a fit.
class Dice {
public:
Dice();
int Roll();
int currentDiceSide();
private:
int diceRoll;
int diceReRoll; //Declares and initializes the number of dice to allow for roll next dice throw.
};
Dice::Dice()
: //This is the beginning of the class and sets diceRoll to zero
diceRoll(0)
{
}
int Dice::Roll()
{ //This function actually does the random roll within the class Dice.
diceRoll = ((rand() % 6) + 1);
return diceRoll;
}
int Dice::currentDiceSide()
{ //This function returns the value of the dice roll for the class call.
return diceRoll;
}
void Game::Rules()
{
ifstream inFile;
inFile.open("Farkle Rules.txt");
string line;
if (inFile.fail()) {
cerr << "Error opening file" << endl;
exit(1);
}
if (inFile.is_open()) {
while (inFile.good()) {
getline(inFile, line);
cout << line << endl;
}
inFile.close();
}
}
void Game::GetPlayerInput(int playerInput)
{
cin >> playerInput;
}
void Game::RunGame()
{
Rules();
bool farkle = false;
double turnSum = 0;
double value = 0;
int i = 0;
int w = 6;
int players = 0;
int numPlayer = 0;
int diceOneValue = 0;
int diceTwoValue = 0;
int diceThreeValue = 0;
int diceFourValue = 0;
int diceFiveValue = 0;
int diceSixValue = 0;
int num1s = 0; //Declaring and initializing the variables to hold how many times a number shows up in a roll.
int num2s = 0;
int num3s = 0;
int num4s = 0;
int num5s = 0;
int num6s = 0; //
srand(static_cast<unsigned int>(time(0)));
cout << "Welcome to Farkle!" << endl
<< endl;
cout << "Please enter the number of players " << endl;
cin >> players;
//Dice diceOne;
//diceOne.currentDiceSide();
//Dice diceTwo;
//diceTwo.currentDiceSide();
//Dice diceThree;
//diceThree.currentDiceSide();
//Dice diceFour;
//diceFour.currentDiceSide();
//Dice diceFive;
//diceFive.currentDiceSide();
//Dice diceSix;
//diceSix.currentDiceSide();
Dice diceOne(w);
< -this is the line that I would like to create with a variable that is modifiable.
You cannot give each Dice object a name, but you can create a Vector of Dice object pointers (vectors are basically resizable arrays), like this:
#include <vector>
#include <iostream>
//...
std::cout << "Please enter the number of players " << std::endl;
std::cin >> players;
// do something to make sure players is an integer
// initialize the empty playerDice vector
std::vector<Dice*> playerDice = {};
for (unsigned i = 0; i < players; ++i) {
playerDice.push_back(new Dice); // this adds a new Dice object pointer to the end of the vector
playerDice.at(i)->currentDiceSide();
}
You have then called the currentDiceSide() function on each Dice object you created, and have neatly organized them in a Vector, which you can access like this:
// say we want to access the third Dice Object,
// Vectors start counting at 0, so we acces the element at Index 2.
playerDice.at(2)->doSomething();
Now because you instantiated those Dice objects with new you have to remember to delete them when you're finished with them, otherwise this will create a memory leak.
//...
// this deletes all the Dice objects in the vector, and points the remaining pointers to null
for (auto d : playerDice) {
delete d;
d = nullptr;
}
Or, better yet, if you're using C++11, you can use std::unique_ptr instead of the raw C-style pointers. Those will prevent you from creating memory leaks, because they will be deleted when they go out of scope. Note you have to #include <memory> to use these.
The vector definition then turns into:
std::vector< std::unique_ptr<Dice> > playerDice = {};
And the creation of the objects would look like this
for (unsigned i = 0; i < players; ++i) {
Dice* temp = new Dice;
temp->currentDiceSide();
std::unique_ptr<Dice> uPtr{ temp };
playerDice.push_back(std::move(uPtr));
}
You can then just clear the vector when you're done with all the objects:
playerDice.clear();
which will delete all the Dice objects that you put into the vector.

Creating a vector of objects

vector <Population> obj;
int num_of_cities = 0;
cout<<"Enter the number of cities"<<endl;
cin>>num_of_cities;
for( int x = 0; x < num_of_cities ; x++)
{
cout<<"Enter population for city #"<< x + 1 <<endl;
cin>>populate;
obj[x].setPopulation(populate);
.....
Im trying to make a vector of objects. Basically the user will input the amount of cities and the program should create an object for each city. That way information on each city can be entered. I believe there is a problem with the syntax because once I put a value for populate, the program crashes. Any one can help ?
The following line is the problem:
obj[x].setPopulation(populate);
You are trying to access the object at index x but your vector is actually empty.
To solve this, there is 2 solutions:
You should create a Population object and push_back to your vector at each loop iteration
Example:
//...
for( int x = 0; x < num_of_cities ; x++)
{
cout<<"Enter population for city #"<< x + 1 <<endl;
cin>>populate;
Population pop;
pop.setPopulation(populate)
obj.push_back(pop);
}
//...
Or you initialize your vector with a size only when you know the number of cities: vector <Population> obj (num_of_cities);
Example:
//...
cin>>num_of_cities;
vector <Population> obj (num_of_cities); // move your vector declaration here
for( int x = 0; x < num_of_cities ; x++)
//...
obj[x].setPopulation(populate);
here you are trying to access an element which is out-of-bound, since the vector obj has no content yet.
A way to make this work would be to call
obj.resize(num_of_cities)
before the for loop.
A more general approach (which would work if you do not know beforehand how many elements you will have, but which is slower in this case), would be creating a Population object, and then pushing it to the vector:
Population p;
p.setPopulation(populate);
obj.push_back(p);
While currently available answers correctly identify the source of the problem at hand - access to empty vector - they fail to set the right example of using the vector in a given scenario. They either suggest a push_back of pre-constructed object - thus calling a copy-construcor unnecessarily - or demand a presence of default constructor, with a side effect of exra assignment.
The most elegant solution would be following:
Make sure your Population has a constructor which accepts population size and sets internal member accordingly (likely to be const int):
Population::Population(int habitants) : habitants(habitants) { }
Than, use emplace_back to insert an object without calling any other constructors:
obj.emplace_back(populate);
The following code will let you make a vector of city objects that you can then populate with meaningful data. First, you need to define a City class that can set and get the various data items. The code will compile and run but it is a limited version of what you might want to accomplish.
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class City {
public:
City() {}
~City() {}
void setPopulation(int& pop) { m_pop = pop; }
int getPopulation() const { return m_pop; }
void setName(const string& name) { m_name = name; }
string getName() const { return m_name; }
private:
string m_name{ "" };
int m_pop{ 0 };
};
vector<City> cities(0);
int num_of_cities = 0;
int main() {
do {
cout << "Enter number of cities: ";
cin >> num_of_cities;
} while (num_of_cities < 1);
cities.resize(num_of_cities);
int val{ 0 };
string nam{ "" };
for (int i = 0; i < cities.size(); i++) {
cout << "Enter name of city: ";
cin >> nam;
cities[i].setName(nam);
cout << "Enter population for city: ";
cin >> val;
cities[i].setPopulation(val);
}
system("pause");
return 0;
}

Sorting an array of strings in C++

I am trying to accomplish the following task:
List the students in alphabetic order, sorted by last name.
Do not change the given case of the names.
Do not change the output file format. (Firstname Lastname)
Just print the records in order by last name, i.e.
Annie J
Martin K
Toby L
This sort needs to be true alphabetical (not just the "lexicographical" sort).
The data was read in from a file and passed through a virtual function depending on what course this student was enrolled in. Here's what I have.
for (int i = 1; i < numStudents; i++)
{
if (( list[i] -> getLastname() ) < ( list[i - 1] -> getLastname() ))
{
Student *temp = list[i - 1];
ist[i - 1] = list[i];
list[i] = temp;
}
}
I've been working on this for a while now and I'm worried I've gone about this all wrong. Any tips/pointers appreciated!
I assume you have a struct like:
struct Student
{
std::string m_LastName;
std::string m_FirstName;
};
Now you need to make sure you can handle the case where two person have the same last name. In that case you want to look at the first name.
bool NameCompare(const Student &name1, const Student &name2)
{
if(name1.m_LastName == name2.m_LastName) {
return name1.m_FirstName < name2.m_FirstName;
}
return name1.m_LastName < name2.m_LastName;
}
Then just call sort on your list of Student
std::list<Student> student_list;
// add some Student to the list
student_list.sort(NameCompare);
Use string comparison function instead of the less than sign you used here:
if (( list[i] -> getLastname() ) < ( list[i - 1] -> getLastname() ))
Also here's a similar stackoverflow question (partially)
Is string::compare reliable to determine alphabetical order?
Try this reference, first you have to declare the size of the array of firstname and lastname using int z = sizeof(lastname, firstname)/sizeof(lastname[0], firstname[0]); and using sort(lastname,lastname+z); will sort the array of lastname then using loop to print the firstname with the sorted lastname.
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
string firstname[] = {"Michael", "Patricia", "Joseph", "Elizabeth", "Charles", "Barbara", "Thomas", "Margaret", "Robert", "Sarah"};
string lastname[] = {"Smith", "Johnson", "Williams", "Brown", "Jones", "Miller", "Davis", "Garcia", "Rodriguez", "Wilson"};
int z = sizeof(lastname, firstname)/sizeof(lastname[0], firstname[0]);
sort(lastname,lastname+z); //Use the start and end like this
for(int y = 0; y < z; y++){
cout << firstname[y]<< " " << lastname[y] << endl;
}
return 0;
}

C++ Dynamic Array of Objects Sorting by Property

I'm trying to implement selection sort as a member function within class, to sort the objects of the class where the number of total players are being got by user input, also the names and scores of the players are being got by user too.
I'll sort the player objects by the property of their scores, which is a class member, being got by user input.
My problem is, i got stuck within the main where i can't call the class' member function sort for the array of objects.
class Player{
private:
string name;
int score;
public:
void setStatistics(string, int) // simple setter, not writing the whole function
void sortPrint(int, Player []);
int getScore(){ return score; }
void print(){ cout << name << " " << score << endl; }
};
void Player::sortPrint(int n, Player arr[]){
int i, j, minIndex;
Player tmp;
for (i = 0; i < n - 1; i++) {
int maxIndex = i;
for (j = i + 1; j < n; j++)
{
if (arr[j].getScore() > arr[minIndex].getScore())
{
minIndex = j;
}
}
if (minIndex != i) {
tmp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = tmp;
}
for(int i=0; i<n; i++){
arr[i].print(); // not sure with this too
}
}
};
int main(){
int n,score;
string name;
cout << "How many players ?" << endl;
cin >> n;
Player **players;
players = new Player*[n];
for(int i=0;i<n;i++) {
cout << "Player's name :" << endl;
cin >> name;
cout << "Player's total score:" << endl;
cin >> score;
players[i] = new Player;
players[i]->setStatistics(name,score);
}
for(int i=0; i<n;i++){
players->sortPrint(n, players); // error here, dont know how to do this part
}
// returning the memory here, didn't write this part too.
}
Try to replace void Player::sortPrint(int n, Player arr[]) with void Player::sortPrint(int n, Player*) and call function like players->sortPrint(n, *players)
Your problem is, that players is a pointer to array of Player, and arrays do not have member functions of the containees. As Player::sortPrint does not depend on the object itself, declare it as static and call it like Player::sortPrint(n, players);
Unless you have a very good reason not to, you should use std::sort rather than your own sorting algorithm. You should use a comparison function which compares the score of each player.
The following should work in C++03:
bool comparePlayerScores(const Player* a, const player* b)
{
return (a->getScore() < b->getScore());
}
// Returns the players sorted by score, in a new std::vector
std::vector<Player*> getSortedPlayers(Player **players, int num_players)
{
std::vector<Player*> players_copy(players, players + num_players);
std::sort(players_copy.begin(), players_copy.end(), comparePlayerScores);
return players_copy;
}
void printSorted(Player **players, int num_players)
{
std::vector<Player*> sorted_players = getSortedPlayers(players, num_players);
// Could use iterators here, omitting for brevity
for (int i = 0; i < num_players; i++) {
sorted_players[i]->print();
}
}
(Alternatively, you could define an operator< on your Player class which compares scores, which would let you store players in a std::set or std::map.)

Dynamically adding an object?

#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;
static int c;
class DAI //DynamicArrayInput
{
public :
void getCountry()
{
cout << "Country : ";
cin >> Country;
}
void putCountry()
{
cout << Country << endl;
}
static int putCount()
{
return count;
}
private :
static int count;
char Country[30];
};
int main()
{
vector< DAI > A;
DAI B[3];
//DAI * B;
cout << "Enter name of countries\n";
for(int i=0; i < 3; i++)
{
//THIS DOESN'T WORK
/*
c++; //c is a static variable declared at top
B = new DAI[sizeof(DAI) * c/sizeof(DAI)];
GIVES WRONG OUTPUT :
Enter name of countries
Country : a
Country : b
Country : c
Printing name of countries
c
size of vector A is 3
vector after initialisation:
*/
A.push_back(B[i]);
B[i].getCountry();
}
cout << "Printing name of countries\n";
for(int i=0; i < 3; i++)
{
B[i].putCountry();
}
cout << "size of vector A is " << A.size() << "\
\nvector after initialisation:" << endl;
return 0;
}
**/*
CORRECT OUTPUT WITH LIMITED ARRAY SIZE = 3:
*Enter name of countries
Country : a
Country : b
Country : c
Printing name of countries
a
b
c
size of vector A is 3
vector after initialisation:*
*/**
I am just learning and this is not my homework,
I dont want to be limited to any array size such as above but when i try to use the staements in the first for loop as mentioned it does not work
i mean this does not works
c++; //c is a static variable declared at top
B = new DAI[sizeof(DAI) * c/sizeof(DAI)]; //B is a pointer of type class DAI
My aim is to use arrays of objects i like dynamically so that i can input any number of countries I don't want to use something like B[3] with something known array size but i am unable to do.
Please guide me, help me.
c++;   //c is a static variable declared at top
B = new DAI[sizeof(DAI) * c/sizeof(DAI)];
Here you are confusing new with malloc. To allocate c elements of type DAI, just do
B = new DAI[c];
However, your best bet is to stay with std::vector, which handles size and capacity itself.
for (int i = 0; i != 10; ++i)
A.push_back(DAI());
adds 10 DAIs to the vector A.
Edit
You can also create the vector with a number of DAIs from the start:
std::vector<DAI> A(10);
This also creates a vector with 10 elements, all at once. Unlike B[3], which always contains 3 elements, the vector can grow or shrink as needed without you having to handle the allocations.
Your array is static DAI[3] v (v is in the process' stack). To be able to add an element dynamically, you need to change it with DAI[INITIAL_SZ] v = new DAI[INITIAL_SZ] (now v is in the heap) so that now you can realloc it when adding/deleting elements.