Displaying elements of a struct array by using a function [closed] - c++

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
So I need to display a table with all of the categories such as housing, transportation, etc along with the other information in each of the arrays that I've initialized. I can't seem tot figure out how to display individual array elements using the "displayTable" function. When I say "cout << housing[0] << " " << utilities[0]" and so on I keep getting a build error. I've tried different syntax as well but I just can't figure it out. It's for an assignment in one of my classes. Any help is much appreciated!
#include <iostream>
#include <iomanip>
using namespace std;
struct Category
{
string category;
double maxAmount;
double amountSpent;
};
void displayTable(Category housing[3], Category utilities[3],
Category transportation[3], Category food[3],
Category entertainment[3], Category miscellaneous[3])
{
cout << setprecision(2)
<< housing[0];
}
int main()
{
int menuChoice;
Category housing = {"Housing", 500.00, 0.00};
Category utilities = {"Utilities", 150.00, 0.00};
Category transportation = {"Transportation", 50.00, 0.00};
Category food = {"Food", 250.00, 0.00};
Category entertainment = {"Entertainment", 150.00, 0.00};
Category miscellaneous = {"Miscellaneous", 50.00, 0.00};
do
{
} while (menuChoice != 3);
return 0;
}

I think you don't study well the array etc. With the string housing[0] you access the first element of an array named housing however in this case there is not array in the code. Maybe you want to create a function that take an element of the type Category and print the value of its field. Something like this:
displayCategory(Category aCategory){
cout<<"Category = "+aCategory.category+"\n"+"maxAmount ="+aCategory.maxAmount+"\n"+"amountSpent = "+aCategory.amountSpent+"\n";
}
Then you maybe want to create an array of Category so instead of declare a variable for every category you could do this:
Category arrayOfCategory[6];
arrayOfCategory[0] = {"Housing", 500.00, 0.00};
arrayOfCategory[1] = {"Utilities", 150.00, 0.00};
arrayOfCategory[2] = {"Transportation", 50.00, 0.00};
arrayOfCategory[3] = {"Food", 250.00, 0.00};
arrayOfCategory[4] = {"Entertainment", 150.00, 0.00};
arrayOfCategory[5] = {"Miscellaneous", 50.00, 0.00};
now if you want a function that print all the category in arrayOfCategory simply using the first function displayCategory we can create this function:
displayTable(Category *anArrayOfCategory, int dimOfTheArray){
for(int i = 0; i < dimOfTheArray; i++)
displayCategory(anArrayOfCategory[i]);
}

void displayTable(Category housing[3], Category utilities[3],
Category transportation[3], Category food[3],
Category entertainment[3], Category miscellaneous[3])
{
std::cout << setprecision(2) << housing[0];
}
housing[0] is of type Category, which you made. How should std::cout know what to print if you give it your own custom type?
You can print the members of your struct one by one like so:
std::cout << setprecision(2) << housing[0].maxAmount;
or implement a custom << operator inside your type:
struct Category
{
std::string category;
double maxAmount;
double amountSpent;
friend std::ostream& operator<< (std::ostream& stream, const Category& category) {
stream << category.maxAmount;
}
};
and then your original code should work fine.
(Obviously you need to add the other members to the output as well, and maybe add some headers/titles, commas etc. for human readability, but the principle should be clear.)

You should treat Category as a struct, not as an array
The simplest way is to access members in the following way
void displayTable(Category housing, Category utilities,
Category transportation, Category food,
Category entertainment, Category miscellaneous)
{
cout << setprecision(2)
<< housing.maxAmount;
}

Related

C++: How do I create a collection field for storing a finite number of instances of a class?

I have a problem that requires I track tickets sold at a theatre. To do this I need to create a ShowTicket class. The class should contain a collection field for the rows, seat numbers, and whether the tickets have been sold or not. The class only needs to support 10 sold tickets. Assume a ticket that is not referenced before has a sold status of false.
Member functions must include:
bool is_sold(string row, string seat);
void sell_seat(string row, string seat);
string print_ticket(string row, string seat);
So I made the following class and methods:
class ShowTicket {
string row;
string seat_num;
bool sold;
public:
ShowTicket(string row = "-1",
string seat_num = "-1",
bool sold = false)
:row(row),
seat_num(seat_num),
sold(sold){}
bool is_sold(string new_row, string new_seat_num){return sold;}
void sell_seat(string new_row, string new_seat_num){row = new_row; seat_num = new_seat_num; sold= true;}
string print_ticket(string row, string seat_num) {
string s;
sold ? s = "sold" : s = "available";
return row + " " + seat_num + " " + s;
}
};
I need my main function to look like this:
int main() {
ShowTicket myticket;
if(!myticket.is_sold("AA","101"))
myticket.sell_seat("AA","101");
cout << myticket.print_ticket("AA","101") << endl;
cout << myticket.print_ticket("AA","102") << endl;
return 0;
}
And return:
AA 101 sold
AA 102 available
*This is not required but it is possible to implement this without the 10 object limit. If you have time to include that version as well it would be much appreciated. Thanks to all.
When approaching a problem like this figuring out how many classes are needed and what they represent is one of the keys. Figuring out what the real world object types are can be helpful. So in a theater there are many objects; 1 Theater, potentially multiple viewing rooms, movies, tickets, concession stands, seats and many more. However, in this case you're dealing with three, one theater that's assumed to have only one movie playing in a single viewing room. You also have seats and tickets. But this problem really doesn't do anything with tickets. For instance you are not tracking who has it, who paid for it, what time it's sold. So you really only have two objects of interest a theatre and seats. So you'll need two classes to represent those two real world object. Now a seat doesn't have many theaters in it, but a theater does have many seats. So the theatre will need a collection of seats associated with it. In this case you'll you will also have a ShowTicket class, since that's a given constraint of the problem. Logically the ShowTicket is most closely related to the theatre, since you've been told it should contain a container of seats.
Now what attributes of a seat are interesting in this problem? Whether it reclines? It's price? Who's sitting in it? What row it's in? What's its number in that row? Has the ticket been sold? The answer to those questions. You'll have to decide where and how to store that information within the class. For instance is the seat position going to be represented as a string, like "A101" or is it going to be represented by a row 'A' and a seat 101. And are those values going to be strings, a single character or an integer.
And how are you going to represent sold verses available? Are you going to put available seats in one container class and sold seats in another containers object? Or are you simply going to store a bool in the class indicating sold or not?
There are many ways to code a solution to this problem that will work. And some will be more efficient than others. And some when written in production code, that may be modified over and over again for years will be easier to maintain due to a simple, clear design.
Obviously I'm not providing a concrete solution to your problem. Instead I'm trying to help to provide the thought processes to guide you to solving many programming problems.
Credit to Jerry Jeremiah:
#include <iostream>
#include <string>
#include <map>
class ShowTicket {
using TicketsMap = std::map<std::pair<std::string,std::string>,bool>;
static TicketsMap tickets; // needs to be defined outside the class
public:
bool is_sold(std::string row, std::string seat_num){return tickets[{row,seat_num}];}
void sell_seat(std::string row, std::string seat_num){tickets[{row,seat_num}]=true;}
std::string print_ticket(std::string row, std::string seat_num) {
return row + " " + seat_num + " " + (tickets[{row,seat_num}]?"sold":"available");
}
};
ShowTicket::TicketsMap ShowTicket::tickets;
int main() {
ShowTicket myticket;
if(!myticket.is_sold("AA","101"))
myticket.sell_seat("AA","101");
std::cout << myticket.print_ticket("AA","101") << std::endl;
std::cout << myticket.print_ticket("AA","102") << std::endl;
return 0;
}

C++ an array of pointers to the base class [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
There are several classes which are made in different files.
Here are the class name: Hospital class and patient class.
Basically, for hospital class, it has array of pointers for patient which can store the information of patient based on their patient type. For patient class(base class) has four derived classes as follows: male, female, inpatient, outpatient. What I am trying to do is, in hospital class, I want to assign the object according to the patient type.
#include <iostream>
#include "Hospital.h"
#include "Female.h"
#include "Male.h"
#include "Inpatient.h"
#include "Outpatient.h"
using namespace std;
Hospital::Hospital(const char * name, int capacity)
{
hospitalName=name;
patientCapacity=capacity;
totalPatients=0;
}
void Hospital::determinePatientType()
{
int selection;
cout << "What is the patient type?" << endl;
cout << "1. Female" << endl;
cout << "2. Male" << endl;
cout << "3. Outpatient" << endl;
cout << "4. Inpatient" << endl;
cin >> selection;
if(selection==1)
{
patients = new Patient[totalPatients];
Female* female = new Female();
patients = female;
}
else if(selection==2)
{
patients = new Patient[totalPatients];
Male* male = new Male();
patients = male;
}
else if(selection==3)
{
patients = new Patient[totalPatients];
Outpatient* outpatient = new Outpatient();
patients = outpatient;
}
else
{
patients = new Patient[totalPatients];
Inpatient* inpatient = new Inpatient();
patients = inpatient;
}
totalPatients++;
}
This is my code, but I am not sure if it's correct or not.
And I will upload the class diagram for your understanding what I am doing.
Thank you for your assistance in advance.
enter image description here
No your code is not correct.
Lets take a look at this code snippet:
if(selection==1)
{
patients = new Patient[totalPatients];
Female* female = new Female();
patients = female;
}
With
patients = new Patient[totalPatients];
you allocate an array of totalPatients objects. You make patients point to the first element of that array.
Then two lines down you do
patients = female;
That reassigns the variable patients to point to the single object female.
You will lose the original memory from the first allocation, which will lead to a leak. It will most likely also lead to undefined behavior if you then use delete[] patients since patients no longer point to an array.
Unless your exercise is to lean how to use pointers and dynamic memory allocation, you should use std::vector.
There's also another potential issue, and that relates toobject slicing and polymorphism (if the classes are polymorphic, i.e. they have virtual member functions).
If you solve the first problem the natural way, by doing e.g.
patients[totalPatients] = female;
Or if you use a std::vector<Patient> and do a simple push_back(female).
Then you slice the female object, and lose all data specific for the Female class. And since you no longer have a Female objects, all virtual functions will think they are called on a Patient object, making polymprphism not working either.
To solve this problem you should have a vector (or array) of pointers to the base Patient class. Like e.g. std::vector<Patient*>.
Or better yet use a smart pointer like std::unique_ptr as in std::vector<std::unique_ptr<Patient>>.

Why are my keys being overwritten when inserting an object into a map?

I am running into the problem of when inserting elements into a map, I assume that every time I put a new object into it, the previous one is overwritten. Whenever I go to print out the contents of my map, only the most recent item I added is printed. I have two classes, a Recipe class and an Ingredient class. My recipe has a map<Ingredient*, int> which holds and object and its quantity.
Recipe::Recipe(){
title = "";
ingredients;
}
void Recipe::insertIngredient(Ingredient* item, int quantity){
ingredients.insert( make_pair( item, quantity ) );
}
Ingredient::Ingredient(){
name = "";
unit = "";
}
I have getters and setters for each class that I use to initialize variables and to print out the contents, but whenever I print the contents of my Recipe's map only the last item I put into it is printed out. The following is in my main function to print out the map.
map<Ingredient*, int> tempIngredients = tempRecipe->getIngredients();
map<Ingredient*, int>::iterator ingredientIt;
for (ingredientIt = tempIngredients.begin(); ingredientIt!= tempIngredients.end(); ingredientIt++) {
Ingredient* tempIngredient = ingredientIt->first;
int quantity = ingredientIt->second;
cout << "\n" << tempIngredient->getName() << " " << tempIngredient->getUnit() << " " << quantity << flush;
}
My output is currently the following:
unbleached wheat blend flour C. 1
Which is the igredient's name, unit, and quantity(value from the map) of the last element I added.
You are using a pointer for the map key without implementing a comparison operator.
You would be much better off using an Ingredient object and implement operator<.
For example,
class Ingredient {
public:
bool operator<(const Ingredient & b) const {
return getName() < b.getName();
}
// Rest of class methods data etc...
};
std::map<Ingredient, int> ingredients;
How are you calling insertIngredient? Since your map is keyed by Ingredient*, each entry must be a unique Ingredient object (by new or off the stack) or the keys will clash.
The real question is why are you keying on a pointer? Per #GWW's answer, a better solution would be to store Ingredient values and create a custom comparator function for them.

Search vector of objects by object data member attribute

I'm writing a Jukebox simulator and I'm trying to search a vector of Album objects by album title and return the index for use in other functions. The function is to be used for a number of different things such as deleting an album, printing an album etc.
I have gotten it to work in a previous application when the function was in the same Class as the data member to search for. I can however for some reason not get it to work using getters. No matter what I input as search key the idx returns 3 although the vector only contains indexes 0, 1 and 2 (only 3 albums right now).
The lambda function seem to be able to access data by using the getAlbum()-getter but somehow the comparison doesn't work.
My approach might be entirely wrong and I'd be grateful for any pointers in the right direction, or suggestions on how to accomplish the desired result using some other technique.
int Jukebox::findAlbumIdx(string key)
{
// Get search predicate
auto it = find_if(albvec.begin(), albvec.end(), [key](Album const &a)
{
return (a.getAlbum() == key);
});
int idx = it - albvec.begin();
return idx;
}
void Jukebox::delAlbum()
{
cin.get();
string key;
cout << "Input title of album to delete: ";
getline(cin, key);
int idx = findAlbumIdx(key);
if(idx > albvec.size() - 1)
cout << "No hits." << endl;
else
albvec.erase(albvec.begin() + idx);
}
getAlbum is just a simple inline getter as such:
string getAlbum() const {return album_title;}
Following Jonathan Wakely's suggestion to add std::cout << a.getAlbum() << " == " << key << std::endl; in the lambda the output is this:
Input title of album to delete: Abbey Road
== Abbey Road
== Abbey Road
== Abbey RoadLonely Hearts Club Band
No hits.
Obviously the getter isn't actually getting much to use for comparison. Not sure why it only gets the last entry and on the right hand side of the comparison.
If I add this to any of the functions above it gets and displays the Album titles correctly. The vector seems to be fine just before calling findAlbumIdx(key); and also inside the findAlbumIdx function.
for(unsigned int i = 0; i < albvec.size(); ++i)
cout << albvec[i].getAlbum() << endl;
The original playlist file that is read into the vector to be searched had dos newlines, after converting it using dos2unix (since I'm running Linux) the search, and I presume a lot of other things, is working correctly.
I suppose trimming newline characters while reading the file into the vector would be the more correct approach.

How to store objects for later use and make them searchable

At the moment I am using a vector to store pointers to the object every time one is made, but that feels a little silly. There's probably a better way, but I haven't found it.
What I'm doing: Example usage:
The problem:
If I want to retrieve a certain Date I have to go over all items in the vector to see if RecPaymentsStack.stackDate matches the date the user requested.
The RecPaymentStack is actually completely useless at the moment because what I should be doing, is, when adding a new item, checking if a "RecPaymentStack.stackDate" has already been made for the new item's Date property, and if so add the new pointer to "RecPayments" to an array of pointers inside the "RecPaymentStack" object. But how?
I'm probably unnecessarily complicating things (something I do a lot) so an explenation on how something like this should be done would be very nice.
Detailed info: (in case I was being too vague)
The below example is supposed to resemble a calendar that can hold certain items (RecPayments) and those items are grouped by their date (RecPaymentsStack).
struct RecPayments
{
std::string name;
Date* date;
float cost;
};
struct RecPaymentsStack
{
Date* stackDate; //This stack's date
RecPayments * thePaymentItem; //Hold pointer to the actual item
};
And here's how I'm currently storing them
std::vector<RecPaymentsStack*> RecPaymentsVector; //This vector will hold pointers to all the Recurring Payments
void addRecurring(std::string theDate,std::string theName,float theCost)
{
//New recurring payment
RecPayments * newPaymentItem = new RecPayments;
//Set recurring payment properties
newPaymentItem->name = theName;
newPaymentItem->date = new Date(stringToChar(theDate));
newPaymentItem->cost = theCost;
//Add recurring payment to stack
RecPaymentsStack * addToStack = new RecPaymentsStack;
addToStack->stackDate = new Date(stringToChar(theDate));
addToStack->thePaymentItem = newPaymentItem;
//Add pointer to RecPaymentsStack to vector
RecPaymentsVector.push_back(addToStack);
}
So to retrieve the items for a given date, I am currently going over all pointers in the vector to see if the "stackDate" property matches the requested date, and if so I use the "thePaymentItem" property to show the actual item.
void getItemsNow(Date requestedDate)
{
std::cout << "Showing Dates for " << requestedDate << std::endl;
unsigned int i;
for(i=0;i<RecPaymentsVector.size();i++) //Go over all items in vector
{
Date dateInVector(*RecPaymentsVector[i]->stackDate); //Get the date from the vector
if(dateInVector == requestedDate) //See if Date matches what the user requested
{
//Date matched, show user the item properties.
std::cout << "Date: " << dateInVector <<
" has name: " << RecPaymentsVector[i]->thePaymentItem->name <<
" and price " << RecPaymentsVector[i]->thePaymentItem->cost <<
std::endl;
}
}
}
3 problems with this:
Going over all items in the vector is highly inefficient if I only
need a couple of pointers
The RecPaymentStack is actually completely useless at the moment because what I should be doing, is, when adding a new item, checking if a "RecPaymentStack.stackDate" has already been made for the new item's Date property, and if so add the new pointer to "RecPayments" to an array of pointers inside the "RecPaymentStack" object. But how?
All of this feels extremely silly to begin with.. there's probably a much easier/professional way to do this but I can't find out what, probably because I'm still thinking like a PHPer.
So the general idea here is that I end up doing something like (silly example)
for each RecPaymentsStack->stackDate //For each unique Date, show it's children items.
{
cout << "The Date is " CurrentRecPaymentsStack->stackDate and it holds the following items:
for each CurrentRecPaymentsStack->thePaymentItem //This would now be an array of pointers
{
cout << "item name " CurrentRecPaymentsStack->thePaymentItem->name << " with cost " << CurrentRecPaymentsStack->thePaymentItem->cost << endl;
}
}
Which would basically go over all the unique "RecPaymentsStack" objects (unique determined by it's "Date" property) and for each Date it would then show it's "children" from the RecPayments struct.
And there has to be some way to search for a particular date without having to go over all the available ones.
Rather than using a vector to manage your items, you should replace your RecPaymentsStack instance with a std::multimap. The key type is your Date structure, the value type is RecPayments (which I would change to the singular form RecPayment). Small example (untested):
typedef std::multimap<Date, RecPayment> RecPaymentsByDateMap;
typedef std::pair<RecPaymentsByDateMap::iterator,
RecPaymentsByDateMap::iterator>
RecPaymentsByDateMapIters;
RecPaymentsByDateMap payments_by_date;
RecPaymentsByDateMapIters findByDate(Date date) {
return payments_by_date.equal_range(date);
}
...
// find all payments with the given date
RecPaymentsByDateMapIters iters = findByDate(...);
for (RecPaymentsByDateMap::iterator it = iters.first;
it != iters.second;
++it)
{
std::cout << "Payment " << it->second.name << std::endl;
}
I might design it like this -- this is just a loose idea, details should be adjusted according to your requirements:
#include <deque>
#include <map>
#include <string>
struct RecPayment
{
std::string name;
Date date;
float cost;
};
struct RecPaymentsStack
{
Date stackDate;
std::deque<RecPayment> thePaymentItem;
bool operator<(RecPaymentsStack const & rhs) const
{
return stackDate < rhs.stackDate;
}
explicit RecPaymentsStack(Date const & d) : stackDate(d) { }
};
typedef std::multimap<RecPaymentsStack> PaymentsCollection;
Now you can insert elements:
PaymentsCollection payments;
{
auto it = payments.emplace(Date("..."));
it->thePaymentItem.emplace_back(Payment{name1, date1, cost1});
it->thePaymentItem.emplace_back(Payment{name2, date2, cost2});
it->thePaymentItem.emplace_back(Payment{name3, date3, cost3});
}