Pointer wird ungültig



  • Hallo,

    ich habe folgendes Problem, bei dem ich nicht weiter komme.
    Das Programm ist in MFC geschrieben, aber es ist wohl eher nur ein C++ Problem.

    Also ich habe eine Klasse A in der ich einen std:vector mit Zeigern auf Volumen-Objekte habe.

    std::vector<LogicalVolume*> logVolumes;
    

    Nun wird ein Volume-Objekt erstellt und der Zeiger in den Vektor eingefügt.

    LogicalVolume *logVol = new LogicalVolume(shape, str);		
    logVolumes.push_back(logVol);
    

    Später wird durch ein Ereignis eine Methode ausgelöst, in der ich auf die Elemente des Vektors zugreifen möchte.

    LogicalVolume* lv = logVolumes.at(0);
    std::wstring str2 = lv->getName();
    

    Das Problem ist, dass an dieser Stelle in der Liste bereits ein ungültiger Zeiger steht. Es wird keine andere Operationen mit der Liste durchgeführt (kein lesen/kein schreiben).

    Woran könnte es liegen? Was mache ich falsch?

    Ich habe schon probiert zu debuggen, aber durch Ereignis-basierendes System von MFC ist es praktisch unmöglich, alle Schritte zu verfolgen.

    Gruß


  • Mod

    Pointer werden nicht von alleine ungültig. Irgendwas ist zwischendurch passiert. Zum Beispiel würde mich interessieren, was aus logVol geworden ist.

    Wieso überhaupt Zeiger? Anscheindend sind deine Objekte ja nicht polymorph und haben auch keine exotische Lebensdauer. Oder haben sie eben doch eine exotische Lebensdauer und dies ist gerade der Grund für den Fehler, da das Objekt an Stelle 0 bereits zerstört, aber nicht aus der Liste entfernt wurde?



  • Mit logVol wird nach dem Einfügen in die Liste nichts weiter gemacht. Es wird nicht zerstört, falls du es wissen willst.

    Zeiger, weil die Datenstruktur doch etwas größer ist und es sehr viele Objekte sein sollten. Alles auf den Stack zu legen, wäre ungünstig.

    Was meinst du mit exotischer Lebensdauer?

    Der Name der Liste kommt in meinem Code nur 3 mal vor, genau wie oben aufgelistet (habe gerade noch mal nachgeschaut 😉 ). Ich komme gar nicht zum entfernen oder irgendwas löschen, ich möchte nur nach dem Einfügen auf das Element zugreifen...



  • Maxim_2004 schrieb:

    Zeiger, weil die Datenstruktur doch etwas größer ist und es sehr viele Objekte sein sollten. Alles auf den Stack zu legen, wäre ungünstig.

    std::vector legt die Objekte intern sowieso auf dem dynamischen Speicher - soll heissen Heap auf den gaengigen Systemen - an.
    Nur die Verwaltungsdaten des vectors, d.h. Element-Anzahl, Reservierte Anzahl und Zeiger auf die Datenstruktur im Heap etc. liegen auf dem Stack (bzw. dort wo der vector erstellt worden ist).

    Was SeppJ meint: Es gibt sehr selten Faelle, in denen ein Objekt seinen Ersteller unbedingt ueberleben muss. Ein Beispiel wo das noetig ist faellt mir grad nicht ein, sollte aber bei gutem Design auch nur in Ausnahmefaellen wirklich noetig sein...

    EDIT II: Zu deinem eigentlichen Problem: Zeig mal die relevanten Code-Stellen. Und geh das Programm am besten mal im Debugger durch. Setz Breakpoints an den Stellen, wo auf deinen Vector zugegriffen wird und gehe sie mit Single-Step durch. Oder setz einen Variablen-Breakpoint fuer den Zugriff auf die Adresse deines Vectors (sofern dein Debugger sowas unterstuetzt).



  • Dann werden doch aber nur Kopien von meinen Objekten in den Vektor eingefügt oder? 😕 Dann müsste ich einen ordentlichen Kopierkonstrukter schreiben, so ist es ja nicht nötig, wenn ich ständig auf das eine Objekt zugreife.

    Zeig mal die relevanten Code-Stellen.

    Das habe ich. Mehr gibt es nicht. Das sind zwei Methoden die Ereignisgesteuert aufgerufen werden. In einer wird das Objekt erstellt. In der anderen wird versucht darauf zuzugreifen.

    Wie gesagt mit dem Debugger ist es schwierig (weil ereignisgesteuert von MFC). Ich stelle dann den Breakpoint auf LogicalVolume* lv = logVolumes.at(0); und danach ist der Pointer ungültig oder er ist gültig und beim lv->getName(); gibts nen Laufzeitfehler in der windows wcscpy-Funktion.

    getName ist einfache Get-Methode:

    const std::wstring& getName() const { return name;}
    

  • Mod

    Maxim_2004 schrieb:

    Dann werden doch aber nur Kopien von meinen Objekten in den Vektor eingefügt oder?

    Ja. Zur Not gibt es noch emplace_back ab C++11, mit dem man die Objekte direkt an Ort und Stelle erzeugen kann. Außerdem werden die Objekte falls möglich an die richtige Stelle gemoved.

    Dann müsste ich einen ordentlichen Kopierkonstrukter schreiben, so ist es ja nicht nötig, wenn ich ständig auf das eine Objekt zugreife.

    Vielleicht solltest du allgemein dein Design überdenken, wenn du tatsächlich einen Kopierkonstruktor schreiben musst und du zudem noch denkst, dass dies problematisch ist. Ressourcen sollten in eigenen Halteklassen (z.B. Smartpointer für rohen Speicher) gehalten werden. Die brauchen dann natürlich einen Kopierkonstruktor, aber alles was diese Klassen nutzt braucht keinen mehr. Viele dieser Klassen gibt es auch schon fertig in der Standardbibliothek. Außerdem braucht man dies trotzdem eher selten, dein ursprünglicher Weg mit dem Pointervektor weckt den Verdacht, dass du irgendetwas unnötig umständlich gelöst hast, was eigentlich sehr einfach wäre. Und nun bekommst du Probleme, weil die Frickellösung an anderer Stelle zu mehr Frickelei bzw. zu Fehlern führt. Ich wette, am Ende wird sich herausstellen, dass hier auch die Ursache für dein Pointerproblem liegt.

    Mehr gibt es nicht.

    Dann können wir auch nicht weiter helfen, außer mit solch allgemeinen Tipps wie oben.



  • Danke für die Tipps, aber das Code-Design hat nichts mit dem Problem zu tun.

    Es scheint mir, dass es ein Problem zwischen STL und MFC gibt. Anders kann ich mir das ganze nicht erklären, da es stinknormaler C++ Code ohne Hacks etc. ist.
    Schiebt mal einer die Frage in den MFC-Thread?


  • Mod

    Maxim_2004 schrieb:

    Danke für die Tipps, aber das Code-Design hat nichts mit dem Problem zu tun.

    Wetten dass?

    Schiebt mal einer die Frage in den MFC-Thread?

    Meinetwegen, aber die wollen dort im Forum sicher auch mehr Informationen, als bloß eine Vermutung deinerseits. Welche Art von Antwort erhoffst du dir mit einer Frage ohne Code?



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++ (auch C++0x und C++11) in das Forum MFC (Visual C++) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Was mir grade auffällt: Sorgst du denn dafür, dass das Objekt auch korrekt erzeugt wird? Was, wenn kein Logical Volume (was auch immer das ist, sich er was mit Sound :P) gefunden wird?

    Abgesehen davon, warum packst du das nich in einen auto_ptr ?



  • Greifst Du von verschiedenen Threads auf den vector zu?



  • @threadproblem

    Also explizit nicht.
    Das Programm (MFC) hat aber auf jeden Fall mindestens zwei Threads, denn am Ende steht immer:
    "Der Thread 0xd70 hat mit Code 0 (0x0) geendet.
    Der Thread 0x100c hat mit Code 0 (0x0) geendet."
    Alles läuft aber innerhalb einer Klasse ab...

    Intelligente Pointer bringen da kein Stückchen weiter, das Problem liegt wo anders.

    Manchmal ist der Zeiger gültig bis zur zweiter Zeile:

    LogicalVolume* lv = logVolumes.at(0); 
    std::wstring str2 = lv->getName();
    

    Ich sehe mit dem Debugger, dass lv gültig ist und das Attribut name einen String besitzt. Führe ich aber den einen Schritt aus, so gibt es einen Laufzeitfehler in memcpy.asm in msvcr110d.dll.
    Fehlermeldung: "Ausnahme (erste Chance) bei 0x574FEA8E (msvcr110d.dll) in test.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x51004127"



  • Hallo,

    dann nehme die Funktion getName unter die Lupe, wie sieht die Zeichenkette aus, die getName zurück liefert, wie wird dieser Name erzeugt, ist er immer korrekt null-terminiert, wenn das alles nichts hilft, dann debugge dich in den Code der Initialisierung "str2 = lv->getName();", um herauszufinden, warum memcpy bzw. wcscpy nicht funktioniert, ...

    MfG,

    Probe-Nutzer



  • Da gibt es eigentlich nichts zu beachten. Ich benutze durchgängig std::wstring. In der Klasse ist ein Attribut std::wstring name;, der im Konstruktor mit name=L"noname"; initialisiert wird.

    Wenn ich mal mehr Zeit habe, werde ich versuchen, das Programm ohne MFC als Consolenprogramm umzuschreiben, dann weiß ich ob es daran liegt. Aber letztendlich kann ich auf MFC nicht verzichten...


  • Mod

    Setz doch einen Datenbreakpoint auf die erste Position im Array!
    Dann kannst Du doch sehen, wenn dieser Zeiger evtl. falsch überschrieben wird.

    Zusätzlich würde ich sicherheitshalber mal einen Breakpoint auf den Destruktor von Deinem Objekt setzen.
    Auf was zeigt denn *lv in diesem Moment?
    Ist der Zeiger ganz ungültig oder zeigt er auf Garbage.
    Ist evtl. das Objekt überschrieben worden. Dann setze einen Datenbreakpoint auf den ersten Datenmember der Klasse.


Log in to reply