8.5 Type Conversion and Operator Overloading
Type conversions are often provided to lessen the duplication that frequently occurs when overloading operators. A typical example of this duplication is encountered in defining binary, commutative operators (such as + and *). Suppose that an overloaded set of arithmetic operators is needed in class X so that an int may be added to an object of class X. This requires two operator overloadings: one for the case of "X + int" and one for the case of "int + X." To eliminate this duplication, a type conversion is defined that converts an int to an X and provides a single overloading for the case "X + X." Thus, both "int + X" and "X + int" become the single case "X + X".
A set of classes for manipulating colors will be used in this section to illustrate type conversion and operator overloading. Colors generated by a computer are a combination of three primary colors (red, blue, and green). Each of these colors has an intensity value associated with it. The range of intensity values determines how many different colors may be produced. If we assume that the intensities are in the range 0-63, then the color aqua is generated by using no red and intensities of 50 for both green and blue. The color purple is obtained from aqua by adding red at intensity 63 and increasing the blue intensity by 13.
Operator overloading makes it possible to program with colors in the most natural way. The phrase "add red at intensity nine to aqua" suggests using operator overloading to create a set of arithmetic operators that could be used as follows:
Color aqua(Red(0), Green(50), Blue(50)); Color darkRed = Red(63); Color purple = darkRed + aqua + Blue(13); Color peach = purple - Blue(13) - Green(3); Color test1 = darkRed + peach; Color test2 = peach + darkRed; cout << aqua << endl; cout << purple << endl; cout << peach << endl; cout << darkRed << endl;
This usage reads naturally and the stream output generated is a readable description of the color. For example, outputting the color aqua results in:
(red:0, green:50, blue:50)
Defining classes for the primary colors is staightforward. Because each of these colors has a common structure they can be defined via inheritance from the base class that is shown in the figure below.
The base class, PrimaryColor, defines a single data value representing
the color's intensity value. In addition to the two constructors,
a stream output operator is defined as a non-member friend function.
The friend permission is used in the stream output operator to
access the intensity data in the protected region of the PrimaryColor
object. The first constructor of the PrimaryColor class can be
viewed as a way to construct a PrimaryColor object using an int
value or as a type conversion operator that converts an int value
into a PrimaryColor object.
The individual classes Red, Green, and Blue can be derived from PrimaryColor; the next figure presents the Red class in detail; the Green and Blue classes are similar. Operator overloadings for the addition and subtraction operators are given for each of the Red, Green, and Blue classes. The overloadings allow the normal arithmetic operators to be used to add and substract colors in the same way that integer values are added and subtracted. For example, the code:
Red lightRed(20); Red darkerRed(100); Red darkestRed = lightRed + darkerRed; Red lessRed = darkestRed - Red(30);
shows how Red objects can be declared and manipulated using these overloaded operators.
In addition to providing greater readability, the introduction of the simple classes for Red, Green, and Blue also helps to avoid certain errors that would occur were only basic int types used instead. For example, if a programmer unintentionally wrote the following code using only integer values:
int color = 10; // meant to be red int shade = 20; // meant to be red int hue = 20; // meant to be green ... int shade = color + hue;
the addition of a red integer and a green integer cannot be detected by a mistake by the compiler although it represents a programming mistake. The compiler is unable to help because the only type involved is the single type int. However, by introducing the Red, Green, and Blue classes this code becomes:
Red color(10); // meant to be red Red shade(20); // meant to be red Green hue(20); // meant to be green ... shade = color + hue; // compile-time error
and the compiler is able to detect the unintended mixing of the two color values because of the extra types introduced by the Red, Green, and Blue classes.
The more general Color class can now be defined in terms of the
three PrimaryColor classes. Each object of the Color class has
encapsulated values that represent that color's combination of
red, green, and blue intensities. Ignoring the possibility of
type conversions, a first attempt at defining the operator overloading
for the Color class is shown in the figure below.
A cleaner solution can be obtained by using type conversion to
elevate a PrimaryColor object to a Color object before applying
the overloaded addition or subtraction operator, which leads to
the class defined in the following figure. In this class, the
three constructors with a single argument play the role of type
conversion operators because they produce an object of one class,
a Color object, from an object of a different class, Red, Green,
The compiler utilizes the type conversion implicitly when it encounters an expression involving objects that are not of the same class. For example, in the code
Color hue(Red(50), Green(50), Blue(50); Color shade = Red(10) + hue;
the expression "Red(10) + hue" involves an operation between an object of the Red class and an object of the Color class. No operator overloading is immediately recognized for this case. However, there is available the overloaded addition operator in the Color class that provides a way of adding together two objects of that class. Furthermore, there is a type conversion that defines how to convert the Red object to a Color object. Thus, the last statement above would be interpreted as if the programmer had written
Color shade = Color(Red(10)) + hue;
For clarity, the programmer may prefer to write the code with the explicit type conversion operator. However, the compiler will supply the type conversion implicitly if such is required.
In summary, the use of type conversion operators, overloaded operators, and implicit type conversion by the compiler allows a compact set of operator overloadings to be written that provide for flexible ways of operating on the value of different classes.