In lesson 5.6 - 关系运算符和浮点数比较 we discussed the six comparison operators. Overloading these comparison operators is comparatively simple (see what I did there?), as they follow the same patterns as we’ve seen in overloading other operators.
Because the comparison operators are all binary operators that do not modify their left operands, we will make our overloaded comparison operators friend functions.
Here’s an example Car class with an overloaded operator== and operator!=.
#include<iostream>#include<string>#include<string_view>classCar{private:std::stringm_make;std::stringm_model;public:Car(std::string_viewmake,std::string_viewmodel):m_make{make},m_model{model}{}friendbooloperator==(constCar&c1,constCar&c2);friendbooloperator!=(constCar&c1,constCar&c2);};booloperator==(constCar&c1,constCar&c2){return(c1.m_make==c2.m_make&&c1.m_model==c2.m_model);}booloperator!=(constCar&c1,constCar&c2){return(c1.m_make!=c2.m_make||c1.m_model!=c2.m_model);}intmain(){Carcorolla{"Toyota","Corolla"};Carcamry{"Toyota","Camry"};if(corolla==camry)std::cout<<"a Corolla and Camry are the same.\n";if(corolla!=camry)std::cout<<"a Corolla and Camry are not the same.\n";return0;}
COPY
The code here should be straightforward.
What about operator< and operator>? What would it mean for a Car to be greater or less than another Car? We typically don’t think about cars this way. Since the results of operator< and operator> would not be immediately intuitive, it may be better to leave these operators undefined.
最佳实践
Only define overloaded operators that make intuitive sense for your class.
However, there is one common exception to the above recommendation. What if we wanted to sort a list of Cars? In such a case, we might want to overload the comparison operators to return the member (or members) you’re most likely to want to sort on. For example, an overloaded operator< for Cars might sort based on make and model alphabetically.
Some of the container classes in the standard library (classes that hold sets of other classes) require an overloaded operator< so they can keep the elements sorted.
Here’s a different example overloading all 6 logical comparison operators:
#include<iostream>classCents{private:intm_cents;public:Cents(intcents):m_cents{cents}{}friendbooloperator==(constCents&c1,constCents&c2);friendbooloperator!=(constCents&c1,constCents&c2);friendbooloperator<(constCents&c1,constCents&c2);friendbooloperator>(constCents&c1,constCents&c2);friendbooloperator<=(constCents&c1,constCents&c2);friendbooloperator>=(constCents&c1,constCents&c2);};booloperator==(constCents&c1,constCents&c2){returnc1.m_cents==c2.m_cents;}booloperator!=(constCents&c1,constCents&c2){returnc1.m_cents!=c2.m_cents;}booloperator<(constCents&c1,constCents&c2){returnc1.m_cents<c2.m_cents;}booloperator>(constCents&c1,constCents&c2){returnc1.m_cents>c2.m_cents;}booloperator<=(constCents&c1,constCents&c2){returnc1.m_cents<=c2.m_cents;}booloperator>=(constCents&c1,constCents&c2){returnc1.m_cents>=c2.m_cents;}intmain(){Centsdime{10};Centsnickel{5};if(nickel>dime)std::cout<<"a nickel is greater than a dime.\n";if(nickel>=dime)std::cout<<"a nickel is greater than or equal to a dime.\n";if(nickel<dime)std::cout<<"a dime is greater than a nickel.\n";if(nickel<=dime)std::cout<<"a dime is greater than or equal to a nickel.\n";if(nickel==dime)std::cout<<"a dime is equal to a nickel.\n";if(nickel!=dime)std::cout<<"a dime is not equal to a nickel.\n";return0;}
COPY
This is also pretty straightforward.
Minimizing comparative redundancy
In the example above, note how similar the implementation of each of the overloaded comparison operators are. Overloaded comparison operators tend to have a high degree of redundancy, and the more complex the implementation, the more redundancy there will be.
Fortunately, many of the comparison operators can be implemented using the other comparison operators:
operator!= can be implemented as !(operator==)
operator> can be implemented as operator< with the order of the parameters flipped
operator>= can be implemented as !(operator<)
operator<= can be implemented as !(operator>)
This means that we only need to implement logic for operator== and operator<, and then the other four comparison operators can be defined in terms of those two! Here’s an updated Cents example illustrating this:
#include<iostream>classCents{private:intm_cents;public:Cents(intcents):m_cents{cents}{}friendbooloperator==(constCents&c1,constCents&c2);friendbooloperator!=(constCents&c1,constCents&c2);friendbooloperator<(constCents&c1,constCents&c2);friendbooloperator>(constCents&c1,constCents&c2);friendbooloperator<=(constCents&c1,constCents&c2);friendbooloperator>=(constCents&c1,constCents&c2);};booloperator==(constCents&c1,constCents&c2){returnc1.m_cents==c2.m_cents;}booloperator!=(constCents&c1,constCents&c2){return!(operator==(c1,c2));}booloperator<(constCents&c1,constCents&c2){returnc1.m_cents<c2.m_cents;}booloperator>(constCents&c1,constCents&c2){returnoperator<(c2,c1);}booloperator<=(constCents&c1,constCents&c2){return!(operator>(c1,c2));}booloperator>=(constCents&c1,constCents&c2){return!(operator<(c1,c2));}intmain(){Centsdime{10};Centsnickel{5};if(nickel>dime)std::cout<<"a nickel is greater than a dime.\n";if(nickel>=dime)std::cout<<"a nickel is greater than or equal to a dime.\n";if(nickel<dime)std::cout<<"a dime is greater than a nickel.\n";if(nickel<=dime)std::cout<<"a dime is greater than or equal to a nickel.\n";if(nickel==dime)std::cout<<"a dime is equal to a nickel.\n";if(nickel!=dime)std::cout<<"a dime is not equal to a nickel.\n";return0;}
COPY
This way, if we ever need to change something, we only need to update operator== and operator< instead of all six comparison operators!