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

viti

Frischling

  • »viti« ist der Autor dieses Themas
  • Private Nachricht senden

1

06.02.2015, 15:27

[c++] Speicherleck?

Ich bin gerade dabei, mein erstes kleines Spiel zu machen und hätt ein Haufen Fragen. Eine, die ich grad nicht selber lösen kann, betrifft mögliche Speicherlecks.

Ganz kurz zur Situation: Ich hab ein Raumschiff, das schiessen kann. Raumschiff und Schuss haben eine eigene Klasse (class Ship und class Shot). Die Klasse Schiff hat als Membervariable einen Vektor aus Zeigern auf Schüsse. Also ganz grob so:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
class Shot {
  ...
};

class Ship {
private:
  ...
  vector<Shot*> Shots;
  ...
}


Wenn das Schiff schiesst, wird innerhalb von einem Switch ein neuer Schuss erstellt und in den Vektor geschoben:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
case SDLK_f:
    Shot* NewShot;
    NewShot = new Shot;
    NewShot->SetRenderer(_pRenderer);
    NewShot->SetPos(_x + 1.1*_w*sin(Rad(_alpha)), _y + 1.1*_w*cos(Rad(_alpha)));
    NewShot->SetVelocity(_vx, _vy);
    NewShot->Load("Textures\\Fire.png");
    _Shots.push_back(NewShot);
      cout << "New Shot fired. There are " << _Shots.size() << " Shots in the vector." << endl;
      break;


Wenn der Schuss dann mal was trifft oder wenn er zu weit aus dem Spielfeld geflogen ist, möchte ich ihn mit _Shots.erase(pos) wieder löschen.

Frage: Ist das eine saubere Lösung? Oder muss ich am Ende von 'case SDLK_f' den NewShot mit delete grad wieder löschen? Ich denk mir, Variablen, die innerhalb einer Funktion erstellt werden, kann ich nach dem Funktionsaufruf vergessen, aber ist das auch bei Zeigern so?

2

06.02.2015, 15:34

Am Ende des Case musst du ihn nicht löschen, du hast ja noch einen Zeiger im vector und willst ja auch später nochmal darauf zugreifen. Bevor du ihn aus dem vector löscht musst du allerdings ein delete aufrufen. Sinnvoller wäre hier allerdings ein unique_ptr:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
#include <memory>

// ...
std::vector<std::unqiue_ptr<Shot>> shots;

// ...
Shot* NewShot = new Shot;
shots.emplace_back(NewShot)
"Theory is when you know something, but it doesn’t work. Practice is when something works, but you don’t know why. Programmers combine theory and practice: Nothing works and they don’t know why." - Anon

3

07.02.2015, 02:07

Ja, unique_ptr sind klasse. Solltest du echt überall benutzen, wo es möglich ist, denn dann brauchst du dir um Speicherlöcher keine Gedanken mehr zu machen.

In modernem C++ kommt praktisch nie new oder delete vor. Um ein neues Objekt zu erzeugen benutzt man stattdessen in der Regel make_unique was im wesentlichen genau wie new funktioniert, dir aber direkt einen unique_ptr liefert.

Außerdem solltest du dir vermutlich noch move ansehen. [Hinweis von Nimelrian] Falsches move, das hier ist das richtige: move [/Hinweis von Nimelrian] Oder am besten nach irgendwelchen Artikeln suchen, die all das etwas ausführlicher erklären. Fragen zur Verwendung kannst du aber auch hier stellen (am besten mit Codebeispiel, wie du es probiert hast und mit Erläuterung, was nicht funktionierte).
Lieber dumm fragen, als dumm bleiben!

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Jonathan_Klein« (07.02.2015, 20:08)


KeksX

Community-Fossil

Beiträge: 2 107

Beruf: Game Designer

  • Private Nachricht senden

4

07.02.2015, 02:20

Das new gibt es weiterhin, nur das delete spart man sich. (Hab die selbe Aussage auch schonmal gemacht :D)
WIP Website: kevinheese.de

5

07.02.2015, 10:47

Ja, unique_ptr sind klasse. Solltest du echt überall benutzen, wo es möglich ist

Absolut richtig :)

In modernem C++ kommt praktisch nie new oder delete vor.

Da muss ich dir leider etwas widersprechen: Es gibt durchaus Fälle in denen das vorkommen kann. Herb Sutter hat das in seinem Blog gut formuliert:

Zitat

Always use the standard smart pointers, and non-owning raw pointers. Never use owning raw pointers and delete, except in rare cases when implementing your own low-level data structure (and even then keep that well encapsulated inside a class boundary).

Kurz: Wenn man weiß was man tut, sind raw pointer Implementierung-spezifisch durchaus sinnvoll wenn man auf die class boundary achtet.

LG

6

07.02.2015, 11:17

Das new gibt es weiterhin, nur das delete spart man sich. (Hab die selbe Aussage auch schonmal gemacht :D)


Naja, ein make_unique ist einfach weniger Code als ein explizites Aufrufen von new. Und man hat halt keinerlei Vorteil davon, new selber aufzurufen, im Gegenzug dazu könnte man aber wieder Speicherlöcher bekommen, wenn irgendetwas zwischen "Objekt erstellen" und "Objekt in unique_ptr packen" passiert. Es ist natürlich eine Umgewöhnung, auf new zu verzichten, aber ich sehe keinerlei Vorteil es irgendwie noch beizubehalten. Wobei das natürlich eine Stil-Regel ist und kein unumstößliches Gesetz.

Zitat

Kurz: Wenn man weiß was man tut, sind raw pointer Implementierung-spezifisch durchaus sinnvoll wenn man auf die class boundary achtet.

Jup. Aber wenn man weiß was man tut, sind eh so ziemlich alle best-practice Regeln hinfällig, Ausnahmen gibt es ja immer. Ich war nur nicht auf sowas eingegangen, da es doch relativ selten vorkommt und ich insbesondere denke, dass jemand der Fragen zu SPeicherlöchern hat noch nicht unbedingt spezielle low-level Module schreibt.
Lieber dumm fragen, als dumm bleiben!

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

7

07.02.2015, 11:49

Wieso nicht einfach direkt einen vector aus Shots machen, anstatt die Shots alle per new zu allokieren und einen vector aus Zeigern auf diese zu machen? ;)

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

8

07.02.2015, 12:08

Allerdings kann man keine netten Forward Declarations mehr machen, wenn man std::unique_ptr benutzt.
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

9

07.02.2015, 12:39

Wieso nicht einfach direkt einen vector aus Shots machen, anstatt die Shots alle per new zu allokieren und einen vector aus Zeigern auf diese zu machen? ;)

Hach, ich wollte jetzt gerade etwas darüber schreiben, wie kniffelig es sein kann, Objekte richtig zu kopieren, bis mir eingefallen ist, dass man sie heutzutage in den meisten Fällen nur noch 'moven' müssen wird (was immer relativ einfach ist). Hach, alte Gewohnheiten.

Aber ein echtes Gegenargument hätte ich evtl. doch: Wenn man irgendwo eine Referenz auf ein Objekt im vector haben will, möchte man vielleicht nicht, dass sich die Speicherposition dauern ändert. Andererseits muss man natürlich auch sagen (wo wir das hier schon so schön diskutieren), dass überall im Hauptspeicher verteilte, dynamisch allozierte Objekte sehr schlecht für sämtliche Caches sind. Wenn es also wirklich auf Geschwindigkeit ankommt, wird man das vermeiden wollen. Wenn man aber mal 100 Schüsse irgendwo in einem Vector hat, dürfte man das kaum merken.

Zitat

Allerdings kann man keine netten Forward Declarations mehr machen, wenn man std::unique_ptr benutzt.


Forward Declarations sollten in den meisten Fällen tatsächlich ausreichen. Tun sie jedenfalls bei mir.

https://stackoverflow.com/questions/6012…definition-of-t
Lieber dumm fragen, als dumm bleiben!

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

10

07.02.2015, 12:44

Ah, stimmt, da war ja was. Danke für die Auffrischung. :)
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

Werbeanzeige