Böser Stack: Warum Pair nach Verlassen der Funktion nicht mehr existiert?



  • Hallo,

    Ich kenne eigentlich grundsätzlich den Unterschied zwischen dem Heap und dem Stack. Ich dachte lange, dass ich den Gebrauch des Heaps gänzlich vermeiden kann und alle Objekte auf den main Stack legen kann.... ich glaube diese Zeit ist jetzt vorbei.

    Mein Problem: Ich möchte zwei Werte (int und Objekt) in einem Pair container in einen Vector pushen. Das frisch gepushte Pair scheint nach dem Verlassen der Funktion nicht mehr zu existieren. Inkl. den darin enthaltenen Instanzen?

    void Scheduler::wakeMeUp(int interval, InfClockListener listener) {
    
    	// push back to our todo-list
    	this->wakeUpCandidates.push_back(wakeUpCandidate(executeAllNnsec, listener));
    }
    

    Müsste eigentlich nicht der Default-Copy-Constructor von Pair eine unabhängige Kopie erstellen, da die Instanz in einem Funktionsaufruf (push_back()) übergeben wird?

    Führt hier kein Weg an New und Heap vorbei? 😕

    Danke für die Nachhilfe.



  • Wie sind die Datentypen von wakeUpCandidate und wakeUpCandidates?



  • maus_on_c schrieb:

    Müsste eigentlich nicht der Default-Copy-Constructor von Pair eine unabhängige Kopie erstellen, da die Instanz in einem Funktionsaufruf (push_back()) übergeben wird?

    Doch. Wie sieht deine Pair-Klasse aus?

    Und wie kommst du zur Annahme, dass es nach dem Pushen nicht mehr existiert? Hast du den std::vector mit dem Debugger angeschaut? Ist es gar nicht mehr da, oder hat es komische Werte, oder...?

    Wie du siehst, haben wir ein bisschen wenig Informationen. Abgesehen davon würde ich mir Dinge wie "C++ ohne Heap programmieren" nicht angewöhnen, dafür ist das die falsche Sprache. Klar kannst du die manuelle Speicherverwaltung durch Container & Co. auf ein Minimum reduzieren (was auch gut ist), aber es gibt durchaus Fälle, bei denen du manuelle Speicherverwaltung brauchst (und sei es nur innerhalb der Implementierung einer Klasse).



  • maus_on_c schrieb:

    Ich dachte lange, dass ich den Gebrauch des Heaps gänzlich vermeiden kann...

    Aus Sicht des Programmierers vielleicht. Aber die Standardbibliothek arbeitet durchaus intern mit Heap-Speicher (Gerade auch der std::vector).

    Dein Problem dürfte aber an einer anderen Stelle liegen, den wie du schon richtig annimmst, wird im vector mit einer Kopie gearbeitet.

    Ich möchte zwei Werte (int und Objekt)...
    

    Was ist bitte "Objekt", ich hoffe keine Basisklasse (den ansonsten solltest du mit Zeigern, Smartpointer oder ähnlichen arbeiten).

    cu André



  • Hallo,

    Danke für eure Antworten.

    Nexus schrieb:

    maus_on_c schrieb:

    Müsste eigentlich nicht der Default-Copy-Constructor von Pair eine unabhängige Kopie erstellen, da die Instanz in einem Funktionsaufruf (push_back()) übergeben wird?

    Doch. Wie sieht deine Pair-Klasse aus?

    Ich verwende die std::Pair Klasse aus <utility> . Die folgende Aussage stimmt doch: "Wenn Objekte als Wert in Funktionen übergeben werden, wird eine Kopie dieser erstellt" (in etwa)

    Nexus schrieb:

    Und wie kommst du zur Annahme, dass es nach dem Pushen nicht mehr existiert? Hast du den std::vector mit dem Debugger angeschaut? Ist es gar nicht mehr da, oder hat es komische Werte, oder...?

    Sobald ich auf den Vector zugreife, erhalte ich einen Segmentation Fault. Im Debugger sieht man, dass nix mehr im Vector ist.

    asc schrieb:

    Was ist bitte "Objekt", ich hoffe keine Basisklasse (den ansonsten solltest du mit Zeigern, Smartpointer oder ähnlichen arbeiten).

    knivil schrieb:

    Wie sind die Datentypen von wakeUpCandidate und wakeUpCandidates?

    typedef pair<int, InfClockListener> wakeUpCandidate;
    

    Beim WakeUpCandidate handelt es sich um einen Integer und die Interface-Klasse InfClockListener. Es handelt sich also tatsächlich um eine Basisklasse. Warum sollte man mit Basisklassen nur mit Pointern arbeiten?

    Was ich machen will: Es handelt sich beim Code um die Realisierung des Interface Pattern. In der anfänglich pulblizierten Funktion wakeMeUp(wakeUpCandidate) werden Klassen in den Vector gepusht, welche das Listener Interface implementieren.

    Was ich wissen will: Soll ich nun das Pair mit New im Heap erzeugen. Wer ist für die Löschung des Pairs verantwortlich, bzw. wo sollte das Pair gelöscht werden?

    Vielen Dank für eure Inputs.



  • maus_on_c schrieb:

    ...
    Beim WakeUpCandidate handelt es sich um einen Integer und die Interface-Klasse InfClockListener. Es handelt sich also tatsächlich um eine Basisklasse. Warum sollte man mit Basisklassen nur mit Pointern arbeiten?...

    Weil es sonst (wenn man nicht mit Pointern oder Referenzen arbeitet) zu "slicing" kommt. Beim Kopieren wird lediglich das "Basisklassen-subobjekt" kopiert und nicht die "geerbten" Teile.

    Und da für std::vector die Objekte kopierbar sein müssen und Referenzen das nicht sind, bleiben für so eine Lösung lediglich nur Pointer übrig. Dann bleibt natürlich die Frage, wo die Objekte selbst dann liegen bzw. wer die wie erzeugt und vernichtet ... z.B. via new oder new[], gibt aber auch andere Techniken.

    maus_on_c schrieb:

    ...
    Sobald ich auf den Vector zugreife, erhalte ich einen Segmentation Fault. Im Debugger sieht man, dass nix mehr im Vector ist. ...

    Das erklärt es IMHO immer noch nicht ausreichend:
    - liefert vector::size() 0 zurück?
    - zeigt der interne "Datenzeiger" des vectors auf 0?
    - was bedeutet "Zugriff"?

    cout << vec[0]; // ?
    vec[0].doSomething(); // ?
    vec[anyIndex].doSomething(); // ? const- / nonconst-Memberfunktion ?
    (dynamic_cast<derived_type> vec[anyIndex].second).doSomething(); // ?
    ...
    

    Es gibt seeeeehr viele Gründe für einen Segmentatione-fault in einem beliebig komplexen Statment - und sei es einfach eine schlichte Indexüberschreitung (die man z.B. mittels vector::at() statt vector::operator[]() abfangen kann)

    leicht OT: Wäre es nicht nett, wenn es für dieses "Standard"-Problem zukünftig auch mal eine "Standard-Lösung" gäbe? (sowas wie ein "std::baseclass_vector")

    Gruß,

    Simon2.



  • Simon2 schrieb:

    leicht OT: Wäre es nicht nett, wenn es für dieses "Standard"-Problem zukünftig auch mal eine "Standard-Lösung" gäbe? (sowas wie ein "std::baseclass_vector")

    Mit den Boost PointerContainers ist man recht gut beraten, und sehr viele Leute haben Boost. Aber Standard wäre natürlich schon besser, evtl. wird das ja mal aufgenommen. Ansonsten kann man sich auch seinen std::vector wrappen und intern Zeiger speichern...



  • Nexus schrieb:

    ...Mit den Boost PointerContainers ist man recht gut beraten, ...

    Ah ... danke, werde ich mir mal ansehen.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Das erklärt es IMHO immer noch nicht ausreichend:
    - liefert vector::size() 0 zurück?
    - zeigt der interne "Datenzeiger" des vectors auf 0?
    - was bedeutet "Zugriff"?

    cout << vec[0]; // ?
    vec[0].doSomething(); // ?
    vec[anyIndex].doSomething(); // ? const- / nonconst-Memberfunktion ?
    (dynamic_cast<derived_type> vec[anyIndex].second).doSomething(); // ?
    ...
    

    Es gibt seeeeehr viele Gründe für einen Segmentatione-fault in einem beliebig komplexen Statment - und sei es einfach eine schlichte Indexüberschreitung (die man z.B. mittels vector::at() statt vector::operator[]() abfangen kann)

    Der Zugriff auf den Vector sieht wie folgt aus:

    void Scheduler::runThroughListe(int mSecond) {
    	int debug = this->wakeUpCandidates.size();  // liefert 1965600 zurück...
    	for (vector<wakeUpCandidate>::iterator it = this->wakeUpCandidates.begin(); it
    			!= wakeUpCandidates.end(); it++) {
    
    		if (it->first > mSecond) {  // Hier bekomme ich SigSegV
    
    			InfClockListener debug = (it->second);
    			debug.wakeUp();
    		}
    	}
    
    }
    

    Die Grösse des Vektors ergibt beim debuggen keinen Sinn.
    Komisch, das erste Element des Pairs (it->first) sollte doch noch gültig sein, da es sich um einen trivialen INT handelt.
    Dass dieses Problem mit dem erwähnten Slicing zu tun hat leuchtet mir ein. Werde das Pair nun auf dem Heap erstellen. Danke für die ausführliche Diskussion. 👍


  • Administrator

    maus_on_c schrieb:

    Die Grösse des Vektors ergibt beim debuggen keinen Sinn.

    Wie hast du gedebuggt? (Hmmm, wie sagt man dem auf Deutsch? Das Wort sieht hässlich aus :D)
    Wieso macht es keinen Sinn? Welchen Wert erwartest du?

    Zudem, passiert in der Schleife, welche du uns gezeigt hast, nichts anderes? Also löschst du zufälligerweise ein Element aus dem Vektor und danach tritt der Fehler auf?

    maus_on_c schrieb:

    Dass dieses Problem mit dem erwähnten Slicing zu tun hat leuchtet mir ein. Werde das Pair nun auf dem Heap erstellen. Danke für die ausführliche Diskussion.

    Wenn das Objekt mit der Basisklasse InfClockListener geschnitten wird, dann kannst du noch so lange das std::pair auf den Heap legen, es wird immer noch abgeschnitten, schon nur wenn es an die Funktion übergeben wird. Du musst einen Zeiger auf InfClockListener speichern, also das entsprechende Objekt auf dem Heap ablegen.

    Grüssli


Anmelden zum Antworten