Rational numbers with operator overloading ========================================== This example extends the previous rational-number project by introducing operator overloading. The Rational class and most of the underlying rational-number operations are essentially unchanged. Refer to the previous project for details about rational arithmetic, comparisons, multiplicative inverses, absolute values, const references, and the organization of the class into header and source files. The focus of this project is on allowing familiar C++ operators to work with Rational objects. Operator overloading ==================== In the previous project, operations on rational numbers were performed using ordinary functions. For example, add( p, q ) sub( p, q ) mul( p, q ) equal( p, q ) With operator overloading, these operations can instead be written as p + q p - q p*q p == q For example, when the compiler encounters p + q for two Rational objects, it can use the overloaded function operator+( p, q ) Thus, operator overloading allows operations on objects to be written using notation that more closely resembles the corresponding mathematical notation. Operator overloading does not create new operators, nor does it change the precedence or associativity of existing operators. It defines what an existing operator does when applied to particular types. Arithmetic operators ==================== This project overloads the four basic arithmetic operators: p + q p - q p*q p/q where p and q are Rational objects. Unlike the previous 3-d vector example, these operators are also overloaded to allow an integer to appear as either operand: p + 2 2 + p p - 2 2 - p p*2 2*p p/2 2/p These require different overloads because the types of the arguments appear in different orders. For addition and multiplication, reversing the operands does not change the mathematical result. For subtraction and division, however, the order of the operands is significant: p - 2 and 2 - p represent different rational numbers, as do p/2 and 2/p Consequently, the appropriate overload must preserve the order of the operands. Unary and binary operators ========================== The + and - operators can be either unary or binary. The expressions +p -p each have one operand and therefore use the unary versions of operator+ and operator-. The expressions p + q p - q each have two operands and therefore use the binary versions. Unary + returns the rational number unchanged, while unary - returns its additive inverse. These are examples of function overloading: functions can have the same identifier provided they have different parameter lists. Reusing overloaded operators ============================ Once fundamental operators have been defined, other operations can often be implemented using them. For example, this project uses expressions such as p + (-q) p*inverse( q ) !(p == q) (p < q) || (p == q) !(p < q) !(p <= q) to implement other arithmetic and comparison operators. The overloads involving integers can also convert an integer n into Rational{ n, 1 } and then reuse an operation already defined for two Rational objects. This reduces duplicated code and makes the mathematical relationships between the operations clearer. Comparison operators ==================== The six comparison operators are overloaded: p == q p != q p < q p <= q p >= q p > q Each is also overloaded to allow an integer to appear as either operand. The comparisons operate on the values of the rational numbers rather than simply comparing their stored numerators and denominators. For example, Rational p{ 3, 4 }; Rational q{ 6, 8 }; satisfy p == q even though the two objects store different numerators and denominators. The fundamental comparisons are implemented using the arithmetic properties of rational numbers. Other comparison operators are then defined using operators that have already been implemented. Operations that remain ordinary functions ========================================= Not every operation needs an operator. The functions inverse( p ) abs( p ) real( p ) to_string( p ) remain ordinary functions. For example, C++ does not have an existing operator that naturally means "take the multiplicative inverse," so inverse(...) remains a named function. Operator overloading should make code clearer and more natural; it should not assign