Sort a vector of objects - c++

I'm working with a simple c++ program, with this class:
(I'm a very beginner programmer)
class Car{
private:
string newBrand;
int newMileage;
int newYear;
double newPrice;
(i didn't post the public classes)
bool sortByName(Car &CarVector)
{
return CarVector.getBrand() < CarVector.getBrand();
}
Main:
int main(){
vector<Car> CarVector;
ReadFile(CarVector);
ListCar(CarVector);
return 0;
}
LIst Car Functions, when i call the function "sort", to order my vector of objects by name:
void ListCar(vector<Car>&CarVector){
int i, op;
system("CLS");
sort(CarVector.begin(), CarVector.end(), sortByName);
cout << "MENU::CAR LIST BY NAME" << endl;
cout << ":Brand: \t:Mileage: \t:Year: \t\t:Price:" << endl;
for(i=0; i<CarVector.size();i++)
{
cout << CarVector[i].getBrand() << " \t\t";
cout << CarVector[i].getMileage() << " \t\t";
cout << CarVector[i].getYear() << " \t\t";
cout << CarVector[i].getPrice() << " \t\t";
cout << endl;
}
do
{
cout << endl << "1.Back: ";
cin >> op;
}while(op!=1);
}
I think that this program is supposed to work. Can you help me finding the error?
Best regards

In your compare function, you need to use two parameters: it should compare these objects with each other. Also, that's independent of any vector (it operates on elements of your vector), so you shouldn't name the arguments like that to avoid confusion.
So the function could look like this:
bool sortByName(Car &a, Car &b)
{
return a.getBrand() < b.getBrand();
}
Additionally, it's a good choice (but not always required) to add const to parameteres passed by reference in order to indicate that the function body doesn't modify them:
bool sortByName(const Car &a, const Car &b)
{
return a.getBrand() < b.getBrand();
}
But then it's necessary to put const at the end of the signature of the function Car::getBrand() in order to indicate that that function won't modify the object it operates on. This is called const-correctness. As mentioned before, that process is not always required (like when using std::sort) but it's a good style to have const-correctness.
Or you can use a lambda if your compiler supports it (you need to enable C++11 support):
std::sort(CarVector.begin(), CarVector.end(), [](const Car &a, const Car &b){
return a.getBrand() < b.getBrand();
});
Note that if you're going to compare cars always by name, it makes sense to implement an operator< for your class. When doing that, you don't need to specify a compare function in the call to std::sort, as those objects are then simply compared like a < b instead of yourFunction(a, b).

bool sortByName(Car &CarVector1,Car &CarVector2)
{
return CarVector1.getBrand() < CarVector2.getBrand();
}
use this sortByName

You have to pass two Car Objects in sortByName method to compare two Objects as :
bool sortByName(Car &C1,Car &C2)
{
return C1.getBrand() < C2.getBrand();
}
Check this link for more detail

Related

Why does the == operator of std::unordered_multiset<T> returns wrong result when T is a pointer type?

Is this a bug, or am I doing something wrong? I already tried providing hashing and equality functors for the pointer type, but it doesn't seem to work. I even tried creating my own miniature template container just to test the functors.
Hashing functor:
class CharPtHash
{
private:
using pChar = char*;
public:
size_t operator()(const pChar& c) const
{
std::hash<char> hasher;
if (c == nullptr)
{
return 0;
}
return hasher(*c);
}
};
Equality:
class CharPtEqual
{
private:
using pChar = char*;
public:
bool operator()(const pChar& lhs, const pChar& rhs)const
{
if (lhs == rhs)//not sure of nullptr is equal to itself.
{
return true;
}
else if (lhs==nullptr || rhs==nullptr)
{
return false;
}
return *lhs == *rhs;
}
};
Main:
int main()
{
cout << "Testing unordered_multiset with keys being simple types:\n";
unordered_multiset<char> sA1({ 'a','b','c' });
unordered_multiset<char> sA2({ 'a','c','b' });
cout << "Values: " << endl << sA1 << endl << sA2 << endl;
cout << (sA1 == sA2 ? "Equal" : "Not Equal");
cout << endl;
cout << "Testing unordered_multiset with keys being pointers to simple types:\n";
char** c1 = new char* [3]{ new char('a'), new char('b'), new char('c') };
char** c2 = new char* [3]{ new char('a'), new char('c'), new char('b') };
unordered_multiset<char*,CharPtHash,CharPtEqual> sB1;
unordered_multiset<char*,CharPtHash,CharPtEqual> sB2;
sB1.insert(c1[0]);
sB1.insert(c1[1]);
sB1.insert(c1[2]);
sB2.insert(c2[0]);
sB2.insert(c2[1]);
sB2.insert(c2[2]);
cout << "Values: " << endl << sB1 << endl << sB2 << endl;
cout << (sB1 == sB2 ? "Equal" : "Not Equal");
cout << endl;
cin.get();
}
I tried compiling it to c++20 and c++14 using Visual Studio 2022.
This is the output:
Testing unordered_multiset with keys being simple types:
Values:
{ a, b, c }
{ a, c, b }
Equal
Testing unordered_multiset with keys being pointers to simple types:
Values:
{ a, b, c }
{ a, c, b }
Not Equal
Well, my previous answer was completely wrong: as far as I can tell, your Hash and Pred are correct. However, the issue is somewhere else. operator == for std::unordered_multiset uses std::is_permutation() to perform comparison internally and doesn't provide any comparison function that algorithm, so default operator == for that type (in this case char*) is used. There is UB due to that I think, but I don't really understand the phrasing there.
To be fair, this looks like an oversight in standard. operator == for std::unordered_multimap doesn't allow comparing different types of maps, so it should be possible to pass Pred instance to std::is_permutation. Or maybe there is a reason to have it that way, but I don't see it.
Supplying your own KeyEqual only change the behavior internally, i.e. inserting new items. However it has no effects on operator==.
According to operator==(std::unordered_multiset), the behavior of it is as if each equivalent equal_ranges were compared with std::is_permutation.
You can potentially specialize the behavior of std::is_permutation for your set pre-C++20(this is undefined behavior since C++20):
template<>
bool std::is_permutation(
std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator l_begin,
std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator l_end,
std::unordered_multiset<char*, CharPtHash, CharPtEqual>::const_iterator r_begin)
{
return std::is_permutation(l_begin, l_end, r_begin, CharPtEqual{});
}
Or just create your own char* wrapper with a custom operator==.

Container Class vs Class - C++

I'm new to programming and just come across this assignment
Create a container class family that holds an array of 20 person objects.
I have been looking on the internet as well as in my book, but I still can't figure out the difference between a Container Class and a Class in C++.
How could I create a family class and 20 person objects at the same time?
"Container class" is not some official term; it's just the word "class" with an English describing word next to it. The assignment is asking you to create a class that contains some other stuff; namely, an array of 20 person objects.
At its most basic, the result could be as simple as this:
class family
{
public:
person people[20];
};
In real life, you might do something like this instead:
#include <array>
using family = std::array<person, 20>;
It seems unlikely that every family (or even most families) has precisely 20 people in it, so I would personally end up just going with:
#include <vector>
std::vector<person> family;
… and manipulating the vector as appropriate.
C++ is an object oriented language that encourages you to "encapsulate". The first step of this is to group concepts into objects described in terms of their data values and the operations that can be performed on that data.
So one could define an Account class consisting of an id and balance and functions to deposit or withdraw currency. This definition forms the type, but when you "instantiate" (create an instance of) this type, i.e.
Account a;
then the variable a refers to an object of type Account in this scope.
Sometimes you need a class that can store and track objects of other types. This is what is sometimes referred to as a "container class" and typically it combines a store and a count.
Say we want to store some floats. We could just write:
float store[64];
std::cout << "Enter the first number: ";
std::cin >> store[0];
But how would we track how many floats we have? We would probably need a counter.,
float store[64];
int stored = 0;
std::cout << "Enter the first number: ";
std::cin >> store[0];
stored++;
std::cout << "Enter the second number: ";
std::cin >> store[1];
stored++;
This works, and it's not terribly difficult, but if you are writing a function that expects to take a store and it's size, how do you express that?
void myFunction(std::string label, float* store, int count);
This requires two arguments and it's not exactly explicit.
C++ is about encapsulation: this idea of a "store" with a count of the contents could be encapsulated into a class:
struct Store {
float store_[64] {};
int count_ {0};
};
this is a container. We can now write our function that takes an object that contains other values with a single parameter:
void myFunction(std::string label, Store& store); // & here = by reference
If this was 'C' you would write code that directly manipulated the values in the store:
store.store_[N] = 1;
store.count_++;
but that's messy, we didn't check there was room. In C++, we can encapsulate this into the class description with member functions and hide the member variables so that you have to go through our proscribed interface to manipulate the data.
#include <iostream>
class Store {
enum { MaxCount = 64 };
float store_[MaxCount] {};
size_t count_ = 0;
public:
// return the maximum number of elements we can store
size_t capacity() const { return MaxCount; }
// true/false: is the store empty?
bool empty() const { return count_ == 0; }
// return the current count
size_t size() const { return count_; }
bool add(float value) {
if (count_ >= capacity()) {
std::cerr << "store is full!\n";
return false;
}
store_[count_] = value;
++count_;
}
// reset
void clear() {
count_ = 0; // we don't actually need to change the store
}
// allow array-like usage
const float& operator[](size_t index) const { return store_[index]; }
float& operator[](size_t index) { return store_[index]; }
// provide bounds-checked array-ish access
float at(size_t index) const {
if (index >= count_)
throw std::invalid_argument("array index out of bounds");
return store_[index];
}
};
int main() {
Store store;
for (size_t i = 0; i < store.capacity(); ++i) {
std::cout << "Enter number #" << i << " or -ve to stop: " << std::flush;
float f = -1;
std::cin >> f;
std::cout << "\n" << f << "\n";
if (f < 0)
break;
store.add(f);
}
std::cout << "You entered " << store.size() << " values:";
for (size_t i = 0; i < store.size(); ++i) {
std::cout << ' ' << store[i];
}
std::cout << '\n';
}
Live demo: http://ideone.com/boE3Ki
They are telling you to create a class that acts as a container for an array. In programming, especially when you first start, you will see many elements called containers because your teacher wants you to view variables, classes, and arrays (among other programming tools) as containers for you to store data in.
Viewing programming this way makes is much easier to conceptualize information as you get into more complex ideas like pointers.
So, long answer short, create a class with the array inside it. If you are learning about constructors and overloading make sure you are initializing it based on the data you pass in. If not, it should be only a couple lines of code to complete the project.

Using a map that has a abstract base class pointer and calling a derived class function

I've searched on the web and can't find any solutions to my problem I hope you can help.
So I have constructed an abstract base class and have two derived classes that represents different experiments. (one is actually a derived derived class of my base class) And I made a map as such in a separate header file to store different types of experiments.
//Map class template to store name of experiment and the experiment within a project
typedef map <string, baseData <double>*> ExpContainer;
void search(string searchName, ExpContainer exps) {
ExpContainer::iterator Iter;
Iter = exps.find(searchName); //finds the entry corresponding to searchName and returns the iterator
if (Iter != exps.end()) { //need this as if entry is not found, the return will be end iter.
cout << "Found the experiment " << Iter->first << "." << endl;
Iter->second->print();
}
else {
cout << "Sorry, experiment " << searchName << " is not found" << endl;
}
}
The print() function is different for each experiment type and I know there's a problem called slicing so I've made sure that print() is virtual in the base class. Here's my base class:
namespace mynmsp {
//base class of data can be measurements or errors
template < class T> class baseData {
public:
virtual void print() =0;
virtual ~baseData() {
cout << "Destructor called for the base class." << endl;
}
};
}
Then in my main.cpp I've constructed different types of experiment and I want to print them. Each experiment class has different implementation of the print function that overrides the print function from the base class, like:
void print(){ //do something };
And in my main.cpp I have the map defined as:
ExpContainer project;
And after I have constructed each experiment, I've asked the user for the name of the experiment (expName) and inserted into project as such:
project[expName] = &expC;
I think the insertion is fine as I tested the size of project and it was correct.
However, a runtime error occured when my search function was called like this:
search(name, project);
I don't know if there's a problem with slicing or with my pointers?
I tried to make print() a virtual function in each derived class but that doesn't seem to work either.
Apologies for the long question, please help!
Edit: I've constructed my experiments inside a do while loop while project is declared outside. The whole code is very long but its basics is something like this:
string repeatstr; //user input whether to repeat do while loop or not
bool repeat = true; //condition for staying inside do while loop
ExpContainer project; //declared project to store experiments
do {
string expName;
string ans1; //character to store user input
cout << "What is the name of your experiment? " << endl;
cin >> expName;
cout << "Is this a experiment C ? (y/n)" << endl;
cin >> ans1;
if(ans1 =="y"){
//call the constructor for expC
project[expName] = &expC;
}else {
//call the constructor for expB
project[expName] = &expB;
}
cout << "Do you want to enter another experiment? (y/n)" << endl;
cin >> repeatstr;
if (repeatstr == "n") { repeat = false; }
}while (repeat); //loop over this do-while loop while repeat is true
cout << "There are " << project.size() << " in this database." << endl;
//to retrieve info from a certain experiment
string input, name;
cout << "Would you like to retrieve any experiments (y/n)? " << endl;
input = EitherInput("y", "n");
if (input == "y") {
cout << "Please enter the name of the experiment you want to retrieve: " << endl;
cin >> name;
search(name, project); //code breaks down here!
}
You are saving a pointer to the object that was already destroyed. You can check the addresses that you have in the map, most probably they are the same. You should store your experiment object in dynamic memory
if(ans1 =="y")
{
project[expName] = new expC();
} // Scope ends here and all variable created in it will be destroyed.
else
{
project[expName] = new expB();
} // Scope ends here and all variable created in it will be destroyed.
And after you are done with them you need to call delete on each pointer to avoid memory leak. Also you need to check if the items in the map are already existing, otherwise you will loose pointers to allocated memory which is automatically a memory leak.
I would recommend you to use std::share_ptr< baseData<double> > instead of bare baseData<double>*. Here you can read more about it. And also consider using typedef in order to have more clear syntax.
P.S.
The function
void search(string searchName, ExpContainer exps)
will copy whole map to its body. Use constant reference instead
void search(string searchName, const ExpContainer& exps)
But then you'll also need to declare function print as const:
virtual void print() const = 0;
and override it with const modifier:
virtual void print() const override;
And use constant iterator ExpContainer::const_iterator Iter

how to overload << operator to output a vector that is a member of a class

I'm trying to use the << operator to output vectors that are private members of my class.
The compiler won't let me access the vectors directly as they are private, but it also won't let me access public member functions that return the vectors.
How do I make the << operator output all the contents of a private member vector?
This is my class:
class Name_pairs
{
public:
Name_pairs (){}
//....
vector<string> Names (){return names; }
vector<double> Ages (){return ages; }
vector<double> Sorted_ages (){return sorted_ages;}
private:
//....
vector<string> names;
vector<double> ages;
vector<double> sorted_ages;
};
This is the overloaded << function:
ostream& operator<<(ostream& os, const Name_pairs & n)
{
return os<< n.Names(); //won't let me access
return os<< n.names.size(); //won't let me access
}
This is the print function that I'm trying to replace by overloading the << function:
void Name_pairs:: print_name_age ()
{
cout << endl << endl;
cout << "These names and ages are now sorted" << endl;
for(int index = 0; index < names.size(); ++index)
{
cout << "index " << index << ": " << names[index]<< " is age: " << sorted_ages[index] <<endl;
}
}
n.Names() returns a vector and you can't print vectors directly through a standard operator << method. You have to iterate through the vector and print its elements.
std::ostream& operator<<(std::ostream& os, const Name_pairs& n)
{
if (!os.good())
return os;
auto names = n.Names();
std::copy(names.begin(), names.end(),
std::ostream_iterator<std::string>(os));
return os;
}
The line
return os<< n.Names(); //won't let me access
doesn't work, because you're trying to write a whole vector at once, instead of it's elements, and ostream doesn't provide an overloaded operator << for the std::vector. The solution is just writing elements from the vector, that's being returned by this function.
for(int i=0;i<n.Names().size();i++)
cout << n.Names()[i];
As a side note: you probably don't want to use your version with large vectors, since (unless your compiler is smart enough to make the function inline), will consume a lot of time to return the whole vector. Try returning a const reference to the vector, instead of the vector itself.

C++ remove_if overwriting my vector

My remove_if seems to be overwriting the elements that are not filtered out with values of filtered out elements. The purpose of these code is to allow user to filter and display only teacher from a certain category. (Not deleting any element)
Here are some of the code
static string compare;
static string debug;
bool filter_Cat (Teacher &t)
{
return (t.getCat() != compare);
}
void filterCat (vector<Teacher> &t)
{
vector<Teacher>::iterator i;
vector<Teacher>::iterator newedited = remove_if(t.begin(), t.end(), filter_Cat);
for (i = t.begin(); i != newedited; ++i)
{
Teacher& te = *i;
te.getName();
cout << "\t";
te.getCategory();
cout << "\t";
te.getLocation();
}
}
void filterTutorCat(vector<Teacher> &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
if(choice <= 3 && choice > 0)
{
if (choice == 1)
{
compare = "English";
filterCat(t);
}
if (choice == 2)
{
compare = "Math";
filterCat(t);
}
if (choice == 3)
{
compare = "Science";
filterCat(t);
}
}
else
{
cout << "Invalid Option" << endl;
}
}
remove_if shifts elements, for which the compare function returns false, from right to left; which in other words means, it overwrites the elements, for which compare returns true, with elements, for which compare returns false. The size of the vector doesn't change, however.
This reads,
Removes all elements satisfying specific criteria from the range [first, last). The first version removes all elements that are equal to value, the second version removes all elements for which predicate p returns true.
Removing is done by shifting the elements in the range in such a way that elements to be erased are overwritten. The elements between the old and the new ends of the range have unspecified values. Iterator to the new end of the range is returned. Relative order of the elements that remain is preserved.
So what you want to do should be expressed as:
void filterCat (vector<Teacher> &v)
{
for (vector<Teacher>::iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter_Cat(*i))
{
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
It seems in your code, getName() prints the name which ideally it should not do, instead it should return name. So I would suggest you to change it to make it return name. And do the same for getCategory as well. Choose your name correctly. If it is getName(), you should get you name by returning it; if it is printName(), then it should print name.
Also, the code which you've written isn't good:
You should avoid global variables.
You should avoid if-else as much as possible. Learn better ways.
You should learn about function objects (or functor)
You should learn about const member function.
You should understand the difference between iterator and const_iterator, and their usage.
You should understand the difference between const reference, and non-const reference. And try using them appropriately.
So I would write your code as:
//this is functor, not a function
struct filter_cat
{
std::string m_cat; //use member data, avoid global variable
filter_cat(std::string const & cat) : m_cat(cat) {}
bool operator()(Teacher const & t) const //const member function
{
return (t.getCat() != m_cat); //getCat should be const member function
}
};
//pass vector by const reference
void filterCat (vector<Teacher> const & v, filter_cat filter)
{
//use const_iterator here, instead of iterator
for (vector<Teacher>::const_iterator it = v.begin(); it != v.end() ; ++it)
{
if (!filter(*i))
{
//getName and getCategory should be const member function
std::cout << i->getName() <<"\t" << i->getCategory() << std::endl;
}
}
}
void filterTutorCat(vector<Teacher> const &t)
{
int choice;
cout << "No\tCategory" << endl
<< "1\tEnglish" << endl
<< "2\tMath" << endl
<< "3\tScience" << endl
<< "Choose the category you wish to filter :";
cin >> choice;
getline(cin, debug);
//avoid if-else as much as possible, learn better ways!
std::string cats[] = {"English", "Math", "Science"};
if(choice <= 3 && choice > 0)
{
filterCat(v, filter_cat(cats[choice-1]));
}
else
{
cout << "Invalid Option" << endl;
}
}
As noted in the comments: getCat, getName and getCategory should be const member functions. In fact, if getCategory returns category, then getCat isn't even needed.
Solved my issue.
remove_if collects the values for which filter_Cat returns false at the start of the container. While it doesn't reduce the number of elements in the container it neither does make any guarantees about the values of the elements beyond the returned range. So you are loosing values when using remove_if.