priority queue using class object - c++

I am trying to make a priority queue with Student class.
I overloaded operator< function which compare student id of two objects of Student class, but my code does not work.
Could you give me any advice?
#include <iostream>
#include <queue>
#include <vector>
#include <string>
using namespace std;
class Student {
public:
string name;
int id;
Student(string str, int n) {
this->name = str;
this->id = n;
};
};
bool operator<(const Student& a, const Student& b) {
return a.id < b.id;
}
int main() {
priority_queue<Student> pq;
pq.push(Student("Miria", 1));
pq.push(Student("Ken", 2));
pq.push(Student("Bob", 3));
while (!pq.empty()) {
cout << pq.top() << endl;
pq.pop();
}
return 0;
}

I am kind of unaware what an "My code does not work seems", although it seems to me that you are not printing the objects in the priority_queue correctly.
The appropriate way of printing them will be by using the object's properties or using a string representation operator overload. Let me give you an example of how the simpler version of the code would look like by using your code:
cout << pq.top().name << "'s id is: " << pq.top().id << endl;
The reason your code is not working is because you are lacking the output operator overload. You are not able to compile the program itself if you are lacking this operator.
The former spoken of is done the foreshown code down below:
friend ostream& operator<<(std::ostream& output, const Student& obj) {
return output << obj.name << "'s id is: " << obj.id << endl;
};
This way you will be able to use the pq.top() function in the cout stream:
cout << pq.top();
I hope this is what you expect and if you want to make it in increase order like Java you can use priority_queue.
The expected output as the question is described should be:
Bob's id is: 3
Ken's id is: 2
Miria's id is: 1

Related

How to add a list to a string? [duplicate]

This question already has answers here:
Printing out contents of a list from the c++ list library [duplicate]
(5 answers)
Closed 7 months ago.
I am trying to add a list to a string.
int main() {
std::cout << "Hello, welcome to Jay's Coffee!!\n";
std::string name; std::cout <<"What is your name "; std::cin >> name;
std::cout <<"Hello " << name << ", thank you so much for coming in today";
std::list <std::string> menu = {"Black Coffee" "Espresso" "Latte" "Cappucino"};
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n" << menu;
}
Returns
invalid operands to binary expression ('basic_ostream<char>' and 'std::list<std::string>' (aka 'list<basic_string<char>>'))
There is no operator<< for lists. You have to write a loop. For example
for (auto& item : menu)
{
std::cout << item << '\n';
}
If you think about it it's obvious why you have to do this yourself. How are you doing to separate the list items? I've chosen to put each item on a new line. You might choose to separate them with commas or spaces or some fancy format. Because there is no obvious single way to print a list there is no predefined way in the C++ library.
You should write code this way. In c++, you can't print a list directly.
#include <string>
#include <list>
using namespace std;
int main() {
cout << "Hello, welcome to Jay's Coffee!!\n";
string name;
cout <<"What is your name ";
cin >> name;
cout <<"Hello " << name << ", thank you so much for coming in today";
list <string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
cout << name <<",what would you like from our menu today? Here is what we are serving.\n" ;
for ( string& s : menu )
{
cout << s << '\n';
}
}
The error message means that the operator << that you are trying to use with your object menu of the type std::list<std::string> is not defined for the class std::list<std::string>.
Also you need to separate strings in the initializer list with commas.
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
Otherwise the list will contain only one string due to the concatenation of string literals.
You could define such an operator as shown in the demonstration program below.
#include <iostream>
#include <string>
#include <list>
std::ostream & operator <<( std::ostream &os, const std::list<std::string>& lst )
{
for ( const auto &s : lst )
{
os << s << '\n';
}
return os;
}
int main()
{
std::list <std::string> menu =
{
"Black Coffee", "Espresso", "Latte", "Cappucino"
};
std::cout << menu;
}
The program output is
Black Coffee
Espresso
Latte
Cappucino
Or just use the range-based for loop directly in main like
std::cout << name <<",what would you like from our menu today? Here is what we are serving.\n";
for ( const auto &s : menu )
{
std::cout << s << '\n';
}
Or place the range-based for loop in a separate function similar to the operator << shown above.
First thing, you didn't define a list: in the declaration of menu, initializer-list elems were not separated by comma (,).
To make it reusable, I'd do it something like this:
#include <iostream>
#include <list>
static const std::string list_sep = ", ";
template<typename C> struct FormattedContainer
{
FormattedContainer(const C& cont, const std::string& sep = list_sep)
: cont_(cont)
, sep_(sep) {}
friend std::ostream& operator<<(std::ostream& os, const FormattedContainer& fc)
{
bool first = true;
for (auto&& e : fc.cont_)
{
if (first)
{
os << e;
first = false;
}
else
{
os << fc.sep_ << e;
}
}
return os;
}
const C& cont_;
const std::string sep_;
};
template<typename C>
auto make_fc(const C& cont, const std::string& sep = list_sep)
-> FormattedContainer<C>
{
return FormattedContainer<C>(cont, sep);
}
int main() {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << "What would you like from our menu today? Here is what we are serving.\n" << make_fc(menu);
}
This way, you don't need to define operator<< for something in std namespace (which might result in ambiguous calls as others might also define it, don't need to import another namespace, just simply call a wrapper around the type. You can use it with basically any container or iterable type using this method.
All other answers are correct but here is the better way to do the same
#include <iostream>
#include <algorithm>
#include <list>
template <typename T>
std::ostream & operator << (std::ostream & os, const std::list<T> & vec){
std::for_each (vec.begin () , vec.end() , [&](const auto& val){
std::cout << val << " ";
});
return os;
}
int main () {
std::list <std::string> menu = {"Black Coffee", "Espresso", "Latte", "Cappucino"};
std::cout << menu << "\n";
return 0;
}

Grab data from a specific struct by typing in name and year

I am building a wine inventory system for people who want to have an easy way of seeing the wine they own, which automatically gets its market price and county of creation.
this program should get a struct from multiple predefined structs seen in code below, by the user only typing name and year so it prints the entire struct, for example, I type in "Greenock creek Roennfelt road shiraz" and the year "2002" then it outputs the entire struct seen below.
I was thinking of using a read or get command but before researching how I should do make this I wanted to ask if there was a more efficient way of doing this.
the struct below is just one of many in a large list of predetermined structs in a second c++ file connected to the main file.
is this possible in c++ if so how would you recommend proceeding?
A struct in a different file:
struct red1 // one of the predetermined structs
{
string name = "Greenock Creek Roennfeldt Road Shiraz";
double year = 2002;
string place = "Australia";
double price = 295.00;
string type = "Redwine";
};
Main File input: (this part is not 100% yet it's just to show what I mean.
for (int i = 3; i < 10; i++)
{
string str; //input for data
cout << "Please enter the data of your Wine: " << endl;
cout << "Enter name: ";
getline(cin, wine.name);
cout << endl << "Enter year: ";
getline(cin, wine.year);
cout << endl;
cout << "your entered data: " << endl;
printwine(wine);
wineinventory.push_back(wine); // store in vector
}
I dont understand why you want to have several structs. I think you need just one and then create different instances for different wines. For the sake of the example I will use only year and name:
#include <vector>
#include <string>
#include <iostream>
struct wine {
int year;
std::string name;
};
// custom output operator to insert a wine into a ostream
std::ostream& operator<<(std::ostream& out, const wine& w) {
out << "year: " << w.year << " " << w.name;
return out;
};
int main() {
// fill a vector with different wines
std::vector<wine> wines { {2001,"the red one"}, {2005,"the white one"}};
// select a year
int year = 2001;
// pick the ones with matching year and print them
for (auto& w : wines) {
if (w.year == year) std::cout << w << "\n";
}
}
This will print:
year: 2001 the red one
There is already an accepted answer with many upvotes. Very good.
I just wanted to guide the new user in the correct direction. Since we are working with C++, we should use a more object oriented approach.
You have data and you have methods that should work on your data. For example, your Wine object has properties, the data members. And only the Wine object should operate on thes members. So I added / overloaded an inserter and extractor function. The inserter knows, how to print its data. Later, you would even encapsulate your data, and nobody but the functions should operate on it. If you later add one property to the Wine, you will adapt the inserter and the rest of the program will continue to work. You need to make this abstraction.
So, I recomend to learn the object oriented approach, otherwise, you will continue to wriet C code with some syntactic C++ sugar.
I drafted a skeleton example program for you. It should give you an idea of what I mean
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <iterator>
// A wine with its data and methods
struct Wine
{
std::string name{};
int year{};
std::string place{};
double price{};
std::string type{};
friend std::istream& operator >> (std::istream& is, Wine& w) {
return is >> w.name >> w.year >> w.place >> w.price >> w.type;
}
friend std::ostream& operator << (std::ostream& os, const Wine& w) {
return os << w.name << "\n" << w.year << "\n" << w.place << "\n" << w.price << "\n" << w.type << "\n";
}
};
// A Wine list (internally a vector) with its data and methods
class Wines
{
std::vector<Wine> wines{};
public:
void add(Wine& wine) { wines.push_back(wine); }
void remove(std::string wineName) { wines.erase(std::remove_if(wines.begin(), wines.end(), [&wineName](const Wine & w) { return w.name == wineName; }), wines.end()); }
bool findAndPrint(std::string& wineName, std::ostream& os) {
bool result = false;
std::vector<Wine>::iterator found = std::find_if(wines.begin(), wines.end(), [&wineName](const Wine & w) { return w.name == wineName; });
if (found != wines.end()) {
os << "\nWine found:\n" << *found;
result = true;
}
else
os << "\nNo wine with this name found\n";
return result;
}
friend std::istream& operator >> (std::istream& is, Wines& w) {
w.wines.clear();
std::copy(std::istream_iterator<Wine>(is), std::istream_iterator<Wine>(), std::back_inserter(w.wines));
return is;
}
friend std::ostream& operator << (std::ostream& os, const Wines& w) {
std::copy(w.wines.begin(), w.wines.end(), std::ostream_iterator<Wine>(os, "\n"));
return os;
}
};
int main(void)
{
// One wine
Wine wine;
// A lsit with wines
Wines wines;
// Add some data
std::cout << "\nEnter wine data. Name, Year, Place, Price, Type:\n";
std::cin >> wine;
wines.add(wine);
std::cout << "\n\nEnter another wine data. Name, Year, Place, Price, Type:\n";
std::cin >> wine;
wines.add(wine);
{
// Store all wines on disk
std::cout << "\nSaving on disk\n\n";
std::ofstream database("c:\\temp\\winelist.txt");
// Stores all wines in file
if (database) database << wines;
}
{
// Read back all wines from disk
std::cout << "\nReading from disk\n\n";
std::ifstream database("c:\\temp\\winelist.txt");
// Reads the complete list from file
if (database) database >> wines ;
}
// Search for a wine, if found, then remove it
std::cout << "\n\nWine List:" << wines << "\n\n\nSearch for a wine. Enter a wine name:\n" << wines;
std::string wineToSearch;
std::cin >> wineToSearch;
if (wines.findAndPrint(wineToSearch, std::cout)) {
wines.remove(wineToSearch);
std::cout << "\nRemoving wine from list: New List\n\n" << wines << "\n";
}
return 0;
}
There are of course tons of other possibilities. But you should get the idea.

sorting when class object is used as a key to map

I have created a class with student details(name and id) and I want to use the object of that class as key value to map.
I have some questions
I am getting error while compiling this code. Kindly tell me the solution for this?
As map is ordered, here I have object as a key value and it has both string and number based on what the key value will get sorted?
I have used
cout<<(*ii).first<<'\t'<<(*ii).second<<endl;
to print the value. Is that the correct way to print the value of class object
( (*ii).first)?
Kindly find my code below
#include<iostream>
#include<map.h>
#include<utility>
#include<string.h>
using namespace std;
class A
{
private:
char name[10];
int id_no;
public:
A(char *myname,int no):id_no(no)
{
strcpy(name,myname);
}
void output()
{
cout<<name<<'\t'<<id_no<<endl;
}
};
int main()
{
map<A,int> e;
A a("abc",10);
A b("xyz",1);
e.insert(pair<A,int>(a,123));
e.insert(pair<A,int>(b,345));
for(map<A,int>::iterator ii = e.begin(); ii!= e.end(); ii++)
{
cout<<(*ii).first<<"rank is"<<(*ii).second<<endl;
}
return 0;
}
#include<iostream>
#include<map>
#include<utility>
#include<string>
using namespace std;
class A
{
private:
string name; // there is no reason not using string here
int id_no;
public:
A(char *myname, int no) : name(myname), id_no(no)
{
}
void output()
{
cout<<name<<'\t'<<id_no<<endl;
}
const string & getName() const
{
return name;
}
bool operator<(const A &right) const // A must be comparable to be used as keys
{
return name < right.name;
}
};
int main()
{
map<A,int> e;
A a("abc",10);
A b("xyz",1);
e.insert(pair<A,int>(a,123));
e.insert(pair<A,int>(b,345));
for(map<A,int>::iterator ii = e.begin(); ii!= e.end(); ii++)
{
cout<<(*ii).first.getName() << " rank is " <<(*ii).second<<endl;
}
return 0;
}
I am getting error while compiling this code.
std::map uses std::less(default) to compare keys. std::less uses operator< to compare passed objects. So, you should overload operator< for your class:
bool operator< (const A& a)
{
// compare
}
Based on what the key value will get sorted?
It will depends on your overloaded operator<.
Is that the correct way to print the value of class object?
The more general way: you should overload operator<< for std::ostream object and your class object:
friend std::ostream& operator<< (std::ostream& stream, const A& a)
{
stream << a.name << '\t' << a.id_no << std::endl;
return stream;
}
Only then you can print it like this:
cout << ii -> first << "rank is" << ii -> second << endl;
Without it you should use your output function:
ii -> first.output();
cout << "rank is" << ii -> second << endl;

error pushing structure objects with string members into a std::vector

I'm a C#, C programmer learning C++ and I'm having a bit of a trouble.
I'm trying to push an object of the struct type 'Person' into a vector but the string values which are members of the Person type are not copied. Also the code exits with an error message - posted at the bottom:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef struct Person {
string Name;
string Lastname;
int Age;
} Person;
void CreatePerson(Person* in_person, string in_name, string in_last,
int in_age)
{
Person t_person;
t_person.Name = in_name;
t_person.Lastname = in_last;
t_person.Age = in_age;
memcpy(in_person, &t_person, sizeof(t_person));
}
int main(int argc, char *argv[])
{
vector<Person> people;
Person t_ppl;
CreatePerson(&t_ppl, "Zareh", "Petros", 13);
people.push_back(t_ppl);
CreatePerson(&t_ppl, "Tina", "Yarroos", 26);
people.push_back(t_ppl);
int ii;
for(ii=0; ii < people.size() ; ii++) {
cout << "Element - " << ii << endl;
cout << "name:" << people[ii].Name << endl;
cout << "lastname:" << people[ii].Lastname << endl;
cout << "age:" << people[ii].Age << endl;
}
return 0;
}
And here is the error message:
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x09d48048 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75ee2)[0xb74e8ee2]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x1f)[0xb76e551f]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSs4_Rep10_M_destroyERKSaIcE+0x1b)[0xb76cc99b]
/usr/lib/i386-linux-gnu/libstdc++.so.6(+0x909dc)[0xb76cc9dc]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZNSsD1Ev+0x2e)[0xb76cca4e]
std::string is a class and should not be copied by a memcpy operation. It may hold instance-specific data, which will get scrambled if held by two different instances (and probably that's the cause of your problems).
Imagine, that std::string is something like:
class string
{
private:
char * data;
int dataLength;
};
If you memcpy one string to another, both data and dataLength are copied to another place (lately being treated as a normal string instance). However, when the destructor is called on these strings (when they run out of scope), they will attempt to free the data held in data field. First string (which is an actual owner of this pointer) will free memory pointed to by this pointer. But then another destructor of your copied string will run and will try to free this memory again, what is not permitted.
Note, that it's exactly, what your system is reporting: double freeing of memory.
Your code is very C-style. In C++ one would create a class with constructor rather than a function, which fills in a struct. I would write your code in the following way:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <string.h>
using namespace std;
struct Person
{
public:
string Name;
string Lastname;
int Age;
Person(string newName, string newLastname, int newAge)
: Name(newName), Lastname(newLastname), Age(newAge)
{
}
};
int main(int argc, char *argv[])
{
vector<Person> people;
Person person1("Zareh", "Petros", 13);
people.push_back(person1);
Person person2("Tina", "Yarros", 26);
people.push_back(person2);
for(unsigned int i=0; i < people.size() ; i++)
{
cout << "Element - " << i << endl;
cout << "name:" << people[i].Name << endl;
cout << "lastname:" << people[i].Lastname << endl;
cout << "age:" << people[i].Age << endl;
}
getchar();
return 0;
}
The role of your creation method takes the class constructor. It fills in fields of the class properly. Also, C++ provides default copy-constructor and assignment operator, which will handle assigning one person to another properly.
Some side notes about your code style.
In C++ avoid using memcpy. If you have a need of using it, you probably should consider creating proper copy constructor, std::copy or simply making an assignment (what would work perfectly in your situation). memcpy should be used only when copying raw chunks of memory.
typedef is no longer required for structs in C++. Instead of writing:
typedef struct Name { ... } Name;
You can simply write:
struct Name { ... };
You've already been told you shouldn't be using memcpy in this situation, so I won't bother repeating that.
The problem with your CreatePerson goes well beyond using memcpy, and just changing to std::copy isn't really going to make it right.
Instead of a free function to create a person, you should almost certainly write that functionality as a constructor instead:
struct Person {
string Name;
string Lastname;
int Age;
Person(string Name, string Last, int Age)
: Name(Name), LastName(Last), Age(Age)
{}
};
With this, we can create Person objects much more cleanly:
std:::vector<Person> people;
people.push_back(Person("Zarah", "Petros", 13));
people.push_back(Person("Tina", "Yarroos", 26));
I'd also write an inserter that's responsible for displaying a Person in the proper format:
std::ostream &operator<<(std::ostream &os, Person const &p) {
return os << "Name: " << p.Name < "\n"
<< "Last: " << p.LastName << "\n"
<< "Age: " << p.Age << "\n";
}
With this, your mainstream of your code can insert complete Person objects into a stream without paying attention to the internal details of what a Person contains or how it should display:
for (int i=0; i<people.size(); i++)
std::cout << people[i] << "\n";
If you want to be a little more ambitious, you can use a standard algorithm instead:
std:copy(people.begin(), people.end(),
std::ostream_iterator<Person>(std::cout, "\n"));
Or, if you're using a relatively new compiler, you can use a range-based for loop:
for (auto &p : people)
std::cout << p << "\n";
Putting all that together, your complete program ends up something like this:
#include <string>
#include <iostream>
#include <vector>
using std::string;
struct Person {
string Name;
string LastName;
int Age;
Person(string Name, string Last, int Age)
: Name(Name), LastName(Last), Age(Age)
{}
};
std::ostream &operator<<(std::ostream &os, Person const &p) {
return os << "Name: " << p.Name << "\n"
<< "Last: " << p.LastName << "\n"
<< "Age: " << p.Age << "\n";
}
int main(){
std::vector<Person> people;
people.push_back(Person("Zarah", "Petros", 13));
people.push_back(Person("Tina", "Yarroos", 26));
for (auto &p : people)
std::cout << p << "\n";
return 0;
}
Do not use memcpy() on non-POD types. It does not call copy-constructors.
Use std::copy() instead.
In this case, it is easier to do an assignment. Replace:
memcpy(in_person, &t_person, sizeof(t_person));
with
*in_person = t_person;

creating a display function

I am working on this for an assignment and cannot figure out how to even get started to write the function for the following program.
The code given is:
#include <iostream>
#include <string>
#include "BACCOUNT.H"
using namespace std;
void display(const bankAccount & anAcct)
{
}
int main()
{
bankAccount a("Annie Hill", 123.00);
bankAccount b("Becker" , 45.60);
display(a);
display(b);
return 0;
}
and I need to write a display function that will make the name and balance for bankAccount a and bankAccount b display.
I've been working on this for about 3 days now, and cannot figure out how to even start.
my output needs to be:
bankAccount: Hill, Annie, $123.00
bankAccount: Becker, Bob, $45.00
what I've got so far is;
anAcct.name();
string name = anAcct.name();
int space = name.find("5");
name.substr(0, 5);
name.substr(5, 9);
name.length();
cout << name;
Which I know is way off, I've just been trying trial and error.
You can overload << operator in your bankAccount class and let that operator print the content of your class from display function.
e.g. code for reference
friend std::ostream& operator<<(std::ostream& os, bankAccount& a)
{
os << a.name() << ":" << a.accountBalance();
}
void display(const bankAccount & anAcct)
{
std::cout << anAcct;
}
bankAccount is you class and you should have at leat 2 variables in it !. is the name and second will be the amount and you need a diolay() function that display those variable using getters
void bankAccount::display(){
std::cout << this->getname() <<this->getamount<< "$ "<< std::endl;
}
in the main function using a.display() and there you go