Overloaded Operators

Overloaded Operators

We can overload many of the C++ operators to work with programmer-defined types. Table 15.1 shows some operators that have been defined to work with several of the standard object classes. We can define how specific operators work for the types we devise.

Operator Functions

class Point {
public:
    double x;
    double y;
};
Point operator + (const Point & p1,
    const Point & p2) {
    Point result;
    result.x = p1.x + p2.x;
    result.y = p1.y + p2.y;
    return result;
}
class Point {
    double x; // Fields are now private
    double y;
    public:
        // Add a constructor
        Point(double x, double y);
    // Add some methods to access the private data members
    double get_x() const;
    double get_y() const;
};
// Initialize fields
Point::Point(double x, double y): x(x), y(y) {}
double Point::get_x() const {
    return x; // Return a copy of the x member
}
double Point::get_y() const {
    return y; // Return a copy of the y member
}

It often is convenient to overload the output stream << operator for custom classes. that allow us to print the primitive types like integers and floating-point numbers.

std::ostream& operator<<(std::ostream& os, const X& x
operator<<(std::cout, x)
operator<<(operator<<(operator<<(std::cout, x), y), '\n');

Operator Methods

A class may define operator methods. A method for a unary operator accepts no parameters, and a method for a binary operator accepts only one parameter. The “missing” parameter is the object upon which the operator is applied;

class EnhancedRational {
    int numerator;
    int denominator;
    public:
        /* Other details omitted */
        // Unary plus returns the double-precision floating-point
        // equivalent of the rational number.
        double operator + () const {
            return static_cast < double > (numerator) / denominator;
        }
    // Binary plus returns the rational number that results
    // from adding another rational number (the parameter) to
    // this object.
    Rational operator + (const Rational & other) const {
        int den = denominator * other.denominator,
            num = numerator * other.denominator +
            other.numerator * denominator;
        return {
            num,
            den
        };
    }
};
Rational fract1(1, 2), fract2(1, 3), fract3;
double value = +fract1; // Assigns 0.5
fract3 = fract1 + fract2; // fract3 is 5/6

Static Members

class Widget {
    int value;
    static int quantity;
};

One unusual thing about static fields is that must be defined outside of the class declaration. For the Widget class above, we must supply the statement

#include <iostream>

class Widget {
    // All Widget objects share serial_number_source
    static int serial_number_source;
    // Each Widget object is supposed to have its own
    // unique serial_number
    int serial_number;
    public:
        // A Widget object's serial number is initialized from
        // the shared serial_number_source variable which is
        // incremented each time a Widget object is created
        Widget(): serial_number(serial_number_source++) {}
    // Client's can look at the serial number but not touch
    int get_serial_number() const {
        return serial_number;
    }
};
// Initialize the initial serial number; the first
// Widget made will have serial number 1
int Widget::serial_number_source = 1;
// Make some widgets and check their serial numbers
int main() {
    Widget w1, w2, w3, w4;
    std::cout << "w1 serial number = " << w1.get_serial_number() << '\n';
    std::cout << "w2 serial number = " << w2.get_serial_number() << '\n';
    std::cout << "w3 serial number = " << w3.get_serial_number() << '\n';
    std::cout << "w4 serial number = " << w4.get_serial_number() << '\n';
}
w1 serial number = 1
w2 serial number = 2
w3 serial number = 3
w4 serial number = 4
#ifndef TRAFFICSIGNAL_H_
# define TRAFFICSIGNAL_H_
class TrafficSignal {
    int color; // The light's current color: RED, GREEN, or YELLOW
    public:
        // Class constants available to clients
        static
    const int RED = 0;
    static
    const int GREEN = 1;
    static
    const int YELLOW = 2;
    TrafficSignal(int initial_color);
    void change();
    int get_color() const;
};
#endif

class, the Traffic Signal Alt class has no default constructor. Recall from that a default constructor accepts no arguments, and that if a programmer provides any constructor for a class, the compiler will not automatically provide a default constructor.

Within the methods of the TrafficSignal class we can use the simple names RED, GREEN, and YELLOW. Code outside of the TrafficSignal class can access the color constants because they are public but must use the fully-qualified names TrafficSignal::RED, TrafficSignal::GREEN, and TrafficSignal::YELLOW.

Since the client code is outside the TrafficSignal class it must must use the full TrafficSignal::YELLOW name. This statement makes an initially yellow traffic light. Since the RED, GREEN, and YELLOW public fields are constants, clients cannot modify them to subvert the behavior of a traffic light object.

class, the class has no default constructor. Recall from Section 14.4 that a default constructor accepts no arguments, and that if a programmer provides any constructor for a class, the compiler will not automatically provide a default constructor.