I have this interesting assignment where I have a std::map of CTurist (previous class) and unsigned variable. Here's the code:
class CTurist
{
protected:
string tName;
int age;
public:
CTurist() {};
CTurist(string name, int age2)
{
tName = name;
age = age2;
}
bool operator<(const CTurist& e) const
{
return age < e.age;
}
friend ostream& operator<<(ostream& os, const CTurist&& e);
friend ifstream& operator>>(ifstream& is, CTurist&& e);
};
class CHotel:public CTurist
{
protected:
string hName;
int stars;
int beds;
map<CTurist, unsigned> Turisti;
public:
unsigned Sum = 0;
CHotel(){};
CHotel(string hName2, int zvezdi, int legla)
{
hName = hName;
stars = zvezdi;
beds = legla;
}
int Compare()
{
list<CTurist*> list;
int br=0;
CTurist vyzrast;
map<CTurist, unsigned>::iterator it = Turisti.begin();
while (it != Turisti.end())
{
if (it->first < vyzrast)
{
br++;
}
else
{
list.push_back(std::move(&it->first));
}
}
}
};
I know it's quite a long one, but I thought it's best to give you all the information.
Now, the int Compare() function AT THE BOTTOM is the one causing me problems.
I have to check if the age of the tourists is above or below a parameter which I've called vyzrast here. We're comparing the age. If it's below, it's quite straight forward.
If it's above though, I have to add those Tourists to a list<CTurist*>, aka. to a list of pointers. If I make the list from objects, not pointers. No luck with this, hence why I'm looking for suggestions how to fix it here.
Why are you using rvalue references for your operator overloads? You should be using const CTurist & for operator<< and CTurist& for operator>>. And you should be using std::istream for operator>>:
friend ostream& operator<<(ostream& os, const CTurist &e);
friend istream& operator>>(istream& is, CTurist &e);
Aside from that, there is no reason for Compare() to use std::move() at all when populating the std::list, as it is adding pointers, not moving actual objects.
Compare() doesn't won't correctly for several reasons:
it->first is an actual const CTurist object, but your std::list is expecting CTurist* pointers. std::map keys are const, so &it->first is a CTurist const * pointer (non-const pointer to const object), not a CTurist* pointer (non-const pointer to non-const object) like you are expecting.
Your vyzrast object is uninitialized. You are not assigning any value to its age (and tName) member at all, so the result of your comparisons is indeterminate.
You are not incrementing your it iterator on each loop iteration, so you are stuck in an infinite loop if the std::map is not empty.
Try something more like this instead:
int Compare()
{
std::list<const CTurist *> clist;
int br = 0;
CTurist vyzrast("", SomeAgeValueHere);
std::map<CTurist, unsigned>::iterator it = Turisti.begin();
while (it != Turisti.end())
{
if (it->first < vyzrast)
{
br++;
}
else
{
clist.push_back(&it->first);
}
++it;
}
// use clist as needed...
return SomeValueHere;
}
Live Demo
Related
I was looking at this. The author first defined operator<() in my_data and said "everything is normal". After adding a member variable, he said "operator<() does not really implement a comparison operation".
I want to know what is the difference between the two and why the former is wrong?
struct my_data
{
std::string key;
std::string value;
//first
bool operator<(const my_data data)const {
return key < data.key;
}
};
//second
struct Compare
{
bool operator()(const my_data& l, const my_data& r)
const {
return l.key < r.key;
}
};
from there
With
struct my_data
{
std::string key;
std::string value;
//first
bool operator<(const my_data data)const {
return key < data.key;
}
};
std::set<my_data> data;
You can use the class with a std::set, but your operator < isn't using all of the object, it is just comparing a single field. That is what the author of the video is complaining about. They take issue that it only compares one field of the object, and not the whole state of the object. By switching to
struct my_data
{
std::string key;
std::string value;
};
struct Compare
{
bool operator()(const my_data& l, const my_data& r)
const {
return l.key < r.key;
}
};
std::set<my_data, Compare> data;
There is no longer a "lying" operator <, but instead a custom comparator that compares just what you want.
I have a class CPerson
class CPerson
{
private:
string name;
string egn;
public:
CPerson() {};
CPerson(string getname, string getegn)
{
name = getname;
egn = getegn;
}
bool operator < (const CPerson &obj)
{
...
}
bool operator ==(const CPerson &obj)
{
...
}
friend ostream& operator << (ostream& out, const CPerson &obj) //извеждане в поток - конзола
{
...
}
friend istream& operator >> (istream& in, CPerson &obj) //четене от поток - конзола
{
...
}
friend ofstream& operator <<(ofstream& out, const CPerson& obj) // извеждане в поток - файл
{
...
}
friend ifstream& operator >>(ifstream& in, CPerson& obj) // четене от поток - файл
{
...
}
I made another class CCity which has a vector of objects CPerson
class CCity
{
private:
string city_name;
vector <CPerson> people;
public:
CCity() {};
CCity(string filename)
{
string name, egn;
ifstream file(filename);
file >> city_name;
while (file >> egn >> name)
{
people.push_back(CPerson(name, egn));
}
}
friend ostream& operator <<(ostream& out, const CCity& obj)
{
vector<CPerson>::iterator iter;
for (iter = people.begin(); iter != people.end(); iter++)
{
}
}
};
I am trying to overload the output operator so I can output my vector of objects but I get the error "left of '.begin' must have class/struct/union". I can't see anything wrong with my code. I have include and
At very first:
friend ostream& operator <<(ostream& out, const CCity& obj);
does not define a member function! Declaring the function (operator) as friend makes it a free-standing one, even if defined inside the class. So there's no people object inside. You need to refer to by obj.people!
Then why do you declare iter in front of the loop? Do you need the iterator for any reason afterwards? If not, prefer keeping it local to for loop. Then you can additionally let the type get deduced: for(auto iter = .... auto will additionally cover another error: obj is const, so obj.people is const as well. And the begin overload for constant objects won't return an iterator, but a const_iterator! Be aware that there are two versions of begin:
iterator begin();
const_iterator begin() const; // <- that one will be called on const objects!
Would you break the loop in between? If not, you could use a range based for loop as well: for(auto& person : obj.people). Again the type of person gets deduced, as we explicitly told auto to be a reference, type will be Person const&.
Whichever variant you chose, you now can output the person:
out << *iter;
out << person;
Assuming they were public, you could access the person's attributes as well:
iter->name;
person.name;
How to implement the comparison function in this case?
void remove(const Object1 &object1) {
for(auto &object2 : objectVector) {
if(object1 == object2) {
/*....... */
}
}
}
You are asking 2 questions:
How can objects be made comparable:
By implementing operator== for the class. Be sure you override operator != then too.
As a member function:
bool Object1::operator ==(const Object1 &b) const
{
// return true if *this equals b;
}
Or as a free function:
bool operator ==(const Object1 &a, const Object1 &b)
How can objects with a given value be removed from a vector:
The easiest way is to use std::remove:
objectVector.erase(std::remove(objectVector.begin(), objectVector.end(), object1), objectVector.end());
You can remove objects also while iterating through the vector, but you have to keep in mind that the vector iterator is invalidated then. You may not further iterate on the vector then.
An object of your class can contain different types and number of members so let's say that you have a class A:
class A{
int x,
float y;
char* z;
};
And you have two instances:
A a1;
A a2;
To compare:
if(a1 == a2) // compile-time error
;// DoSomeThing
Above you get the error because the compiler doesn't know which fields will be compared against each other. The solution is to overload the equals operator "==" to work on objects of your class.
class Student{
public:
Student(std::string, int age);
std::string getName()const;
int getAge()const;
bool operator == (const Student&);
private:
std::string name;
int age;
};
Student::Student(std::string str, int x) :
name(str), age(x){
}
bool Student::operator == (const Student& rhs){
return age == rhs.age;
}
std::string Student::getName()const{
return name;
}
int Student::getAge()const{
return age;
}
int main(){
Student a("Alpha", 77);
Student b("Beta", 49);
if(a == b)
std::cout << a.getName() << " is the same age as " <<
b.getName() << std::endl;
cout << endl << endl;
return 0;
}
Now the compiler knows how to compare objects of your class and know what the equals operator compares on your ojects members.
if object is a class you can override the operator == in the class as a friend function, or you can implement your own function bool isEqual(Object1 o1, Object1 o2) { if(something) return true; return false; }
So I have created a program which can read a .dat file of about 20 lines containing info about different atoms (name, symbol, mass etc) and added them all to a vector of class type I made called Atom.
How would I write a function to find the atom with the highest mass?
Here is my class:
class Atom
{
string element, symbol;
float number;
float mass;
public:
Atom(string e, string s, float n, float m){
element = e; symbol = s; number = n; mass = m;
}
string getElement();
string getSymbol();
float getNumber();
float getMass();
float ratio();
friend ostream& operator<<(ostream& os, Atom c);
};
and the information is added to a vector with the following statements
ifstream fin("atoms.dat");
string E, S;
float M, N;
vector <Atom> periodic;
while(!fin.eof()){
fin >> E >> S >> M >> N;
Atom Atom(E, S, M, N);
periodic.push_back(Atom);
}
I want to be able to write a function which finds which atom has the highest mass, I've tried using a max_element function but I keep getting errors. Is there a quick way of comparing the member variables of class objects stored in a vector?
I'm currently using C++ 98 as it is what my course requires.
Thanks
I don't know what you did wrong with std::max_element, as you haven't provided what you've tried with it.
struct CompareAtomMass
{
bool operator()(const Atom& lhs, const Atom& rhs) {
return lhs.getMass() < rhs.getMass();
}
};
and then:
vector <Atom> periodic;
Atom max_atom = *max_element(periodic.begin(), periodic.end(), CompareAtomMax());
struct CompareAtomMass is called a function object. It's a class with operator() overloaded to return a bool. std::max_element requires just such a function object to spit out the max element as it needs a way to compare your Atoms.
EDIT:
You should mark your getter functions as const since they don't change the inner state of the class.
string getElement() const;
string getSymbol() const;
float getNumber() const;
float getMass() const;
This will allow you to call them from a const object of type Atom just as the above function object requires (const Atom&).
Variation of DeiDeis answer: If you only do this at one place, and don't feel a need to keep a CompareAtomMass function class, you can use a lambda:
const auto maxIt = max_element(periodic.begin(), periodic.end(),
[](const Atom& lhs, const Atom& rhs) {
return lhs.getMass() < rhs.getMass();
));
if(maxIt != periodic.end()){
// use *maxIt ;
}
in C++14 and later you can also use auto in lambdas:
const auto maxIt = max_element(periodic.begin(), periodic.end(),
[](const auto& lhs, const auto& rhs) {
return lhs.getMass() < rhs.getMass();
));
It is a good idea to make your member functions const.
This will allow this code. Otherwise just remove all const from my code.
In case your vector is empty, you will get null pointer.
struct AtomMassComparator
{
bool operator()(const Atom& lhs, const Atom& rhs)
{
return lhs.getMass() < rhs.getMass();
}
};
const Atom* getAtomWithHighestMass(const vector<Atom>& v)
{
vector<Atom>::const_iterator it = max_element(
v.begin(), v.end(), AtomMassComparator());
return v.end() == it ? 0 : &*it;
}
I've made a class student with subclass comparator. Constructor of comparator takes one argument called cmp_mode that specifies how we should compare students.
class student
{
public:
std::string name;
int course, group;
student(std::string name,
int course,
int group): name(name)
{
this->course = course;
this->group = group;
}
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
class comparator
{
cmp_mode mode;
public:
comparator(cmp_mode mode)
{
this->mode = mode;
}
bool compare(student s1, student s2)
{
if (mode == NAME)
return s1.name < s2.name;
else if (mode == COURSE)
return s1.course < s2.course;
else if (mode == GROUP)
return s1.group < s2.group;
else
throw "Oh god! We can't compare variables using these keys!";
}
};
};
Also, I've created a list of students and now I want to sort this list using comparator subclass.
std::list<student> l;
student st1("Anya", 2, 5);
student st2("Misha", 4, 2);
student st3("Yasha", 1, 3);
l.push_back(st1);
l.push_back(st2);
l.push_back(st3);
student::comparator by_group(student::GROUP);
l.sort(by_group.compare);
But I'm getting the following error.
ERROR: Reference to non-static member function must be called.
So what should I do? How can I adjust sorting in the better way?
I'm starting from your comment:
I thought that I can write compare function for each case, but it seems even worse.
Why worse? Personally I think it is faster to run (at least faster to compile) and easier to maintain (shorter functions).
Isn't this much simpler to write:
l.sort(student::compare_by_group);
And this implementation easier to maintain:
class student
{
...
static bool compare_by_name(const student& s1, const student& s2)
{
return s1.name < s2.name;
}
static bool compare_by_course(const student& s1, const student& s2)
{
return s1.course < s2.course;
}
static bool compare_by_group(const student& s1, const student& s2)
{
return s1.group < s2.group;
}
// Add the followings only if you really need this stuff
// e.g. to get comparator type from elsewhere
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
static bool compare(cmp_mode, const student& s1, const student& s2)
{
switch(cmp_mode) {
case NAME: return compare_by_name(s1, s2);
...
}
}
};
Like Piotr, I'd recommend to write comparators for each property, which is faster and less error-prone. However, I'd recommend to use functor objects instead of the static functions:
struct compare_by_name {
bool operator()(const student& a, const student& b) const {
return a.name < b.name;
}
};
If you need the comparators only once or maybe twice and you're using C++11, prefer an inline lambda:
l.sort([](const student& a, const student& b){ return a.name < b.name; });
If you absolutely need a dynamic comparator, write it as normal functor object, i.e., define an operator():
bool operator()(const student& a, const student& b) const {
switch (mode) {
case NAME:
return a.name < b.name;
// No default case, the constructor (if at all) should check whether the mode
// is valid.
}
}
Non-static member functions have an implicit parameter of the type of which the function is a member. They need an instance of this type in order to work. You can use std::bind (or boost.bind if you don't have C++11 support) to "bind" a student::comparator object to the first parameter of the comparison function:
student::comparator by_group(student::GROUP);
using namespace std::placeholders;
auto comp = std::bind(student::comparator::compare, &by_group, _1, _2);
l.sort(comp);
In the code above, the by_group object is bound as first argument to the student::comparator::compare function, and the resulting function object takes two student objects and returns a bool:
std::cout << std::boolalpha;
const student& s1 = l[0];
const student& s2 = l[1];
std::cout << comp(s1, s2) << "\n"; // prints "true" or "false".
I would also advise you to change the signature of the comparison member function to
bool compare(const student& s1, const student& s2) const;
There is no reason to pass by value, and there is no reason for the member function not to be const.