Listen: Anfängerproblem mit Zeigern.
-
Hallo,
ich hab ferade ganz neu mit C++ angefangen und bin dementsprechend in diesem Bereich noch ziemlich unerfahren, da ich aus einer ganz anderen Programmierecke komme

Zum Übern habe ich jetzt versucht das Konzept einer verketteten Liste in C++ nachzubauen. An sich meine ich das Konzept verstanden zu haben, aber leider funktioniert es nicht richtig.Ich habe also die Klassen Liste, Knoten und Abschluss. Letztere beiden leiten sich von Listenelement ab.
Meine Klassen Liste und Abschluss sieht vereinfacht so aus:
class Liste { public: Listenelement* erster; Liste() { Abschluss a1; //Abschluss* erster; erster = &a1; cout << "Erster: " << (*erster).inhalt << "\n"; } }; //Abschluss mit Konstruktor class Abschluss: public Listenelement { public: Listenelement* naechster; int inhalt; Abschluss() { inhalt = 99; } };Leider wird beim erstellen einer Liste eine sehr hohe Zahl ausgegeben und nicht "99, wie es der Konstruktor von Abschluss eigentlich setzen müsste.
Wenn ich aber den Zeiger erster vom Datentyp "Abschluss" erstelle (hier auskommentiert) funktioniert es. Ich nehme deswegen an, dass es daran liegt, dass Listenelement und Abschluss, bzw Knoten unterschiedlich groß sind und deshalb ein Zeiger vom Typ "Listenelement" ganz woanders hinzeigt, als der eines Abschlusses. Aber wie kann ich das verhindern? Und wieso reicht das Vererben dafür nicht aus?
Ich hoffe ich habe mich verständlich ausgedrückt. Wenn nicht: Sorry! Dann fragt bitte nochmal nach
Vielen Dank schonmal für eure Antworten!

-
Ich nehme an, Listenelement hat ebenfalls einen Member namens inhalt? Denn sonst würde das nicht compilieren. Dann greifst du aber in Zeile 9 auf dessen Inhalt zu, der uninitialisiert ist.
Ich würde alles ganz anders machen:
-Listenelemente haben Inhalt und einen Zeiger auf das nächste Element.
-Das Ende der Liste wird durch einen Nullzeiger angezeigt.Dann braucht man keine spezielle Behandlung des Endes mehr und auch keine Typidentifizierung zur Laufzeit (was ein großer Vorteil ist, wenn man das Vermeiden kann!).
Außerdem: Ich weiß nicht, ob es nur zum Zweck des Beispiels ist oder ob dir das wirklich nicht klar ist: Das a1 aus Zeile 6 wird zerstört sobald der Konstruktor verlassen wird und der Zeiger erster würde auf etwas ungültiges zeigen. Falls dir dies nicht absolut klar war, dann würde ich erst einmal ein bisschen was einfacheres versuchen.
-
Vielen Dank schonmal für die Antwort!
Ja, Listenelement hat auch einen inhalt.
Ok, das ist natürlich auch eine Möglichkeit. Ich hab mich eben vom Konzept her an meine alte Java Implementierung gehalten, bei der Listenelement eine abstracte Klasse war. Kann man soetwas nicht auch in C++ umsetzen? Eben eine Typidentifizierung während der Laufzeit. Oder warum sollte man das nicht so machen?
Der Fehler in Zeile 6, ist mir nur grade beim Vereinfachenn passiert. Ich hab das nur schnell in den Konstruktor mit reingezogen, damit ich nicht noch die main() posten muss. Im echten Code ist das anders. Trotzdem Danke dür den Hinweis

-
php.freak schrieb:
Ok, das ist natürlich auch eine Möglichkeit. Ich hab mich eben vom Konzept her an meine alte Java Implementierung gehalten, bei der Listenelement eine abstracte Klasse war. Kann man soetwas nicht auch in C++ umsetzen? Eben eine Typidentifizierung während der Laufzeit. Oder warum sollte man das nicht so machen?
Ja, das kann man machen (und ist auch nicht sooo schwer). Aber das hat man doch gar nicht nötig. Und anders als in Java bezahlt man in C++ nur für das, was man auch tut. Den Typ von Objekten zur Laufzeit zu verfolgen, dafür muss der Compiler viel Code generieren, der dann alle diese Objekte in Tabellen einträgt (oder eine ähnliche Technik). Das ist (im Vergleich zu wenn man das nicht macht) ungeheuer langsam.
Wenn du das mit Nullzeigern machst, dann wird das einfacher und schneller. Was will man mehr? Versuch nicht Java in C++ zu programmieren. Wenn man eine Sprache mit den Stärken und Schwächen von Java möchte, dann nimmt man Java. Wenn man Java in C++ macht, kombiniert man hingegen eher die Nachteile beider Sprachen.
-
Ja, natürlich.
Ich hab es jetzt mal mit dem Nullpointer Konzept versucht, leider funktioniert das Einfügen von Knoten nicht.Das hier ist die entsprechende Methode in der Listenklasse:
Knoten* knoten_vorne_einfuegen(Knoten k1) { cout << "Knoten mit dem Inhalt " << k1.inhalt << " angefügt.\n"; Knoten* alter_erster; //Adresse vom alten Ersten Knoten alter_erster = erster; erster = &k1; //Erster Knoten gleich neuer Knoten return alter_erster; }Und das ganze wird so aufgerufen:
Knoten k1(1); //Nachfolger vom neuen ersten Knoten, auf alten Ersten Knoten setzen: k1.naechster = l1.knoten_vorne_einfuegen(k1);Leider wird der neue Knoten zwar vorne in der Liste eingefügt, sein "naechster"-Zeiger bleibt aber 0 (durch den Konstruktor) und wird nicht auf den vorherigen Ersten Knoten gesetzt.
Was hab ich diesmal wieder übersehen? Komplett falsch gemacht?
-
Da ist mindestens ein dicker Fehler drin, wahrscheinlich mehr:
Wieder: k1 existiert nicht mehr, wenn die Methode verlassen wird, der Zeiger da drauf wird ungültig. Du arbeitest da mit einer lokalen Kopie.Was ist l1? Wo steht überhaupt der Code, der k1 und l1 benutzt?
Wenn du dir noch nicht sicher bist mit Gültigkeitsbereichen und dem genauen Verhalten von Funktionsargumenten, dann bist du noch nicht bereit für das Projekt. Diese Dinge funktionieren ein bisschen anders als in Java, sehen aber genau so aus. Das ist gefährlich. Außerdem wirst du für die Liste manuelle Speicherverwaltung benötigen. Auch so ein Thema, wo man leicht auf die Idee kommt, es so wie in Java zu machen und auf den ersten Blick scheint es auch zu funktionieren, aber in C++ geht es eigentlich ganz anders. Zumindest, wenn man es selber macht. Dir ist sicherlich bekannt, dass man weder in Java noch in C++ jemals selber solch verbreitete Datenstrukturen basteln würde. Wobei dieses Thema aber durchaus sehr gut zum Lernen geeignet ist, denn wenn du es richtig machst, lernst du ein paar sehr wichtige C++-Techniken zur Ressourcenverwaltung, die nicht nur auf dynamische Speicherverwaltung beschränkt sind, und die es so in Java gar nicht gibt.
-
Vielen Dank nochmal für das geduldige Korregieren meiner immer wieder gleichen Fehler!

Ja, es lag natürlich wieder an der lokalen Variable. Mir ist das vom Prinzip her schon klar, ich bin es einfach nur noch nicht gewöhnt, da ich es jahrelang eben anders eingeübt habe. Außerdem habe ich das Problem bei den Zeigern vermutet, die mir bislang am kompliziertesten erscheinen und dabei wieder das Offensichtliche übersehen...
Der Rest des Codes funktioniert jetzt übrigens einwandfrei.
l1 ist das entsprechende Objekt der Klasse Liste. Dieser Code steht direkt in meiner main(). (In der ich auch noch den gleichen Namen "k1" benutzt habe, was das ganze natürlich nicht gerade übersichtlicher gemacht hat
).Wie gesagt, grundsätzlich istversteh ich das schon und ich glaube auch, dass ich durchaus in der Lage bin mein Projekt zu realisieren. Es geht mir dabei auch nur ums learning by doing. Ich habe nicht vor die Liste in einem echten Projekt zu nutzen.
-
Warum lernst du c++?