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

23.01.2014, 00:17

C++ Zeiger und Funktionsübergreifen / verständnis Frage

Hallo Erstmal,

und zwar bin ich seit einigen Wochen dabei meine c++ Kenntnisse wieder auf zu frischen.

Ich programmiere derzeit ein kleines Spiel um vor allem die Objekt orientierte Programmierung zu erlernen.

Nun habe ich nach ein paar Tagen Kopfzerbrechen eine Lösung gefunden. Aber ich verstehe da etwas noch nicht ganz. Und vielleicht könnt Ihr mir da weiter helfen.

Meine Ziel war es, eine Texture innerhalb einer Funktion zu laden, den Zeiger auf die Texture zurück zu geben. So das ich nachher beim Sprite einfach nur noch den Zeiger verwende um die Texture zu nutzen. Das ganze hat auch wunderbar geklappt und einen enormen Geschwindigkeitschub gegeben :)

Ich vermute es liegt daran, das ich vorher immer die Texture neu geladen hatte. Unten angehängt der Quellcode der relevant ist. Wichtig dabei ist, das ich die Zeiger Adressen an Zwei verschiedenen Positionen habe Anzeigen lassen um zu sehen ob sie auch dort ankommen. Also einmal in der Klasse und einmal im main().

Das Ergebnis war aber:

In der main() 0101EF40

In der Klasse 0101F130

Ich dachte die Adressen müssen gleich sein oder liegt da mein Fehler ?

Vielen Dank schon mal :)





C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Background.h 

class Background 
{ 
public: 
Background(); 
virtual ~Background(); 
void display(sf::RenderWindow &Spiel, sf::Texture &Hintergrundbild); 
void bewegen(); 
sf::Texture init(); 

private: 
sf::Sprite Sprite1_Hintergrund; 
sf::Sprite Sprite2_Hintergrund;sf::Sprite Sprite3_Hintergrund; 
sf::Texture *m_Hintergrundbild;float m_Backgroundmove = 0.0f; 
};


C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Background.cpp 

void Background::display(sf::RenderWindow &Spiel, sf::Texture &m_Hintergrundbild) 
{ 
sf::Sprite Sprite1_Hintergrund(m_Hintergrundbild); 
sf::Sprite Sprite2_Hintergrund(m_Hintergrundbild); 
sf::Sprite Sprite3_Hintergrund(m_Hintergrundbild); 

Sprite1_Hintergrund.setPosition(0, -540.0f + m_Backgroundmove); 
Sprite2_Hintergrund.setPosition(0, m_Backgroundmove); 
Sprite3_Hintergrund.setPosition(0, 540.0f + m_Backgroundmove); 

Spiel.draw(Sprite1_Hintergrund); 
Spiel.draw(Sprite2_Hintergrund);Spiel.draw(Sprite3_Hintergrund); 
} 
sf::Texture Background::init() 
{ 
sf::Texture *m_Hintergrundbild;m_Hintergrundbild = new sf::Texture; 
m_Hintergrundbild->loadFromFile("sterne2.jpg"); 
std::cout << "Funktion:" << &m_Hintergrundbild << "\n"; // Adressanzeigen zum Überprüfen 
return sf::Texture(*m_Hintergrundbild); //Rückgabe des Zeigers für die Texture 
}


C-/C++-Quelltext

1
2
3
4
5
6
main.cpp 

sf::Texture Zeiger_background = Background.init(); // hier übergebe ich denn Zeiger 
std::cout << "main:" << &Zeiger_background << "\n"; 

Background.display(Spiel, Zeiger_background);

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

2

23.01.2014, 01:07

In Background::init() (wäre für sowas nicht eigentlich der Konstruktor da!?), erzeugst du hier

C-/C++-Quelltext

1
return sf::Texture(*m_Hintergrundbild);
eine Kopie von m_Hintergrundbild. Die Funktion gibt dann diese Kopie zurück und in main kopierst du diese Kopie in Zeiger_background. Nachdem es sich um eine Kopie handelt, hat Zeiger_background (ist übrigens kein entgegen seines Namens kein Zeiger), natürlich eine andere Adresse...

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »dot« (23.01.2014, 01:19)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

3

23.01.2014, 07:06

Eine sf::Texture sollte man übrigens am besten nur per Referenz oder per Pointer umherreichen. Kopien tun wirklich genau das, sie kopieren die Textur auf der CPU *und* auf der GPU. Damit leaked man nicht nur Speicher wie blöd (oder belegt welchen, der zwar irgendwann freigegeben, aber aktuell unnütz doppelt und dreifach verbraten wird), sondern man holt sich damit auch ein ganz übles Performance-Problem in's Haus, weil sie eine Kopie eine sehr teure Operation ist.
Mein persönlicher Vorschlag dazu lautet eine eigene Klasse von sf::Texture abzuleiten und diese non-copyable zu machen. Dann hat man das Problem nicht oder bekommt sofort Compiler-Errors, wenn man es doch versehentlich irgendwo macht.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

4

23.01.2014, 13:23

Das ist ja etwas, was leuten regelmäßig passiert. Hat sich der SFML-Autor zu diesen Thema geäußert? Da wäre ein explizites Clone() in Ordnung in Verbindung mit sf::NonCopyable.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

23.01.2014, 14:05

Nun, rein logisch betrachtet macht es keinen Sinn ein Clone anzubieten, wenn es nicht kopierbar sein soll. Denn entweder soll man es kopieren können oder eben nicht. Wenn man es kopieren können soll, dann macht der Copy-Constructor am meisten Sinn. Dass das fehleranfällig ist, ist letztlich nicht das Problem von Laurent, sondern von Neulingen und ungeübten Programmierern.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

6

23.01.2014, 14:20

Wenn aber so viele drüber stolpern, dann finde ich das implizite kopieren ziemlich schlecht. Vor allem, ein kopieren sieht man in C++ finde ich sehr schlecht, vor allem in kombination mit auto:

C-/C++-Quelltext

1
auto tex = GetTexture();


In Abhängigkeit davon, ob GetTexture() jetzt einen Pointer oder das Objekt liefert, wird entweder nur der Pointer oder das gesamte Objekt kopiert. Das finde ich ziemlich Schade. Und mal so ziemlich deutlich von dir auch gesagt: Das kopieren von Ressourcen ist ziemlich der Ausnahmefall und kann doch daher verboten werden. Dann muss man halt explizit Clone() aufrufen und ist sich sicher, dass alles klappt. Wobei wenn ich länger darüber nachdenke, funktioniert das gar nicht gut in der SFML-Architektur, weil sehr viel mit Objekten und Referenzen an statt mit Smartpointern gearbeitet wird. Das wäre ja ein ziemlicher Stilbruch...

patrick246

Treue Seele

Beiträge: 328

Wohnort: nahe Heilbronn/BW

Beruf: TG Profil Informatik-Schüler

  • Private Nachricht senden

7

23.01.2014, 17:29

Wenn GetTexture eine Referenz zurückliefert, ist tex dann nicht auch eine Referenz?

8

23.01.2014, 23:23

Hi Leute,

vielen Dank für die Antworten. Doch mich verwirrt das ganze immer mehr, ich weiss nicht woran es liegt, ich dachte eigentlich das ich es verstanden habe :dash:

Immerhin habe ich das Buch an machen Stellen mehrmals lesen müssen :)


So sieht das mit dem Zeiger in meinem Buch aus :) Das gibt es ja dann auch für Klassen, Structuren...etc.

C-/C++-Quelltext

1
2
int *pPunkte = NULL; // Zeiger auf "Punkte" 
pPunkte = &Punkte; // Zeiger initialisieren

Nun dachte ich mir...das hier unten ist doch auch ein Zeiger...also ich erzeuge eine Instanz auf dem heap und packe das in meinen Konstruktor von der Background.cpp

C-/C++-Quelltext

1
2
sf::Texture *m_Hintergrundbild; 
m_Hintergrundbild = new sf::Texture;

So und hiermit rufe ich dann eine Member Funktion der Klasse auf und laden das Bild :

C-/C++-Quelltext

1
m_Hintergrundbild->loadFromFile("sterne2.jpg");


Alles wunderbar dachte ich mir...nun noch die Member variable deklarieren.... wie immer in der Background.h

C-/C++-Quelltext

1
2
private: 
sf::Texture *m_Hintergrundbild;

Soweit so gut...jetzt dachte ich das ich innerhalbe meiner Klasse diese Instanz überall verwenden kann. Aber auch das lief wieder mal schief...um zu sehen was genau passiert... habe ich mal folgendwerte abgefragt und mir anzeigen lassen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
Und zwar innerhalb des Konstruktor...wo das Bild auch geladen wird 
Background::Background() 
std::cout << "Konstruktor:" << &m_Hintergrundbild <<"\n"; 
std::cout << "Konstruktor:" << m_Hintergrundbild << "\n"; 

Und einmal in einer Member Funktion...die denn Sprite Zeichen soll. 
void Background::display(sf::RenderWindow &Spiel) 
std::cout << "Display:" << &m_Hintergrundbild << "\n"; 
std::cout << "Display:" << m_Hintergrundbild << "\n";


Das Ergebnis war wieder eine Katastrophe:
Konstruktor: &m_Hintergrundbild = 00D9F220
Konstruktor: m_Hintergrundbild = 04CED4A8

Konstruktor: &m_Hintergrundbild = 00D9F874
Konstruktor: m_Hintergrundbild = CCCCCCCC


Also mal unabhängig ob die Funktion so sinnvoll ist oder nicht. Wo liegen da meine gedanken Fehler ich habe schon das ganze Internet durchsucht, aber es gibt auch keine wirklich Antwort da drauf.

Ich möchte doch nur ein Bild laden. Und diese Funktionen möchte ich von den anderen innerhalb der Klasse abkapseln, damit es nicht immer wieder geladen wird, sondern schon im Speicher vorliegt.

Zum anderen möchte ich natürlich das mit den Zeiger und Referenzen auch verstehen. Wobei ich das bei den kleinen Beispielen in dem Buch auch nachvollziehen kann....aber ich bekomme es anscheinend bei Klassen nicht hin mit Zeiger und Referenzen zu arbeiten.


Wie immer schon mal danke für die guten Hinweise :)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

9

24.01.2014, 07:49

Du wirst schon den ganzen Code aus der .h und der .cpp zeigen müssen. Meine Vermutung ist aber, dass du "m_Hintergrundbild" einmal im Header und einmal im Konstruktor deklarierst und das wäre natürlich fatal.
Die Adresse von m_Hintergrundbild abzufragen ist übrigens ziemlich sinnlos, weil m_Hintergrundbild ja bereits ein Pointer ist und Du damit eigentlich nur indirekt rausbekommst, wo das besitzende Objekt (also Background) im Speicher liegt (plus dem Offset von m_Hintergrundbild selbst natürlich). Diese Info ist aber relativ nutzlos. Was wirklich interessant ist, ist ja nur die Adresse, wohin er zeigt.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

10

24.01.2014, 11:32

Hallo BlueCobold,


Der tip war genau richtig. Ich habe heute nochmal ein test setup gemacht um wirklich alles aus zu schliessen und habe deinem tip mal nach gegangen. Das Problem kam wie folgt zu Stande.

Ich hatte vorher den gesamten teil zu Darstellung des Hintergrundes vom image laden bis zur Übergabe in den Sprite, alles unter einer Funktion.

Da ich ja nur das laden aus dieser Funktion raus haben wollte und in den Konstruktor gepackt habe. und damit fing eigentlich das Chaos an.

Also eigentlich hatte ich vorher als alles unter einer Funktion hatte, die Deklaration direkt in der Funktion. Nun hatte ich diesen teil in den Konstruktor gepackt und da die Instanz Texture ja weiterhin noch in der Funktion benötigt wurde, wurde natürlich vom compiler angemeckert, das die Instanz nicht deklariert war.

Genau das habe ich dann getan :D

Was das nächste Problem zur folge hatte. es gab einen totalen crash.

Diesen Crash konnte ich dann erstmal nur verhindern, in dem ich die Textur nicht mehr dem Sprite übergebe. Und danach habe ich mich zu sehr darauf konzentriert und auszulesen was in den einzelnen variablen für werte hinterlegt werden.

Weiterhin habe ich dadurch aber auch gelernt, das eine Doppelte Deklaration hier ein Problem darstellt. Sie aber kein Problem darstellt, wenn ich sie im Background.h mache und in einer Funktion der background.cpp. In diesem Fall gibt es weder eine Fehlermeldung und das Bild wird auch korrekt angezeigt.

Wieso passiert das nur im Zusammenhang mit dem Konstruktor ?


Danke und Gruss


Werbeanzeige