delete auf ein Vectorelement
-
Hallo!
Ich habe folgendes Codebeispiel:std::vector <cStream*> Streams; Streams.push_back(new cStream) delete Streams[0]; Streams.erase(Streams.begin());Das ist jetzt nur ein vereinfachtes Beispiel. Natürlich sind in dem
Vector noch mehr cStream Pointer. Mein Problem ist, dass das delete hier
einen Segfault ergibt. Ich will nicht nur den Pointer aus dem Vecor
entfernen, sondern auch den Speicher, der beim push_back mit new angefordert
wurde, wieder freigeben.
Warum funktioniert es mit diesem delete nicht?
-
Warum wollen so viele Leute ihre eigene Speicherverwaltung in Container packen? Container sind ja unter anderem gerade dazu da, einem diesbezüglich Arbeit abzunehmen. Bis auf wenige Fälle (vor allem Nicht-Kopierbarkeit, Polymorphie) gibt es wirklich keinen Grund für besitzende Zeiger in Containern. Und selbst in diesen Fällen gibt es bessere Alternativen, zum Beispiel die Pointer-Container von Boost.
Was spricht bei dir gegen Folgendes?
std::vector <cStream> Streams;
-
Nexus schrieb:
Was spricht bei dir gegen Folgendes?
std::vector <cStream> Streams;Daß Wertsemantik bei einer Streamklasse unsinnig ist?
-
audacia schrieb:
Daß Wertsemantik bei einer Streamklasse unsinnig ist?
Huch, hab gar nicht so auf die Bezeichner geachtet. Sorry in diesem Fall...

(Wenn sich mir die Möglichkeit böte, würde ich dennoch zu Pointer-Containern greifen).Aber zu dem Problem:
Könnte es sein, dass diecStream-Klasse intern etwas falsch handhabt? Ist der Destruktor zum Beispiel richtig implementiert? Versuch ansonsten mal, mit dem Debugger den Fehler genau zu lokalisieren...
-
Dran gedacht, dass alle Iteratoren beim Aufruf von erase() ungültig werden?
-
Der Destruktor von cStream gibt sämtlichen allokierten speicher innerhalb
der Klasse frei.
Und wie kann ich das Problem mit den ungültigen Iteratoren umgehen?
Ich werd mir aber trotzdem mal die boost-Container angucken.
-
Tritt dein Problem wirklich auch im oben geposteten Beispiel auf? Da kann es nämlich nicht an den Iteratoren liegen. Falls ja, würde ich trotzdem mal genau debuggen und schauen, ob zum Beispiel ein ungültiger Zeiger freigegeben wird oder so.
-
mase schrieb:
Und wie kann ich das Problem mit den ungültigen Iteratoren umgehen?
mit std::list...
bb
-
indem man den Rückgabewert von erase nutzt.
-
Naja, nach dem erase() wird bei mir gar kein Iterator mehr verwendet.
Vor dem nächsten Aufruf von erase() wird der Iterator neu initialisiert
mit Streams.begin().
-
Ich poste mal etwas mehr Code.
Die Streams werden so in den Vector eingefügt:
Streams.push_back(new cStream);Wenn ein Stream entfernt werden soll, wird folgende Funktion aufgerufen:
void RemoveStream(int _StreamNumber) { //remove stream completely Streams[_StreamNumber]->CloseStream(); std::vector<cStream*>::iterator Iter = Streams.begin(); for (int i = 0; i < _StreamNumber; i++) { Iter++; } delete Streams[_StreamNumber]; Streams.erase(Iter); NumberOfStreams--; return; }_StreamNumber ist der entsprechenden Bezeichner.
Das CloseStream() der cStream-Klasse löscht nichts. Es wird lediglich die
Serververbindung beendet.
Das delete hier führt zu einem Segfault.
-
Ich hab es jetzt mal mit einem Smartpointer in einem Pointervector von
libboost probiert:Streams.push_back(boost::shared_ptr<cStream>(new cStream));Der Vector ist so deklariert:
boost::ptr_vector<boost::shared_ptr<cStream> > Streams;Doch es kompiliert nicht.
By the way: Ist es besser, anstatt erase() ein release() aufzurufen, wenn
das Objekt auch zerstört werden soll?
Das ist die Fehlermeldung des Compilers:error: no matching function for call to 'boost::ptr_vector<boost::shared_ptr<cStream>, boost::heap_clone_allocator, std::allocator<void*> >::push_back(boost::shared_ptr<cStream>)' /usr/include/boost/ptr_container/ptr_sequence_adapter.hpp:178: note: candidates are: void boost::ptr_sequence_adapter<T, VoidPtrSeq, CloneAllocator>::push_back(typename boost::ptr_container_detail::reversible_ptr_container<boost::ptr_container _detail::sequence_config<T, VoidPtrSeq>, CloneAllocator>::value_type) [with T = boost::shared_ptr<cStream>, VoidPtrSeq = std::vector<void*, std::allocator<void*> >, CloneAllocator = boost::heap_clone_allocator]
-
Wieso
ptr_vectorundshared_ptr? Entweder oder, eins davon reicht. Ich würdeptr_vectorempfehlen, da du mit dem Smart-Pointer mit grosser Wahrscheinlichkeit Overhead hast, den du gar nicht benötigst.
-
Ich hab mich für den ptc_vector entschieden. Seh ich das richtig, dass die
Elemente darin automatisch dereferenziert werden, ich also mit dem . Operator
zugreifen muss, anstatt ->?
Aber folgender Aufruf führt zu einem Segfault:boost::ptr_vector<cStream>::iterator Iter = Streams.begin(); for (int i = 0; i < _StreamNumber; i++) { Iter++; } Streams.erase(Iter);
-
Du versuchst wahrscheinlich, ein Element zu löschen, das bereits jenseits der Sequenz liegt.
Normalerweise iteriert man aber auch anders.

for (boost::ptr_vector<cStream>::iterator iter = Streams.begin(); i != Streams.end(); ++i) // wichtig: nicht i++ { // Mache für jedes Element etwas }Wenn du allerdings nur ein Element an einem bestimmten Index löschen willst, kannst du das bei Random-Access-Containern auch so machen:
Streams.erase(Streams.begin() + Index);Aber sei dir bewusst, dass Zeiger, Referenzen und Iteratoren auf Elemente des Containers anschliessend ungültig werden können.
-
Nexus schrieb:
Du versuchst wahrscheinlich, ein Element zu löschen, das bereits jenseits der Sequenz liegt.
Normalerweise iteriert man aber auch anders.

for (boost::ptr_vector<cStream>::iterator iter = Streams.begin(); i != Streams.end(); ++i) // wichtig: nicht i++ { // Mache für jedes Element etwas }Wenn du allerdings nur ein Element an einem bestimmten Index löschen willst, kannst du das bei Random-Access-Containern auch so machen:
Streams.erase(Streams.begin() + Index);Aber sei dir bewusst, dass Zeiger, Referenzen und Iteratoren auf Elemente des Containers anschliessend ungültig werden können.
Ich hab's nach der 2. Methode gemacht. Ich hab jedoch gelesen, dass release(),
anstatt erase() aufgerufen werden sollte, da so auch der Speicher für das
Element freigegeben würde. Mein Ziel war ja, dass nicht nur das Element
aus dem Vector entfernt wird, sondern auch ein delete für das Element auf-
gerufen wird.
Seh ich das richtig:
Beim std::vector muss als Typ <cStream*> angegeben werden, beim
boost::ptr_vector ein <cStream>, sonst wären es ja Zeiger auf Zeiger.
Der Elementzugriff erfolgt mit Punktoperator, anstatt ->.
-
ja.