I have created a class "firefly" similar to this:
class firefly{
private:
float time_flash;
public:
firefly(int, int, float, float);//parametric constr.
firefly();
firefly(const firefly& a);//copy constructor
void receive_flash(std :: vector<firefly>&, float, float, int);
friend bool operator <(const firefly&) const;
};
Focus on the last two functions; I have two question about them.
In the main program I wanted to initialize a vector of fireflies as follows:
vector <firefly> fire_vec(10, firefly(5, 5,(float) 1., (float)1.) );
using the parametric constructor. Can I do this?
The secondo question.
This vector should be ordered by the algorithm sort,
sort(fire_vec.begin(), fire_vec.end());
having overloaded operator< as follows:
bool operator <(const firefly &rhs) const {return time_flash < rhs.time_flash;}
Is there anything wrong in doing this? (i think there is something wrong, because I can't)
I'm not sure what you're having trouble with, but if you'd tried to compile this code, you'd have found that
friend bool operator <(const firefly&) const;
is illegal: you can't have a const qualifier on a freestanding function. Also, operator< is a binary operator, so it should take two arguments, both of type const firefly &.
You can also implement operator< as a member function, as you suggest yourself, but then drop the friend declaration.
Apart from that, there's nothing wrong with your code, except maybe that sorting a vector of entirely equal elements is a waste of time.
Excuse me but why you choose vector+customized sorting rather than set/map with a customized comparator?
Normally, we use vector mostly due to a requirement on random access its elements via index. If that's not the case and especially in your case, you need a sorted vector, I'd suggest the set/map.
For the second question you will have to pass the overloaded function to sort as an argument
sort (myvector.begin()+4, myvector.end(), myfunction);
Related
By this question I am also trying to understand fundamentals of C++, as I am very new to C++. There are many good answers to problem of sorting a vector/list of custom classes, like this. In all of the examples the signature of comparator functions passed to sort are like this:
(const ClassType& obj1, const ClassType& obj2)
Is this signature mandatory for comparator functions? Or we can give some thing like this also:
(ClassType obj1, ClassType obj2)
Assuming I will modify the body of comparator accordingly.
If the first signature is mandatory, then why?
I want to understand reasons behind using const and reference'&'.
What I can think is const is because you don't want the comparator function to be able to modify the element. And reference is so that no multiple copies are created.
How should my signature be if I want to sort a vector which contains pointers to objects of custom class? Like (1) or (2) (see below) or both will work?
vertor to be sorted is of type vector
(1)
(const ClassType*& ptr1, const ClassType*& ptr2)
(2)
(ClassType* ptr1, ClassType* ptr2)
I recommend looking through This Documentation.
It explains that the signature of the compare function must be equivalent to:
bool cmp(const Type1& a, const Type2& b);
Being more precise it then goes on to explain that each parameter needs to be a type that is implicitly convertable from an object that is obtained by dereferencing an iterator to the sort function.
So if your iterator is std::vector<ClassType*>::iterator then your arguments need to be implicitly convertable to ClassType*.
If you are using something relatively small like an int or a pointer then I would accept them by value:
bool cmp(const ClassType* ptr1, const ClassType* ptr2) // this is more efficient
NOTE: I made them pointers to const because a sort function should not modify the values it is sorting.
(ClassType obj1, ClassType obj2)
In most situations this signature will also work, for comparators. The reason it is not used is because you have to realize that this is passing the objects by value, which requires the objects to be copied.
This will be a complete waste. The comparator function does not need to have its own copies of its parameters. All it needs are references to two objects it needs to compare, that's it. Additionally, a comparator function does not need to modify the objects it is comparing. It should not do that. Hence, explicitly using a const reference forces the compiler to issue a compilation error, if the comparator function is coded, in error, to modify the object.
And one situation where this will definitely not work is for classes that have deleted copy constructors. Instances of those classes cannot be copied, at all. You can still emplace them into the containers, but they cannot be copied. But they still can be compared.
const is so you know not to change the values while you're comparing them. Reference is because you don't want to make a copy of the value while you're trying to compare them -- they may not even be copyable.
It should look like your first example -- it's always a reference to the const type of the elements of the vector.
If you have vector, it's always:
T const & left, T const & right
So, if T is a pointer, then the signature for the comparison includes the comparison.
There's nothing really special about the STL. I use it for two main reasons, as a slightly more convenient array (std::vector) and because a balanced binary search tree is a hassle to implement. STL has a standard signature for comparators, so all the algorithms are written to operate on the '<' operation (so they test for equality with if(!( a < b || b < a)) ). They could just as easily have chosen the '>' operation or the C qsort() convention, and you can write your own templated sort routines to do that if you want. However it's easier to use C++ if everything uses the same conventions.
The comparators take const references because a comparator shouldn't modify what it is comparing, and because references are more efficient for objects than passing by value. If you just want to sort integers (rarely you need to sort just raw integers in a real program, though it's often done as an exercise) you can quite possibly write your own sort that passes by value and is a tiny bit faster than the STL sort as a consequence.
You can define the comparator with the following signature:
bool com(ClassType* const & lhs, ClassType* const & rhs);
Note the difference from your first option. (What is needed is a const reference to a ClassType* instead of a reference to a const ClassType*)
The second option should also be good.
I'm asked to overload a comparison operator for a structure.
friend bool operator< (const ObjectID& id1, const ObjectID& id2);
Do I need to consider to compare the cases where either id1 or id2 is const? How to implement then?
You do not need to consider the other cases, because in the case where the comparison gets passed non-const parameters the compiler will automatically add const for you to allow the single operator to match.
I am currently reading information from an input file. Of that information, there is a name. All the information is read into a struct. There is an array of these structs.
I need to alphabetize the structs by the last name, using a Binary Search Tree.
Do I need to write an operator overload function for ==, <, and >. If so can someone help me get started on that?
Yes, you will want to write operator overloads for == and <. (> is not needed; just use the else case after checking e.g. if (a < b); else if (a == b).)
In our case, since we are alphabetizing by last name, one struct is "less than" another if and only if its last name comes before the other's last name alphabetically.
So what exactly is the problem? Do you know how to write operator overloads? Do you know how to compare strings and determine which comes first alphabetically?
You need a way to compare any two instances of the struct. Writing a comparison operator, say, operator<(), might be a convenient way to go about it.
class Record {
friend bool operator<(const Record&, const Record&);
std::string name;
// ...
};
bool operator<(const Record& a, const Record& b)
{
// return true if the name of a is less than the name of b
}
Because a node inserts either on the left subtree or the right subtree you only need to know if a node is "less than" another node. If it isn't, then it doesn't matter whether it's greater or equal to the other node; it goes on the other subtree either way.
Of course, you might need the equality comparison for some other task. If you do then it's a good idea to go all the way and provide the inequality operator as well.
Also of interest is the rel_ops namespace.
Operator overloads work just like functions or methods. They get a return type and arguments in just the same way. For instance, a binary operator (like <) would be a member function with one argument or a free function with two, one each for each side of the operator. the only thing that's different is instead of having an identifier function name, they use a special syntax, the keyword operator followed by the operator being overloaded. So if we wanted to have a comparable type, we could do it this way:
class MyUserType
{
private:
std::string sort_significant;
std::string sort_insignificant;
public:
bool operator<(const MyUserType &) const;
};
bool MyUserType::operator<(const MyUserType & other) const
{
return sort_significant < other.sort_significant;
}
You would normally need to overload <, but if there are other elements in the struct that you might sometime want to sort by, it doesn't really make sense to do that. You should write a separate function that accepts two parameters of your struct, and compares them by last name, returning true if the first should come before the second, false otherwise. Then pass that function to a std::sort. Something like this:
bool compare_by_last_name(const MyStruct & lhs, const MyStruct & rhs)
{
return lhs.last_name < rhs.last_name;
}
// later
vector<MyStruct> v;
// put some elements in v
std::sort(v.begin(), v.end(), compare_by_last_name);
You will notice I have ignored your statement "Using a binary search tree", because I don't quite know what you mean, but it's probably irrelevant. Did you make your own container class or something?
I am following a book to learn C++ (come from a python background). I've written this, which works:
class CatalogueItem
{
public:
CatalogueItem();
CatalogueItem(int item_code, const string &name, const string &description);
~CatalogueItem() {};
bool operator< (const CatalogueItem &other) const;
...
private:
...
};
...
list<CatalogueItem> my_list;
// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));
my_list.push_back(items[0]);
my_list.push_back(items[1]);
my_list.sort();
The part I'm trying out is using the operator < to allow the list to sort itsself.
This all seems good, but http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Operator_Overloading seems to suggest avoiding doing this, which is exactly what the book says to do! ("In particular, do not overload operator== or operator< just so that your class can be used as a key in an STL container; instead, you should create equality and comparison functor types when declaring the container.")
I understand "create equality and comparison functor types" to mean creating comparison functions, like the below one:
bool my_comparison_function(const CatalogueItem &a, const CatalogueItem &b)
{
// my comparison code here
}
Is that what the style guide is referring to?
Does anyone have an option as to which method is more "correct"?
J
A functor type would be more like this:
struct CatalogueItemLessThan
{
bool operator()(const CatalogueItem &a, const CatalogueItem &b)
{
}
};
Then the usage would look like this:
list<CatalogueItem> my_list;
// this is just me playing around
CatalogueItem items[2];
items[0] = CatalogueItem(4, string("box"), string("it's a box"));
items[1] = CatalogueItem(3, string("cat"), string("it's a cat"));
my_list.push_back(items[0]);
my_list.push_back(items[1]);
my_list.sort(CatalogueItemLessThan());
The main advantage of this, is that is allows you to decouple sorting from the object itself. You can now provide as many types of sorting as you want, and use them in different places. (For example, string can be sorted in lexical order, or case-insensitively, or "naturally".
The advantage of using a functor as opposed to a loose function is that you can pass parameters into the comparison to modify how the functor should behave.
In general, the Google style-guide is not really the best style guide out there (IMHO especially their taking exception to exceptions, but that's another discussion). If an object has an obvious sorting order, I often add in a default operator<. If later, there are extra sort orders I want to add, then I add in loose functions. If at a later time, I need to add parameters to the sort order, then I make them into functors. There's no sense in adding in complexity before it's needed.
What Google is trying to say to you is the following.
As you know, you can overload one and only one operator '<' for a given type. Let's say it works for you. But imagine that in the future you might need to sort objects of the same type in accordance with some other comparison criterion. How are you going to do that? The only available version of '<' is already taken.
Of course, you can do that by writing a new named comparison function/functor (not the '<' operator) and explicitly supplying it to the sorting algorithm. You can write 2, 5, 10 more of them. You can write as many as you want. It will work. However, at that point there will be an obvious asymmetry in your code. One comparison function is implemented as 'operator <'. The others - as different named functions/functors. Is there a good reason for this asymmetry?
Well, there might be. If you have a very well-defined and obvious natural sorting method that applies to your type, it makes a very good sense to implement it as operator '<'. This would be the main comparison method. And other, auxiliary, less "natural" comparison methods can and should be implemented as named functions. This is prefectly fine.
However, what if you don't have such an obvious candidate for the "natural" comparison? In this case favoring one method over the other and "wasting" the '<' operator on an arbitrarily chosen one is not a good idea. In this case it is recommended to leave the '<' alone, and stick to named functions/functors instead.
In other words, by overloading the '<' you create a "favorite" comparison for the given type. If that's what you really want - go ahead and do it. But keep in mind that in many cases creating an artificial and arbitrary "favorite" is not a good idea. Don't rush the process of choosing that favorite. Don't take the '<' too early.
A functor type is a C++ type (class or struct), that overloads the () operator so that instances of the type behave like a function. This is similar to a class implementing __call__() in Python.
Some STL collection types like std::map require a key_compare functor to order the keys in interal tree structures and thus providing fast access times. By default, this is std::less, which uses operator< to compare values. Therefore this operator is often provided to allow custom classes to act as keys in std::map (and similar).
Google obviously discourages this in favor of supplying your own comparison functor. So, instead of implementing operator<, you could do the following:
struct my_compare
{
bool operator ()(const CatalogueItem& lhs, const CatalogueItem& rhs)
{
...
}
};
If you must access private members to implement this, declare the functor as a friend of your class.
I have a std::multimap where key is a custom class. Something like this:
Class X {
public:
std::string s;
int x;
operator <(const X& other) const { return s < other.s; }
};
std::multimap<X, int> mymap;
Now, I'd like to use upper_bound and lower_bound to iterate over all elements with the same value of "s". Do I need to implement some other operator for X (for example: ==). Or it will work properly just like this?
Also, what should I supply as argument for upper_bound and lower_bound? I assume I should create a dummy object with desired value of "s"?
Since class X is the key for the multimap, the parameter to upper_bound()/lower_bound() needs to be of that type. If class X has an implicit conversion from std::string (which is the type of X::s) then you can use that as the parameter to upper_bound()/lower_bound().
The default comparison for multimap is less<> which simply calls operator <() - so that's the only operator you a required to have in class X for the multimap to work.
you only need to provide an operator == and <.
upper_bound and lower_bound are just like any other find-type method, so you need the same kind of object to compare with - in your case, a 'dummy' object with the required value of s.
edit: the comments are correct that you only need operator< for lower/upper_bound, and find. But if you want to call other methods on your container, you will need operator== as well. Eg. if you want to sort() your container, you will need operator==.
The 2 overloads you need for all STL containers are operator< and operator==. I find its best practise to implement them both.
Of course, the question could also be answered more fully by implementing a comparison functor in the map itself, not relying on the objects. This is often a good way to implement different ways of calling find() on the map.