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

30.04.2011, 15:09

Vector Problem

Hallo alle zusammen,
ich hab schon seit längerem ein Problem, das ich einfach nicht in den Griff kriege. Für mein 2D-Spiel versuche ich nun schon seit geraumer Zeit Gegner zu implementieren. Die Gegner-Klasse funktioniert so weit auch schon ganz gut. Nun brauche ich aber mehrere Gegner gleichzeitig. Dafür will ich einen std::vector verwenden, aber irgendwie funktioniert es nicht. Das Spiel stürzt immer ab. Leider schmiert der Debugger auch immer gleich ab, was das ganze noch ein wenig verkompliziert. Deshalb hab ich ein Minimal Code Beispiel von meinem Problem erstellt. Ich wäre sehr froh, wenn sich jemand das ganze mal ansehen könnte und mir sagen könnt, was ich falsch mache, weil ich mittlerweile wirklich keine Idee mehr habe, was falsch läuft.
Das Programm verwendet die SFML und einige Hilfsklassen von deren Wiki. Die habe ich in den Ordner helpers gepackt, weil sich denk ich mal mit relativer Sicherheit ausschließen lässt, das der Fehler da liegt. Die beiden interessanten Dateien sind main.cpp und gnome.cpp. Wenn da Makro SINGLE_GNOME gesetzt wird nur ein Gnome über eine normalen Pointer erstellt, um zum Beispiel die Gnomeklasse zu testen (ich fand das auf die schnellen einfacher als immer alles auszukommentieren). Bitte keine fiesen Kommentare über Sauberkeit oder Schönheit des Codes... der dient wirklich nur zur Demonstration des Problems :D
Hier der Downloadlink. Bitte helft mir! Ich hab wirklich keine Idee mehr woran es liegen könnte.

Edit: Das ganze wurde mit CodeBlocks erstellt, aber es sollte kein Problem sein das ganze auch mit jeder anderes IDE zu verwenden.

BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

2

30.04.2011, 15:25

std::vector::push_back() erwartet eine Referenz auf ein Objekt.
m_GnomeVector.push_back(CGnome(0.23f, true)); übergibt dem Vector eine Referenz auf ein Objekt (CGnome), das nur innerhalb des Funktionsaufrufs gültig ist. Nach dem Funktionsaufruf wird das anonyme Objekt, das du als Parameter übergeben hast gelöscht. Die Referenz im Vector zeigt nun auf ein ungültiges Objekt.

Du solltest deine Gnome besser mit Zeigern und new verwalten/erstellen.

jokester

Treue Seele

Beiträge: 125

Wohnort: Mainz

  • Private Nachricht senden

3

30.04.2011, 15:35

std::vector::push_back() erwartet eine Referenz auf ein Objekt.
m_GnomeVector.push_back(CGnome(0.23f, true)); übergibt dem Vector eine Referenz auf ein Objekt (CGnome), das nur innerhalb des Funktionsaufrufs gültig ist. Nach dem Funktionsaufruf wird das anonyme Objekt, das du als Parameter übergeben hast gelöscht. Die Referenz im Vector zeigt nun auf ein ungültiges Objekt.

Du solltest deine Gnome besser mit Zeigern und new verwalten/erstellen.

Das ist eher Quatsch ;) Der vector kopiert den übergebenen Parameter intern natürlich und sorgt selbst für die Speicherverwaltung. Leider weiß ich aber auch nicht, wodran es bei dir liegt, hab auch gerade nicht die Möglichkeit zum testen.
"There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened" -- Douglas Adams.

4

30.04.2011, 15:40

Vielen Dank für die schnelle Antwort. Könntest du mir an eine Codebeispiel zeigen was genau du meinst?

Quellcode

1
2
3
4
5
6
typedef std::vector<CGnome*> GnomeVector;
...

m_GnomeVector.reserve(2);
for(int i = 0; i < 2; i++)
    m_GnomeVector.push_back(new CGnome(0.23f, true));

Etwa so oder anders? Ich hab noch nicht soviel Erfahrung mit Vektoren. Irgendwo hatte ich mal gelesen, das man besser Objekte anstatt Zeigern verwenden sollte, weil es einfacher und unter Umständen auch sicherer seinen kann.

BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

5

30.04.2011, 15:41

Stimmt, habe übersehen, dass er die Daten nicht als Referenz, sondern als Objekt im Vector speichert, habe irgendwie an vector<CGnome&> gedacht... Aber die Objekte immer zu kopieren ist noch größerer Unsinn (Performancegründe), auch wenn es funktionieren sollte. Vielleicht liegt ein Problem mit einem Kopierkonstruktor deiner Klasse CGnome oder einer Klasse der SFML vor?

jokester

Treue Seele

Beiträge: 125

Wohnort: Mainz

  • Private Nachricht senden

6

30.04.2011, 15:43

Da referenzen keine Objekttypen sind wäre ein vector<CGnome&> nichtmal möglich. Und die Objekte zu kopieren kann sinnvoll sein, ein new ist manchmal ungleich teurer... Am optimalsten sind wir da natürlich in C++0x mit move dran. n Fehler im Copyctor klingt aber logisch. Probier die variante mit new ruhig mal aus, wenn die klappt hast du wohl wirklich dadrin Mist gebaut ^^
"There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory which states that this has already happened" -- Douglas Adams.

7

30.04.2011, 16:02

Ich hab nochmal ein wenig herum probiert und es funktioniert, wenn ich folgendes schreibe:

C-/C++-Quelltext

1
2
3
4
5
6
typedef std::vector<CGnome*> GnomeVector;

GnomeVector m_GnomeVector;
m_GnomeVector.reserve(10);
for(int i = 0; i < 10; i++)
    m_GnomeVector.push_back(new CGnome(0.23f, true));

Allerdings habe ich nun zum aufrufen der Draw funktion dieses unschöne ding: App->Draw(*(*i));
Das mit dem Copyconstuctor habe ich nicht verstanden. Ich hab keine Copyconstructor definiert und der wird ja auch nicht verwendet. was sollte daran falsch sein?
Meint ihr ich kann das ganze jetzt so belassen? bekomme ich auch keine Problem, das mein Zeiger, wenn der Vector sich verschiebt ungültig werden? Oder von der Performance?

BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

8

30.04.2011, 16:15

m_GnomeVector.reserve(10); ist unnötig, da sich STL-Container automatisch vergrößern/verkleinern.
App->Draw(*(*i)); könnte glaube ich zu App->Draw(**i); geändert werden.
Selbst wenn du keinen Kopierkonstruktor definierst, wird beim Kopieren eines Objekts der Standardkopierkonstruktor aufgerufen, der den Inhalt der Variablen eines Objekts 1:1 kopiert. Bei trivialen Datentypen, wie Integer funktioniert das. Bei Zeigern, die auf einen bestimmten Speicherplatz zeigen, gibt es Probleme, da der Standardkopierkonstruktor keinen neuen Speicherplatz für die kopierten Zeiger reserviert, sondern beiede Zeiger auf das Objekt der einen Klasse zeigen würden.
Du musst die Objekte deines Vectors vor dem Aufruf von vector::clear() mittels delete löschen.

9

30.04.2011, 16:24

Achso ok dann nehm ich reserve raus.
Ich dachte clear() würde den gesamten Speicher automatisch freigeben... Muss ich also vor clear noch einmal durch den vector iterieren und alle Objekte mit delete löschen?
Wie verhindere ich, dass der Kopierkonstruktor für die Zeiger beim kopieren keinen neuen Speicher anlegt? Wie müsste der Copyconstructor aussehen?

BurningWave

Alter Hase

Beiträge: 1 106

Wohnort: Filderstadt/Konstanz

Beruf: Student

  • Private Nachricht senden

10

30.04.2011, 16:44

Ich dachte clear() würde den gesamten Speicher automatisch freigeben... Muss ich also vor clear noch einmal durch den vector iterieren und alle Objekte mit delete löschen?

Ja musst du, da der Vector nur Zeiger enthält, deren Speicherplatz mit clear gelöscht wird. Der Speicherplatz, auf den die Zeiger zeigen, bleibt aber unberührt.

Das mit dem Kopierkonstruktor ist in deinem Fall entwas komplizierter, da wir nicht wissen, was die SFML Klassen intern machen. Grundsätzlich kann man aber den Compiler nicht anweisen, dass er das automatisch regelt. Mann muss, wenn man eine Klasse mit Zeigern hat, genau überprüfen, ob es notwendig ist, einen Kopierkonstruktor zu schreiben.
Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
class foo 
{ 
public: 
   foo();
   ~foo();
   foo(const foo&); {pointer = new xyz; *pointer = *foo.pointer;} //copy-ctor (wenn privat deklariert, kann das Objekt nicht kopiert werden)
   //... 
   xyz* pointer;
   //...
};

Werbeanzeige