|
|
|
|
Operator overloading
You can make operators virtual
just like other member functions. Implementing virtual operators often
becomes confusing, however, because you may be operating on two objects, both
with unknown types. This is usually the case with mathematical components (for
which you often overload operators). For example, consider a system that deals
with matrices, vectors and scalar values, all three of which are derived from
class Math:
//: C15:OperatorPolymorphism.cpp
// Polymorphism with overloaded operators
#include <iostream>
using namespace std;
class Matrix;
class Scalar;
class Vector;
class Math {
public:
virtual Math& operator*(Math& rv) = 0;
virtual Math& multiply(Matrix*) = 0;
virtual Math& multiply(Scalar*) = 0;
virtual Math& multiply(Vector*) = 0;
virtual ~Math() {}
};
class Matrix : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Matrix" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Matrix" << endl;
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Matrix" << endl;
return *this;
}
};
class Scalar : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Scalar" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Scalar" << endl;
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Scalar" << endl;
return *this;
}
};
class Vector : public Math {
public:
Math& operator*(Math& rv) {
return rv.multiply(this); // 2nd dispatch
}
Math& multiply(Matrix*) {
cout << "Matrix * Vector" << endl;
return *this;
}
Math& multiply(Scalar*) {
cout << "Scalar * Vector" << endl;
return *this;
}
Math& multiply(Vector*) {
cout << "Vector * Vector" << endl;
return *this;
}
};
int main() {
Matrix m; Vector v; Scalar s;
Math* math[] = { &m, &v, &s };
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++) {
Math& m1 = *math[i];
Math& m2 = *math[j];
m1 * m2;
}
} ///:~
For simplicity, only the operator*
has been overloaded. The goal is to be able to multiply any two Math
objects and produce the desired result – and note that multiplying a
matrix by a vector is a very different operation than multiplying a vector by a
matrix.
The problem is that, in
main( ), the expression m1 * m2 contains two upcast
Math references, and thus two objects of unknown type. A virtual function
is only capable of making a single dispatch – that is, determining the
type of one unknown object. To determine both types a technique called
multiple dispatching is used in this example, whereby what appears to be
a single virtual function call results in a second virtual call. By the time
this second call is made, you’ve determined both types of object, and can
perform the proper activity. It’s not transparent at first, but if you
stare at the example for awhile it should begin to make sense. This topic is
explored in more depth in the Design Patterns chapter in Volume 2, which you can
download at www.BruceEckel.com.
|
|
|