[C++] Vererbung

Hi,

ich habe mich gerade zum ersten Mal mit abgeleiteten Klassen beschäftigt und schon hagelts Probleme ohne Ende :wink:

Ich habe 4 Klassen:

class Klasse {...};
class abgelKlasse : Klasse {...};
class Element {...};
class abgelElement : Element {...};

es gibt eine Funktion findeElement(char*) in der Basisklasse, die ein Element zurückliefert.

jetzt mache ich in der abgeleiteten Klasse:
abgelElement* element = findeElement(„soundso“);

und der Compiler sagt mir:
‚class Element *‘ kann nicht in ‚class abgelElement *‘ konvertiert werden.

wie krieg ich das hin, dass er nicht mit Elementen, sondern mit abgeleiteten Elementen arbeitet ohne dass ich die ganze Funktion neu implementieren muss, weil dann kann ich die Vererbung gleich lassen.

Danke
Bruno

Eine Möglichkeit wäre, wenn du dann einfach einen Downcast machst:

abgelElement* element = (agelElement*)findeElement(„soundso“);

Geht natürlich nur, wenn findeElement auf bestehende Objekte zugreift und nicht ein neues Element der Superklasse erzeugt und zurückgibt.

Grüße, Robert

Ok, danke, ansich hätt ich da auch selber draufkommen können :wink:
jetzt compiliert er’s mir immerhin, aber ich bezweifle irgendwie dass er auch wirklich mit abgeleiteten Objekten arbeitet :wink:
aber ich werde es sehen

Bruno

Hallo Bruno,

das mit dem Vorschlag vom Robert wird sicherlich funktionieren.

Sauberer ist es jedoch, wenn Du mal folgendes versuchst:

Element* element = findeElement(„soundso“);

Ist das Element vom Typ abgelElement, so zeigt Element auch auf
ein Objekt vom Typ abgelElement. Das ist dann der Polymorphismus.

Nachteil ist, dass dann nur Methoden aus Element benutzt werden können.
Willst Du dann Methoden aus abgelElement aufrufen, musst Du tatsächlich einen cast verwenden.

Dirk

[Bei dieser Antwort wurde das Vollzitat nachträglich automatisiert entfernt]

Besser nicht!

Hi Robert :wink:))

abgelElement* element = (agelElement*)findeElement(„soundso“);

Das kann natürlich in einer Katastrophe enden, wenn in der abgeleiteten Klasse nämlich neue Daten-Member vorhanden sind! Die Funktion findeElement() gibt ein Objekt der Basisklasse zurück, in der diese Daten-Member natürlich fehlen! Wenn du jetzt versuchst, über element auf diese Daten-Member zuzugreifen, wird ein Fehler auftreten. Wenn du Glück hast, wird es ein „segmentation fault“ sein!

cu Stefan.

Hi Bruno :wink:))

abgelElement* element = findeElement(„soundso“);

Wegen der is-a-Beziehung ist eine automatische Typkonvertierung von der abgeleiteten Klasse zur Basisklasse vorhanden. Was du aber brauchst, ist die umgekehrte Typkonvertierung von der Basisklasse zur abgeleiteten Klasse! Das kannst du dadurch erreichen, dass du für die abgeleitete Klasse einen Constructor definierst, der als Argument ein Objekt der Basisklasse hat.

‚class Element *‘ kann nicht in ‚class abgelElement *‘

Genau dieses Problem wird durch den o.g. Constructor behoben.

cu Stefan.

Hallo Stefan,

danke für deine Antwort, das is die einzige der ich bisher so recht traue :wink: aber ich versteh es nicht ganz.

Ich lege also einfach einen Konstuktor so an:
abgelKlasse::abgelKlasse(Klasse) {}
oder wie?

und was muss ich da reinschreiben? nichts?

Bruno

Moin,

wie krieg ich das hin, dass er nicht mit Elementen, sondern
mit abgeleiteten Elementen arbeitet ohne dass ich die ganze
Funktion neu implementieren muss, weil dann kann ich die
Vererbung gleich lassen.

Wie ich das sehe, mußt Du die Methode natürlich neu implementieren, wenn sie auf einmal einen anderen Typ liefern soll. Ich würde einfach die alte Methode benutzen und explizit casten.
(Mit einem etwas weniger abstrakten Beispiel hätte ich es besser verstanden.)

Thorsten

Hi Bruno :wink:

danke für deine Antwort, das is die einzige der ich bisher so
recht traue :wink: aber ich versteh es nicht ganz.

Ups :wink:)) Danke für das Lob …

Ich lege also einfach einen Konstuktor so an:
abgelKlasse::abgelKlasse(Klasse) {}
oder wie?

abgelKlasse::abgelKlasse (Klasse &klasse) : Klasse(klasse) {
//Jetzt würde ich noch die neu hinzugekommenen
//Daten-Member initialisieren
… }

Ähnlich wie beim Copy-Constructor übergibst du eine Referenz auf die Basisklasse. Mit dieser rufst du den Konstruktor der Basisklasse auf [passiert durch „: Klasse(klasse)“]. Nun solltest du noch die neu hinzugekommenen Daten-Member initialisieren und bist fertig. Theoretisch kann der Funktions-Body aber auch leer sein. Wichtig ist nur, dass der Constructor der Basisklasse aufgerufen wird, um das Objekt der abgeleiteten Klasse korrekt zu initialisieren :wink:))

Merke, dass jeder Constructor mit nur einem Parameter eine automatische Typumwandlung definiert!

cu Stefan.

genauere Erklärung des Problems
Es geht um meine HashTable-Klasse die ich gebaut habe. Ich kam jetzt auf einmal auf die Idee, dass Sie ansich nicht so spezialisiert auf meinen Fall sein bräuchte sondern eine abstrakte Basisklasse sein soll.

Die HashTable enthält in jedem ihrer Felder eine doppelt verkettete Liste.

Also ich habe eine Klasse HashTable, die x Elemente von der Klasse HashTableStartElement hat. Das StartElement hat einen Zeiger auf den ersten und letzten HashTableEntry. Die Entries sind untereinander verkettet.

Jetzt ist mein Problem, dass ich jetzt je nach Einsatzzweck eine von der Klasse HashTable abgeleitete Klasse brauche und auch nen abgeleiteten Entry, da jeder Entrytyp verschiedene Felder hat, je nachdem was er enthalten soll. Nur die Verkettung ist immer gleich (nextElement und prevElement).

Ich möchte deswegen z.B. nicht in jeder abgeleiteten Klasse eine Funktion findElement(char*) implementieren, nur weil sie jedesmal einen anderen Typ von Pointer zurückliefern soll.

MfG Bruno

Hi,

das klingt alles ziemlich vermurkst - sorry.
Ein guter Teil der Arbeit beim objektorientierten Programmieren besteht darin, dass man sich bevor (!) man anfaengt zu programmieren hinsetzt und sich eine brauchbare Klassenhierarchie ausdenkt. Klassen sollten immer (!) so unspezialisiert wie irgend moeglich sein, auch wenns mehr Arbeit bedeutet - das spart im Endeffekt immer Arbeit, auch wenn mans natuerlich nicht immer braucht, das weiss man nur leider vorher nicht. Der beste Vorschlag meiner Vorredner war der mit dem Polymorphismus. So und nur so programmiert man objektorientiert, alles andere ist Murks.
Fuer Dein Problem solltest Du Dich mit overloading, overriding (wichtig stichwort virtual!!!) und polymorphism ueberhaupt beschaeftigen. Manche Probleme lassen sich mit run-time type identification in den Griff bekommen, aber auch das ist in den meisten Faellen Murks.
Es gibt uebrigens ziemlich gute Hashtables ind der STL (standard Template library, die C++ standard bibliothek - ein wahrer Hort praktischer Klassen!), sieh mal nach „map“. So gut wie die kriegst das vermutlich erstmal nicht hin. Seit ich mich mit der STL auseinander gesetzt habe habe ich keine listen mehr geschrieben (naja, ich hasse listen schreiben ;-&gt:wink:, da gibts naemlich fuer die gaengigen Falle alles was man so braucht.
Bei groesseren Projekten (sagen wir mal groesser 5000 Zeilen) wird teilweise Monate nur an der Hierarchie gefeilt bevor auch nur eine Zeile Code steht!
Um eine Hierarchie zu entwerfen muss man natuerlich wissen, welche Sprachmittel zur Verfuegung stehen …
Ich hoffe, das war nicht zu hart, und/oder entmutigend. C++ lohnt sich, STL lohnt sich fast nochmal so viel wie C++ lernen und sauber Programmieren (Hierarchie …) lohnt sich ohnehin. Wenn Du Dich echt hinsetzt und das noetige Wissen in die Birne trichterst kommst Du ca drei Monate schlecht voran (man lernt natuerlich am besten, wenn man an einem aktuellen Projekt arbeitet). Dann steigt Deine Produktivitaet aber mehr, als Du es Dir momentan vermutlich vorstellen kannst.

Gruss und viel Erfolg

Thorsten

Das kann natürlich in einer Katastrophe enden,

Ansich habe ich dazu geschrieben, dass das nur gscheit ist, wenn das zurückgegebene Objekt vom Typ der abgeleiteten Klasse ist.

D. h. wenn er z. B. Elemente eines Arrays vom Typ der Superklasse zurückgibt, in das aber von andren Methoden nur Elemente der abgeleiteten Klasse geschrieben wurden. Dann kannst du gefahrlos downcasten, weil die Methoden etc. vorhanden sind.

Grüße, Robert

Danke für den Tip, aber ich hatte irgendwo gelesen eine map wär ein vector von key, value pairs, das wär ja verheerend langsam. Naja wird vielleicht auch nicht wahr sein.

Aber ich brauche ziemlichen Low-Level-Access auf die Hashtable, weil ich bestimmte Teile davon locken möchte um den Code thread-safe zu kriegen.

Liegt sie STL eigentlich im Source-Code vor?

Danke
Bruno

Hi,

Danke für den Tip, aber ich hatte irgendwo gelesen eine
map wär ein vector von key, value pairs, das
wär ja verheerend langsam. Naja wird vielleicht auch nicht
wahr sein.

Ich weiss ehrlich gesagt nicht, wie es implemetiert ist - vermutlich ist das auch nicht festgelegt. Stanards legen das Verhalten fest, nicht die Implementierung. Dennoch ist eine map zu mindest aus meiner STL-Version verheerend langsam.
Die Geschwindifkeit haengt auch davon ab, was drin ist, da Konstruktor-calls ausgefuehrt werden!

Aber ich brauche ziemlichen Low-Level-Access auf die
Hashtable, weil ich bestimmte Teile davon locken möchte um den
Code thread-safe zu kriegen.

Sorry, das war etwas kurz fuer mich - nix verstanden :smile:

Liegt sie STL eigentlich im Source-Code vor?

Natuerlich.

Gruss

Thorsten

Dennoch ist eine
map zu mindest aus meiner STL-Version verheerend langsam.

??? damit bestätigst du ja praktisch meine Sorge :wink:
und mir gehts rein um Performance, ich habe das Ding auch mit Move-To-Front ausgestattet, weil sich das gut für meinen Einsatzzweck eignet, manche Elemente werden einfach wesentlich stärker aufgerufen.

Aber ich brauche ziemlichen Low-Level-Access auf die
Hashtable, weil ich bestimmte Teile davon locken möchte um den
Code thread-safe zu kriegen.

Sorry, das war etwas kurz fuer mich - nix verstanden :smile:

Ich meinte, ich greife aus mehreren Threads auf die verkettete Liste in einem Hash-Feld zu. Damit es dabei nicht zu Fehlern kommt, werde ich den Code mit Mutexes oder ähnlichem locken, sodass immer nur ein Thread Zugriff auf ein Element hat. Was ich genau locken werde ist mir noch nicht ganz klar, evtl. jeweils den Start einer verketteten Liste, aber ich will nicht die ganze Hashtable locken und dafür muss ich in der Source rumbasteln denk ich mal.

MfG Bruno

Hi,

ja, fuer wirklich geschwindigkeits kritische Anwendungen sind maps wohl zu lahm … der werden meines Wissens waehrend der Suche jede Menge Konstruktoren und Destruktoren aufgerufen!

Zum Multithreading:
Man koennter eine map wohl dennoch verwenden, indem man eine kleine Klasse schreibt, die den Zugriff regelt. Aber da Du offenbar keinen Overhead gebrauchen kannst macht das wenig Sinn.
Ach ja, in meiner Antwort auf Dein Funktionspointerposting ist mindestens ein Syntaxfehler aufgefallen … aber ich denke, Du verstehst, was ich meine …

Gruss

Thorsten