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:
D.h. wenn ein Pointer vom Typ My3Vector * für ein Objekt gerufen wird, das in Wirklichkeit ein MyLVector ist, dann wird auch die zugehörige Funktion MyLVector::Length() benutzt.
Vererbung ist vor allem dann sehr sinnvoll wenn von einer allgemeinen Basisklasse mehrere Klassen ableiten.
Beispiel Grafik:
Die grafischen Objekte Rectangle, Oval, RoundRectangle, haben bestimmte Eigenschaften und
Funktionen gemeinsam, z.B. die
Farbe, Position, etc.
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 } //... }
// ... 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''