Virtual Functions und Polymorphismus

Überschreiben von Funktionen, wann wird Funktion aus Basis-Klasse und wann aus abgeleiteter Klasse gerufen ?



int main()
{
  My3Vector tvec(1.4, 0.7, 0.6);
  MyLVector lvec( 2.1, 1., 1., 0.5);
  My3Vector *pv;  // pointer to three-vector
  MyLVector *pl;  // pointer to lorentz-vector

  cout << tvec.Length() << endl; // Length() aus My3Vector wird benutzt
  cout << lvec.Length() << endl; // Length() aus MyLVector wird benutzt

  pv = &tvec;
  cout << pv->Length() << endl; // Length() aus My3Vector wird benutzt

  pl = &lvec;
  cout << pl->Length() << endl; // Length() aus MyLVector wird benutzt

  pv = &lvec;  // legal, Basisklassen--Pointer kann auf abgeleitetes Objekt zeigen
  cout << pv->Length() << endl; // welches Length() wird gerufen ???
}


Abhängig von Deklaration  der Funktion in Basisklasse:


Wozu virtual Functions ?

Vererbung ist vor allem dann sehr sinnvoll wenn von einer allgemeinen Basisklasse mehrere Klassen ableiten.

Image shapes

Beispiel Grafik: Die grafischen Objekte Rectangle, Oval, RoundRectangle, haben bestimmte Eigenschaften und Funktionen gemeinsam, z.B. die Farbe, Position, etc.
\bgroup\color{green}\ensuremath{\Rightarrow}\egroup abgeleitet von Basisklasse Shape.

Andere Funktionen, wie z.B. zeichnen  sind spezifisch für jede Klasse und müssen spezifisch implementiert werden.



class Shape {
  //..
  Color color;   // Color of the shape.
  void setColor(Color newColor) {
    // Method to change the color of the shape.
    color = newColor; // change value of instance variable
  }
  virtual void Draw() { ...} // declare as virtual
  //..
};
class Rectangle : public Shape {
     void Draw() {
       drawRect . . .  // commands for drawing a rectangle
     }
     . . . // possibly, more methods and variables
}
class Oval  : public Shape {
     void Draw() {
       drawEllipse. . .
     }
}
class RoundRect : public Shape {
     void Draw() {
       drawRoundRect . . .
     }
}



Dann generische Verwendung als shape möglich:



int main ()
{
  vector<Shape*> vecshape;  // vector with base-class pointers
  // add some concrete shapes
  vecshape.push_back( new Rectangle());
  vecshape.push_back( new Oval());
  vecshape.push_back( new Rectangle());
  vecshape.push_back( new RoundRect());
  vecshape.push_back( new Oval());
  vecshape.push_back( new Oval());

  // make the shapes all red, move and draw them 
  for ( int i=0; i<vecshape.size(); i++ ){
    vecshape[i]->setColor( red ); // Shape-func
    vecshape[i]->Move( ... );     // Shape-func
    vecshape[i]->Draw( ... );     // Oval/Rectangle/.. -func
  }
  //...
}



\bgroup\color{green}\ensuremath{\Rightarrow}\egroup Polymorphismus:
Unterschiedliche Objekte mit gemeinsamer Basisklasse können über diese Basisklasse angesprochen werden, d.h. es genügt ein Pointer vom Typ der Basisklasse. Funktionen, die als virtual deklariert sind, werden automatisch dem jeweiligen Objekt zugeordnet. Diese Zuordnung findet erst zur Laufzeit statt (late-binding)  über die virtual function table.



// ...
void gimeSomeShapes( vector<Shape*> &vs, int nshapes )
{
    // creat concrete shapes randomly and add to vector
  for ( int i=0; i<nshapes; i++) {
    int irand = rand()*3./(RAND_MAX+1.0); // Zufallszahlen [0..3)
    switch (irand ) {
    case 0:
      vs.push_back( new Rectangle());
      break;
    case 1:
      vs.push_back( new Oval());
      break;
    case 2:
      vs.push_back( new RoundRect());
      break;
    }
  }
}
int main ()
{
  vector<Shape*> vecshape;  // vector with base-class pointers
  gimeSomeShapes( vecshape, 20 ); // erzeuge shapes
  // make the shapes all red, move and draw them 
  for ( int i=0; i<vecshape.size(); i++ ){
    vecshape[i]->setColor( red ); // Shape-func
    vecshape[i]->Move( ... );     // Shape-func
    // Oval/Rectangle/.. -func, richtige wird automatisch gesucht
    vecshape[i]->Draw( ... );     
  }
  //...
}


Äusserst nützliches Konzept:

Leichte Erweiterbarkeit: man kann später weitere Klassen ( Triangle, Pentagon, ...) zufügen ohne nutzende Funktionen oder Shape ändern zu müssen.

OOP: ``old code calls new code''    versus    prozedural: ``new code calls old code''


GDuckeck 2019-08-01