14.3. Arithmetic and Relational OperatorsOrdinarily, we define the arithmetic and relational operators as nonmember functions, as we do here with our Sales_item addition operator: // assumes that both objects refer to the same isbn Sales_item operator+(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); // copy lhs into a local object that we'll return ret += rhs; // add in the contents of rhs return ret; // return ret by value }
The addition operator doesn't change the state of either operand; the operands are references to const objects. Instead, it generates and returns a new Sales_item object, which is initialized as a copy of lhs. We use the Sales_item compound-assignment operator to add in the value of rhs.
An arithmetic operator usually generates a new value that is the result of a computation on its two operands. That value is distinct from either operand and is calculated in a local variable. It would be a run-time error to return a reference to that variable.
It is simpler and more efficient to implement the arithmetic operator (e.g., +) in terms of the compound-assignment operator (e.g., +=) rather than the other way around. As an example, consider our Sales_item operators. If we implemented += by calling +, then += would needlessly create and destroy a temporary to hold the result from +. 14.3.1. Equality OperatorsOrdinarily, classes in C++ use the equality operator to mean that the objects are equivalent. That is, they usually compare every data member and treat two objects as equal if and only if all corresponding members are the same. In line with this design philosophy, our Sales_item equality operator should compare the isbn as well as the sales figures: inline bool operator==(const Sales_item &lhs, const Sales_item &rhs) { // must be made a friend of Sales_item return lhs.units_sold == rhs.units_sold && lhs.revenue == rhs.revenue && lhs.same_isbn(rhs); } inline bool operator!=(const Sales_item &lhs, const Sales_item &rhs) { return !(lhs == rhs); // != defined in terms of operator== } The definition of these functions is trivial. More important are the design principles that these functions embody:
14.3.2. Relational OperatorsClasses for which the equality operator is defined also often have relational operators. In particular, because the associative containers and some of the algorithms use the less-than operator, it can be quite useful to define an operator<. Although we might think our Sales_item class should support the relational operators, it turns out that it probably should not. The reasons are somewhat subtle and deserve understanding. As we'll see in Chapter 15, we might want to use an associative container to hold Sales_item transactions. When we put objects into the container, we'd want them ordered by ISBN, and wouldn't care whether the sales data in two records were different. However, if we were to define operator< as comparison on isbn, that definition would be incompatible with the obvious definition of ==. If we had two transactions for the same ISBN, neither record would be less than the other. Yet, if the sales figures in those objects were different, then these objects would be !=. Ordinarily, if we have two objects, neither of which is less than the other, then we expect that those objects are equal. Because the logical definition of < is inconsistent with the logical definition of ==, it is better not to define < at all. We'll see in Chapter 15 how to use a separate named function to compare Sales_items when we want to store them in an associative container.
|