C++ Vererbung und Polymorphie Vortrag von Anke Schulz 27.7.2000 Vererbung Die Vererbung ermöglicht es neue Klassen auf der Basis von schon bestehenden Klassen zu definieren. Wie sinnvoll und praktisch das ist haben wir schon bei Java gesehen. Im Unterschied zu Java ist es bei C++ möglich eine neue klasse von mehreren Basisklassen abzuleiten, die dann die Eigenschaften aller dieser Basisklasssen besitzt. Und natürlich all die Eigenschaften, die man neu dazu definiert. Syntax class klassenname : [virtual][public, protectet, private] Basisklassenname1, [virtual][public, protectet, private] Basisklassenname2 { Liste der klassenelemente }; Die Zugriffsspezifizierer public, protectet und private regeln bei der Vererbung nicht den Zugriff aus den Methoden der abgeleiteten Klassen auf die geerbten Elemente, dies wird ja schon in der Deklaration in der Basisklasse geregelt und gilt hier also immernoch, sondern welche Zugriffsrechte die abgeleitete Klasse für die geerbten Elemente nach außen weiter gibt. Z. B. wenn über ein Objekt der abgeleiteten Klasse auf diese Elemente zugegriffen wird. Virtual ist nur bei Mehrfachvererbung interessant. Zugriffsbeschränkung Ausschlaggebend sind also die Zugriffsrechte, die in der Basisklasse vorgesehen waren, diese können dann in der abgeleiteten Klasse durch die Zugriffsspezifizierer dort nur verschärft werden. Zugriffsspez. der Vererbung public protectet private Basisklasse public protectet private public protectet private private private abgeleitete Klasse public protectet private protectet protectet private private private Beispiel class Basis { int w_priv; //private public: int w_pub; }; class Abgeleitet :private Basis { public: void fkt() { w_priv = 2; // kein Zugriff, private w_pub = 2; // ok } }; int main() { class Abgeleitet obj; cout<<obj.w_pub<<endl; //kein Zugriff, private- Vererbung return 0; } Zugriffsrecht lockern Zugriffsbeschränkungen durch die Vererbung können in der abgeleiteten Klasse explizit wieder gelockert werden. Eine geerbte Eigenschaft kann aber keine weitreichendere Zugänglichkeit zugewiesen werden, als sie in der Basisklasse besitzt. Beispiel class Basis { int public: int int int }; w_priv; w_pub1; w_pub2; w_pub3; //private Fortsetzung des Beispiels class Abgeleitet :private Basis { public: Basis::w_pub1; //ist wieder public using Basis::w_pub2; //ist wieder public }; int main() { class Abgeleitet obj; cout<<obj.w_pub1<<endl; cout<<obj.w_pub2<<endl; cout<<obj.w_pub3<<endl; cout<<obj.w_priv<<endl; return 0; } //ok //ok //kein Zugriff //kein Zugriff Vererbung - Einbettung Um in einer Klasse die Eigenschaften einer anderen nutzen zu können gibt es neben der Vererbung und der friend- Deklaration(siehe Vortrag von Marc) die Möglichkeit der Einbettung. Die Entscheidung ob Vererbung oder Einbettung sinnvoller ist hängt von der Beziehung ab, in der die Objekte zueinander stehen. Kann man ein Objekt der Klasse Y auch als Objekt der Klasse X betrachten, dann handelt es sich um Vererbung. Oder gehört zu einem Objekt der Klasse X auch ein Objekt der Klasse Y, dann ist Einbettung sinnvoll. Vererbung - Einbettung Vererbung class X { ... }; class Y : public class X { ... }; Einbettung class X { ... }; class Y { class X var; ... }; Vererbung hat den Vorteil des Polymorphismus, der Zugriffsregelung und die Möglichkeit Instanzen der abgeleiteten Klasse wie Objekte der Basisklasse zu verwenden. Die Verwendung von Elementobjekten ist dagegen einfacher und überschaubarer. Konstruktor Konstruktoren und Destruktoren werden nicht vererbt. Darum müssen bei der Objektbildung einer abgeleiteten Klasse die ererbten Datenelemente vom Konstruktor der jeweiligen Basisklasse erstellt werden. Die von einer Basisklasse geerbten Elemente bilden in der abgeleiteten Klasse eine Art Unterobjekt mit eigenem Konstruktoren, Destruktoren, Zugriffsrechten usw. Standardkonstruktor Ist in der Basisklasse der Standardkonstruktor vorhanden, so braucht sich die abgeleitete Klasse nicht um die Initialisierung der geerbten Elemente zu kümmern. Der Compiler ruft automatisch den Konstruktor der abgeleiteten Klasse auf, wenn er ein Objekt der abgeleiteten Klasse erstellt. Konstruktor Spezieller Konstruktor Mehrfachvererbung Wird eine Klasse von mehreren Klassen abgeleitet, werden deren Konstruktoren in der Reihenfolge, in der sie aufgelistet sind abgearbeitet. Zuletzt wird der Anweisungsteil des Konstruktors der abgeleiteten Klasse ausgeführt. class B1 {...}; class B2 {...}; class A : public B1, public B2 {...}; Indirekte Basisklassen Wird eine Klasse von einer Klasse abgeleitet, die selbst wieder von einer anderen klasse abgeleitet ist, wird zur Einrichtung des Objektes der Klasse A zuerst der Konstruktor von B2 aufgerufen, da A von B2 abgeleitet ist. Da B2 aber von B1 abgeleitet ist muß dazu erst der Konstruktor von B1 aufgerufen werden, damit der Konstruktor von B2abgearbeitet werden kann. Zuletzt wird der Anweisungsteil des Konstruktors von A abgearbeitet class B1 {...}; class B2: public B1 {...}; class A : public B2 {...}; Nicht vererbbare Methoden Wird eine Klasse aus einer anderen abgeleitet, so erbt diese neue Klasse, Datenelemente und Methoden. Es gibt aber Methoden, die nicht mit vererbt werden können. – – – – Konstruktor Destruktor Zuweisungsoperator Friend- Deklarationen, sie stellen auch keine wirklichen Elemente dar. Diese Methoden müssen in der neuen Klasse neu deklariert werden. Polymorphie Polymorphie heißt so viel wie „Vielgestaltigkeit“. Der Begriff Polymorphie ist eng mit der Vererbung verbunden. Er bezeichnet die Tatsache, das eine Methode in verschiedenen abgeleiteten Klassen einer Hierarchie unterschiedlich implementiert werden kann. So ist es möglich, daß verschiedene Klassen gleichlautende Methoden enthalten, die aber nicht den gleichen Inhalt haben. Verschiedene Objekte werden gleich behandelt, reagieren aber unterschiedlich. Sie können gleich behandelt werden, weil sie zum Teil gleiche Schnittstellen besitzen (gleichlautende Datenelemente und Methoden). Beispiel: Paint Programm Benutzung Methoden, die in mehreren Klassen benötigt werden, werden in einer Basisklasse deklariert. In den abgeleiteten Klassen werden sie dann überschrieben, um eine klassenspezifische Ausführung zu erreichen. Methoden, die überschrieben werden sollen werden in der Basisklasse mit dem Schlüsselwort virtual deklariert, um sicher zu stellen, daß beim Aufruf auch wirklich die pberschriebenen Methoden aufgerufen werden und nicht die der Basisklasse. Es ist üblich auch die überschriebenen Methoden in den abgeleiteten Klassen mit virtual zu kennzeichnen. Beispiel Class ZeichenObjekt{ protected: struct Punkt referenzpunkt; virtual int zeichne(struct Punkt p){ //zeichne Referenzpunkt an Koordinate p...} }; class Rechteck: public ZeichenObjekt{ protected: struct Punkt ecken[4]; virtual int zeichne (struct Punkt p){ //zeichne Rechteck mit linker unterer Ecke in p...} }; class Rechteck: public ZeichenObjekt{ protected: float radius; virtual int zeichne (struct Punkt p){ //zeichne Kreis mit Mittelpunkt in p...} };