Se pueden crear operadores virtual
es de forma
análoga a otras funciones miembro. Sin embargo implementar
operadores virtual
es se vuelve a menudo confuso
porque se está operando sobre dos objetos, ambos sin tipos
conocidos. Esto suele ser el caso de los componentes matemáticos
(para los cuales se suele usar la sobrecarga de operadores). Por
ejemplo, considere un sistema que usa matrices, vectores y valores
escalares, todos ellos heredados de la clase
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; } } ///:~
Listado 15.18. C15/OperatorPolymorphism.cpp
Para simplificar sólo se ha sobrecargado el
operator*
. El objetivo es ser capaz de
multiplicar dos objetos Math
cualquiera y
producir el resultado deseado - hay que darse cuenta que
multiplicar una matriz por un vector es una operación totalmente
distinta a la de multiplicar un vector por una matriz.
El problema es que, en el main()
, la
expresión m1 * m2
contiene dos referencias
Math
, y son dos objetos de tipo
desconocido. Una función virtual es sólo capaz de hacer una única
llamada - es decir, determinar el tipo de un único objeto. Para
determinar ambos tipos en este ejemplo se usa una técnica conocida
como despachado múltiple (multiple
dispatching), donde lo que parece ser una única
llamada a una función virtual se convierte en una segunda llamada
a una función virtual. Cuando la segunda llamada se ha ejecutado,
ya se han determinado ambos tipos de objetos y se puede ejecutar
la actividad de forma correcta. En un principio no es
transparante, pero después de un rato mirando el código empieza a
cobrar sentido. Esta materia es tratada con más profundidad en el
capítulo de los patrones de diseño en el Volumen 2 que se puede
bajar de >www.BruceEckel.com.