In visual terms, multiplying a vector by a number makes the vector longer or shorter by that factor. So multiplying a vector by 3 produces a vector with three times the length, but still pointed in the same direction. It’s easy to translate that image into how the Vector class represents a vector. In polar terms, you multiply the magnitude and leave the angle alone. In rectangular terms, you multiply a vector by a number by multiplying its x and y components separately by the number. That is, if a vector has components of 5 and 12, multiplying by 3 makes the components 15 and 36. And that is what the overloaded multiplication operator does:
Vector Vector::operator*(double n) const
{
return Vector(n * x, n * y);
}
As with overloaded addition, the code lets a constructor create the correct Vector object from the new x and y components. This handles multiplying a Vector value by a double value. Just as in the Time example, you can use an inline friend function to handle double times Vector:
Vector operator*(double n, const Vector & a) // friend function
{
return a * n; // convert double times Vector to Vector times double
}
More Refinement: Overloading an Overloaded Operator
In ordinary C++, the - operator already has two meanings. First, when used with two operands, it’s the subtraction operator. The subtraction operator is termed a
To subtract Vector B from Vector A, you simply subtract components, so the definition for overloading subtraction is quite similar to the one for addition:
Vector operator-(const Vector & b) const; // prototype
Vector Vector::operator-(const Vector & b) const // definition
{
return Vector(x - b.x, y - b.y); // return the constructed Vector
}
Here, it’s important to get the correct order. Consider the following statement:
diff = v1 - v2;
It’s converted to a member function call:
diff = v1.operator-(v2);
This means the vector that is passed as the explicit argument is subtracted from the implicit vector argument, so you should use x - b.x and not b.x - x.
Next, consider the unary minus operator, which takes just one operand. Applying this operator to a regular number, as in -x, changes the sign of the value. Thus, applying this operator to a vector reverses the sign of each component. More precisely, the function should return a new vector that is the reverse of the original. (In polar terms, negation leaves the magnitude unchanged but reverses the direction. Many politicians with little or no mathematical training nonetheless have an intuitive mastery of this operation.) Here are the prototype and definition for overloading negation:
Vector operator-() const;
Vector Vector::operator-() const
{
return Vector (-x, -y);
}
Note that now there are two separate definitions for operator-(). That’s fine because the two definitions have different signatures. You can define both binary and unary versions of the - operator because C++ provides both binary and unary versions of that operator to begin with. An operator that has only a binary form, such as division (/), can only be overloaded as a binary operator.
Note
Because operator overloading is implemented with functions, you can overload the same operator many times, as long as each operator function has a distinct signature and as long as each operator function has the same number of operands as the corresponding built-in C++ operator.
An Implementation Comment
The implementation described in the preceding sections stores both rectangular and polar coordinates for a vector in the Vector object. However, the public interface doesn’t depend on this fact. All the interface calls for is that both representations can be displayed and that individual values can be returned. The internal implementation could be quite different. As mentioned earlier, the object could store just the x and y components. Then, say, the magval() method, which returns the value of the magnitude of the vector, could calculate the magnitude from the x and y values instead of just looking up the value as stored in the object. Such an approach changes the implementation but leaves the user interface unchanged. This separation of interface from implementation is one of the goals of OOP. It lets you fine-tune an implementation without changing the code in programs that use the class.