Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

08.04.2013, 16:58

vector.erase() <-> delete

Hi,

es geht natürlich um std::vector. Die Doku behauptet, die Elemente werden zerstört. Was passiert dann aber hier:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
virtual bool Delete( size_t array_num, bool delete_data = 1 )
    {
        // if in vector
        if( array_num < objects.size() )
        {
            objects.erase( objects.begin() + array_num );
        }

        if( delete_data )
        {
            delete objects[array_num];
        }

        return 1;
    }


Findet nach dem erase() nicht ne Reallokation statt, wenn man ein Element mittenraus löscht? Greift man über den Array-Operator überhaupt noch auf dieses Element zu? Wieso ist das dann noch im Vektor? Oder manipuliert man per erase() nur Iterator-Zugriffe?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

08.04.2013, 17:22

Was soll der Code da genau machen? So wie ich das sehe, löscht er die ersten array_num Elemente aus dem vector und wenn delete_data true ist, wird auf das Element, das sich danach an Stelle array_num befindet, der delete Operator angewandt. Schaut mir auf den ersten Blick jetzt nicht besonders sinnvoll aus... ;)

simbad

unregistriert

3

08.04.2013, 18:55

Nach dem erase wird, und das steht normalerweise auch bei der Beschreibung des vector containers, der inhalt verschoben, wenn denn mitten im Vector gelöscht worden ist.
Daraus folgt, und auch das sollte in der Beschreibung stehen, das ein iterator, der auf das vector element verwiesen hat, das gerade gelöscht worden ist ungültig.
Bei einem erase wird zudem der Destruktor der Klasse aufgerufen, die da in dem vector container abgelegt ist.

Aber Achtung. Bei einem vector<Bla*> ist die Klasse ein pointer. Es wird also der Destructor eines pointers aufgerufen. Nicht der Destructor der Klasse auf die der Pointer zeigt.

In dem Fall musst du dann, so wie du das da versucht hast, ein delete machen. Dein Code ist aber trotzdem ungeschickt, da er leicht fehl interpretiert werden kann. Er wird auch so nicht funktionieren, weil das element, das du da per delete freigeben willst, ja nicht mehr unter array_num zu finden ist. Du hast es ja gerade per erase aus dem Container entsorgt.

Ist array_num das letzte element könnte es eine exception geben, das habe ich aber aktuell nicht im Kopf.

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

4

08.04.2013, 19:16

[...] Greift man über den Array-Operator überhaupt noch auf dieses Element zu? Wieso ist das dann noch im Vektor? Oder manipuliert man per erase() nur Iterator-Zugriffe?

Nur um das nochmal klar rauszustellen: Der Code ist absolut idiotisch, das hast du korrekt erkannt. Nach dem erase() ist das Element nicht mehr im Vektor, das was da deletet wird, ist irgendetwas unsinniges oder existiert nicht und führt direkt zum Crash.

Findet nach dem erase() nicht ne Reallokation statt, wenn man ein Element mittenraus löscht? [...]

Kann, muss aber nicht. Manche Implementierungen lassen Vektoren nur wachsen, aber nie schrumpfen. Andere Implementierungen schrumpfen ab und zu, nämlich immer wenn ein gewisser Grenzwert an unbenutztem Speicherplatz überschritten wird. Die Reallokation spielt in deinem Code-Fragment aber keine Rolle, da du nur per Indexzahl auf Elemente zugreifst. Grober Unfug ist der Code dennoch.

Beachte auch das, was die Vorredner schreiben. Kleine Korrektur zu dots Beitrag: Es werden nicht die ersten array_num Elemente gelöscht, sondern nur das eine an Position array_num.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

5

08.04.2013, 20:20

[ Andere Implementierungen schrumpfen ab und zu, nämlich immer wenn ein gewisser Grenzwert an unbenutztem Speicherplatz überschritten wird.
Kennst du da konkrete Implementierungen? Soweit ich weiss sagt der Standard, das nie geschrumpft wird.

6

08.04.2013, 21:12

Nach dem erase() ist das Element nicht mehr im Vektor, das was da deletet wird, ist irgendetwas unsinniges oder existiert nicht und führt direkt zum Crash.
Wird nicht einfach das nächste Element hinter array_num deleted, sofern es das gibt? Denn im Prinzip greift man ja noch mal auf array_num zu und wenn man das vorher schon gelöscht hat und der ganze Container eins nach vorn rückt...

Wie wäre denn dann die Lösung für das Problem? Reicht es, den vector<*T> zu einem vector<unique_ptr<T>> zu machen?

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

08.04.2013, 21:16

Wie wäre denn dann die Lösung für das Problem? Reicht es, den vector<*T> zu einem vector<unique_ptr<T>> zu machen?

Möglicherweise, aber wenn du nach einer Lösung für ein Problem suchst, würd's nicht schaden, erstmal zu erklären, was genau eigentlich das Problem ist... ;)

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

8

08.04.2013, 21:20

[ Andere Implementierungen schrumpfen ab und zu, nämlich immer wenn ein gewisser Grenzwert an unbenutztem Speicherplatz überschritten wird.
Kennst du da konkrete Implementierungen? Soweit ich weiss sagt der Standard, das nie geschrumpft wird.

Da hast du tatsächlich Recht, der Standard verbietet bei std::vector::erase() praktisch jegliche Reallokation (konkret dürfen Iteratoren und Referenzen vor dem gelöschten Element nicht invalidiert werden; da mir keine zuverlässige Möglichkeit bekannt ist, Speicherbereiche ohne Verschiebung zu verkleinern, schließt das Reallokation in der Praxis aus). Eine Entscheidung, die mich ein wenig überrascht, die Komplexität in vielen Fällen jedoch sicher angenehm verringert.

Somit ist Reallokation bei Benutzung eines std::vectors an dieser Stelle tatsächlich ausgeschlossen (gerade GCCs, clangs und STLPort-Implementierungen gecheckt, halten sich alle daran). Dennoch muss das bei vom Standard unabhängigen Container-Klassen von Drittanbietern nicht immer der Fall sein, also schön aufpassen. :)
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »CodingCat« (08.04.2013, 21:26)


9

09.04.2013, 12:10

Möglicherweise, aber wenn du nach einer Lösung für ein Problem suchst, würd's nicht schaden, erstmal zu erklären, was genau eigentlich das Problem ist... ;)
Das Problem ist, diese Delete-Operation sinnvoll auszuführen ^^
Also ein über nen Index spezifiziertes Element aus dem Vektor zu löschen und die unter dem Zeiger liegenden Daten wieder freizugeben.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

10

09.04.2013, 12:24

Lösche zuerst und erase dann. Und außerdem: bool weist man true oder false zu, eine "1" ist einfach nur dumm, auch wenn der Compiler es für Dich in "true" umwandelt. Außerdem: mach Funktionen nicht "virtual", wenn Du nicht vorhast, sie in abgeleiteten Klassen zu überschreiben. Das verkompliziert die Handhabung für den Compiler nur sinnlos. Außerdem: wenn Du array_num gegen die Arraygröße testest, solltest auch der zweite Zugriff mittels array_num von dem Test betroffen sein. Beide Tests sind aber Quatsch, es sollten assert() sein.

Aber immerhin benutzt Du "size_t" für den Array-Index, das ist gut so.

So müsste es aussehen

Quellcode

1
2
3
4
5
6
7
8
void Delete( size_t array_num, bool delete_data = true )
{
  // zuerst das Element löschen
  if( delete_data )
    delete objects[array_num];
  // dann den (jetzt ungültigen) Zeiger entfernen
  objects.erase( objects.begin() + array_num);
}


Wie Du siehst, habe ich auch den überflüssigen Rückgabewert und das "virtual" entfernt. Erfolg oder Fehler macht man nur per Rückgabewert, wenn der Fehlerfall erwartet wird und akzeptabel ist. Fehlerbedingungen, die eigentlich nie auftreten sollten, prüft man mit assert(). Fehlerbedingungen, die eigentlich nie auftreten sollten, aber aus externen Quellen vorkommen, die man nicht absichern kann (wie z.B. Daten aus gelesenen Dateien) behandelt man mit dem Werfen einer Ausnahme.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

Werbeanzeige