Beispiel: ein Kreis hat einen Mittelpunkt, aber ein Kreis ist eine Flaeche.
Eigentlich ist eine Fläche ein abstrakter Begriff, er bekommt erst eine konkrete Bedeutung, wenn die Geometrie festliegt. Trotzdem kann eine Fläche bestimmte Eigenschaften (Schnittstellen nach außen) haben, die unabhängig von der zugrundeliegenden Geometrie für alle Flächen vorhanden sind.
Eine Fläche hat z.B. einen Flächeninhalt und eine Schwerpunkt.
Gehen wir Schritt für Schritt vor:
1. Deklaration eines Kreises:
class CCircle { private: CVector center; double radius; public: CCircle(double radius=1.0); // (default) constructor CCircle(const CVector& center, double radius=1.0); CCircle(const CCircle& circle); virtual ~CCircle(); CCircle& operator=(const CCircle& circle); ... // more functions and operators };
Probleme: damit ist noch nicht gesagt, dass ein Kreis eine Fläche sein soll, und der Mittelpunkt hat noch keine Dimension (default wäre 3, wir wollen aber nur im Zweidimensionalen arbeiten).
1. Deklaration einer Fläche:
class CArea { private: CVector gravity_center; double area; public: CArea(); // constructor virtual ~CArea(); // destructor CArea(const CArea& area); // copy-constructor CArea& operator=(const CArea& area); ... // more functions and operators };
Wie konstruieren wir nun die Fläche, so dass sie einen Schwerpunkt der Dimension 2 hat?
Konstruktor einer Fläche:
CArea::CArea( ) : gravity_center(2) // constructing member-object !! { gravity_center[0]= 0.0; // setting members to default gravity_center[1]= 0.0; // values area =-1.0; // invalid value !! }
Also: der Schwerpunkt wird am Anfang des Konstruktors der Fläche (sozusagen vor der öffnenden geschweiften Klammer) als CVector der Dimension 2 konstruiert.
Weitere Konstruktoren von Member-Objekten würden mit Komma getrennt an die gleiche Stelle geschrieben.
Alle Member-Objekte werden initialisiert, insbesondere, da eine Fläche an sich - ohne Geometrie - keinen Sinn macht, wird der Inhalt negativ initialisiert.
Leiten wir nun den Kreis von der Basisklasse Fläche ab:
2. Deklaration eines Kreises:
class CCircle : public CArea { // circle is an area private: CVector center; // circle has a center double radius; public: CCircle(double radius=1.0); // (default) constructor CCircle(const CVector& center, double radius=1.0); CCircle(const CCircle& circle); virtual ~CCircle(); CCircle& operator=(const CCircle& circle); ... // more functions and operators };
und entwickeln den Konstruktor:
1. Definition eines Kreis-Konstruktors:
CCircle::CCircle( double radius ) : CArea(), // explicit construction of base class center(2) // construction of member-object { center[0]=0.0; center[1]=0.0; CCircle::radius=radius; }
Wir rufen also beim Konstruieren eines Kreises zusätzlich den Konstruktor der Basisklasse auf.
Bemerkung: wird der Konstruktor der Basisklasse nicht explizit aufgerufen, so wird implizit der Standard-Konstruktor der Basisklasse verwendet, was in unserem Beispiel das gleiche wäre.
Problem: das Objekt der Basisklasse ist noch nicht initialisiert, d.h. der Schwerpunkt und der Inhalt sind nicht richtig gesetzt!
Wie kommt die abgeleitete Klasse an die privaten Member-Objekte der Basisklasse heran? Gar nicht!
Wir müssen die Member-Objekte protected deklarieren! Damit sind sie nach außen weiterhin ``unsichtbar'', können jedoch in Member-Funktionen abgeleiteter Klassen verwendet werden.
2. Deklaration einer Fläche:
class CArea { protected: CVector gravity_center; double area; public: CArea(); // constructor virtual ~CArea(); // destructor CArea(const CArea& area); // copy-constructor CArea& operator=(const CArea& area); ... // more functions and operators };
Damit können wir den Konstruktor eines Kreises vervollständigen:
2. Definition eines Kreis-Konstruktors:
CCircle::CCircle( double radius ) : CArea(), // explicit construction of base class center(2) // construction of member-object { center[0]=0.0; center[1]=0.0; CCircle::radius=radius; gravity_center=center; // init of base class area =pi*radius*radius; // members }
Bemerkung: wir könnten pi z.B. als statische Member-Variable eines Kreises deklarieren.
Fügen wir die fehlenden Member-Funktionen ein, können wir damit z.B. folgendes Programm schreiben:
main( ) { CCircle K1(6.0); CVector m(2); m[0]=1.0; m[1]=2.0; CCircle K2(m); cout << K1 << endl; cout << K2 << endl; }
was folgende oder ähnliche Ausgabe erzeugen sollte:
Kreis: Mittelpunkt = (0.0,0.0) Radius = 6.0 Schwerpunkt = (0.0,0.0) Inhalt = 113.097 Kreis: Mittelpunkt = (1.0,2.0) Radius = 1.0 Schwerpunkt = (1.0,2.0) Inhalt = 3.14159
Wir beachten also bei Konstruktoren für abgeleitete Klassen, dass folgende Punkte korrekt abgearbeitet werden: