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

primat

Frischling

  • »primat« ist der Autor dieses Themas

Beiträge: 38

Wohnort: Hamburg

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

1

08.04.2013, 11:32

Programmier-Stil

Ich habe das Buch von Heiko Kalista nun komplett durchgearbeitet. Verständnisschwierigkeiten hab ich nicht, da ich schon seit Jahren programmiere, vor allem in Java. Habe das Buch hauptsächlich genutzt, um mein gefährliches Halbwissen bezüglich C++ zu vervollständigen.
Nun stellen sich mir aber einige Fragen zum guten Programmier-Stil, vor allem was Zeiger, Referenzen und Co betrifft. Da das in Java überhaupt keine Rolle spielt, bin ich mir etwas unsicher. Also hier konkrete Fragen:

Wann Zeiger benutzen?
In Java ist es ja ganz einfach: Eingebaute Datentypen wie int werden einfach initialisiert (int zahl = 3; ), Instanzen von Klassen werden immer mit new erzeugt (Entity entity = new Entity(); ).
In C++ habe ich nun zwei verschiedene Möglichkeiten eine Instanz zu erzeugen: "Normal" (Entity entity; ) oder mit new (Entity *entity = new Entity). Außerdem gibts drei verschiedene Möglichkeiten, Variablen als Parameter zu übergeben (Normal, Zeiger, Referenz). Das ist noch etwas verwirrend für mich, wann welche Technik zum Einsatz kommen sollte.
Zufällig bin ich auf diese Folien gestoßen, die bei jemandem hier aus dem Forum in der Signatur verlinkt waren: http://dl.dropbox.com/u/6101039/Modern%20C%2B%2B.pdf
So wie ich das verstehe, wird dort generell von der Verwendung von Zeigern abgeraten. Warum? Sollte ich einfach immer die "normale" Instanziierung benutzen? Und was genau sind diese shared_pointer bzw unique_pointer?

Init-Funktionen?
In dem SDL-Beispiel benutzt der Autor überall Init-Funktionen, die sofort nach dem Erstellen des Objekts aufgerufen werden. Was ist der Sinn dahinter? Ich würde sowas in Java immer im Konstruktor erledigen. Gibt's dafür in C++ einen besonderen Grund? Soll das guter Stil sein oder ist das schlicht Unsinn?

Singletons?
Hat jetzt eigentlich nichts mit C++ zu tun, sondern allgemein mit Programmier-Stil. Der Autor verwendet im SDL-Beispiel zwei Singleton-Klassen: Timer und Framework. Ich denke, man könnte beide auch als normale Klassen implementieren. Oder spricht was dagegen?

Ich hätte einfach gerne ein paar Meinungen dazu!

TigerClaw25

unregistriert

2

08.04.2013, 12:33

Zum Thema Singletons könnte man sicher auch normale Klassen verwenden. Aber Zum einen benötigst du lediglich ein Objekt von jeder Klasse und zweitens kannst du diese überall verwenden bzw aufrufen, ohne irgendwas global zu halten oder mit "extern" zu arbeiten.

Würdest du darauf verzichten, müsstest du das, soweit iuch verstanden habe, global machen, was sicherlich nicht unbedingt einfacher bzw. übersichtlicher ist.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

3

08.04.2013, 12:51

Zum Thema Singletons könnte man sicher auch normale Klassen verwenden. Aber Zum einen benötigst du lediglich ein Objekt von jeder Klasse und zweitens kannst du diese überall verwenden bzw aufrufen, ohne irgendwas global zu halten oder mit "extern" zu arbeiten.
Das sind allerdings weder Argumente, noch gute Gründe zur Verwendung von Singletons.
Es geht bei Singletons nämlich nicht darum, dass man genau ein Objekt einer Klasse braucht, sondern dass man in keinem Fall mehr als eins haben darf! Das ist hier sicherlich nicht der Fall.
Zweitens sollten Singletons niemals scheinbarer OOP-Ersatz für global oder extern sein. Das ist schlechtes Design und ein sehr schlechtes Konzept.

primat hat mit seinen Fragen die offensichtlichen Schwachstellen sehr gut erkannt. Man sollte keine Init-Funktionen nutzen, sondern den Konstruktor und man sollte die Timer und das Framework nicht als Singleton anlegen.
Die Verwendung von Pointern ist etwas trickreicher. Müssen Objekte dynamisch erzeugt werden und geht das nicht lokal im Stack, muss eben der Heap dafür herhalten. shared und unique-Pointer kümmern sich hierbei um das automatische Freigeben des Speichers, sobald die Eigentümer ihre Zerstörung durchlaufen. Wahlweise kommt man um Pointer nicht gut herum, wenn man Objekte non-copyable machen will oder muss. Ein Shared-Pointer kommt dabei einem Java-Objekt ziemlich nahe. Solange jemand eine "Referenz" hält, bleibt es am Leben. Ein Unique-Pointer hingegen hat nur genau einen Eigentümer und der Pointer. Wird dieser Owner zerstört, sind alle Referenzen auf das Kind-Objekt ungültig. Hier ist indirekt der Unterschied zwischen Aggregation und Komposition nachgebildet.
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]

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »BlueCobold« (08.04.2013, 13:01)


Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

4

08.04.2013, 12:57

Instanzen:
Letztendlich gibt es nur eine Möglichkeit, Instanzen zu erzeugen - man ruft den Konstruktor auf. Der Unterschied zwischen den verschiedenen Arten der Speicherung ist, dass entweder der Stack oder der Heap verwendet wird. Wenn man etwas auf dem Heap (mit new) anlegt, muss dies später auch wieder (mit delete) freigegeben werden. Ich habe das Buch nicht gelesen, aber eigentlich sollte das und bspw. der Unterschied zwischen Pointern und Referenzen beschrieben werden. Wichtig: Java verwendet für Instanzen Referenzen (oder etwas, was am ehesten mit C++ Referenzen zu vergleichen ist)

Ich würde grundsätzlich von Singletons abraten. Immer, wenn man Singletons denkbar sind, gibt es garantiert auch einen anderen Weg der Lösung. Und sollte die globale Zugreifbarkeit tatsächlich ein Grund für einen Singleton sein, sollte man sein Design grundsätzlich nochmal überdenken. (Mal abgesehen davon, dass man nur sehr wenigen Ausnahmefälle gibt, in denen das Erzeugen eines 2. Objekts tatsächlich unter allen Umständen zu Abstürzen oder ähnlichen Problem führt.)

Init-Funktionen wurden bereits an anderer Stelle diskutiert und meines Wissens war die finale Aussage, dass es sich dabei um C-Stil handelt, um Fehler bei der Erzeugung zu behandeln, der dank Exceptions in C++ schlechter Stil in C++ ist.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

5

08.04.2013, 14:53

Nun stellen sich mir aber einige Fragen zum guten Programmier-Stil, vor allem was Zeiger, Referenzen und Co betrifft. Da das in Java überhaupt keine Rolle spielt, bin ich mir etwas unsicher.

In Java ist doch praktisch alles ein Zeiger!? ;)

Wann Zeiger benutzen?
In Java ist es ja ganz einfach: Eingebaute Datentypen wie int werden einfach initialisiert (int zahl = 3; ), Instanzen von Klassen werden immer mit new erzeugt (Entity entity = new Entity(); ).
In C++ habe ich nun zwei verschiedene Möglichkeiten eine Instanz zu erzeugen: "Normal" (Entity entity; ) oder mit new (Entity *entity = new Entity). Außerdem gibts drei verschiedene Möglichkeiten, Variablen als Parameter zu übergeben (Normal, Zeiger, Referenz). Das ist noch etwas verwirrend für mich, wann welche Technik zum Einsatz kommen sollte.
Zufällig bin ich auf diese Folien gestoßen, die bei jemandem hier aus dem Forum in der Signatur verlinkt waren: http://dl.dropbox.com/u/6101039/Modern%20C%2B%2B.pdf
So wie ich das verstehe, wird dort generell von der Verwendung von Zeigern abgeraten. Warum? Sollte ich einfach immer die "normale" Instanziierung benutzen? Und was genau sind diese shared_pointer bzw unique_pointer?

new verwendest du nur dann, wenn es nötig ist. Und das ist nicht unbedingt oft der Fall, wenn dann braucht man es meist z.B. für dynamische Datenstrukturen oder irgendwie im Zusammenhang mit Polymorphie. Bezüglich unique_ptr etc. schaut dir mal an, was man unter RAII versteht.

Init-Funktionen?
In dem SDL-Beispiel benutzt der Autor überall Init-Funktionen, die sofort nach dem Erstellen des Objekts aufgerufen werden. Was ist der Sinn dahinter? Ich würde sowas in Java immer im Konstruktor erledigen. Gibt's dafür in C++ einen besonderen Grund? Soll das guter Stil sein oder ist das schlicht Unsinn?

Init-Funktionen sind furchtbarer Unsinn, dafür gibt es Konstruktoren.

Singletons?
Hat jetzt eigentlich nichts mit C++ zu tun, sondern allgemein mit Programmier-Stil. Der Autor verwendet im SDL-Beispiel zwei Singleton-Klassen: Timer und Framework. Ich denke, man könnte beide auch als normale Klassen implementieren. Oder spricht was dagegen?

Ich hätte einfach gerne ein paar Meinungen dazu!

Vergiss Singletons, Singletons sind böse.

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

6

08.04.2013, 15:23

Bei den Folien geht es nicht darum, das kein new benutzt wird. In fast jedem der Beispiele mit new geht es darum, das ein new weiterhin ausgefuehrt wird aber irgendwo in der Standard Bibliothek versteckt ist.

primat

Frischling

  • »primat« ist der Autor dieses Themas

Beiträge: 38

Wohnort: Hamburg

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

7

08.04.2013, 16:05

Danke für die Antworten!

Wahlweise kommt man um Pointer nicht gut herum, wenn man Objekte non-copyable machen will oder muss.

Das versteh ich nicht ganz. Inwiefern wird durch die Benutzung von Pointern ein Objekt non-copyable?

In Java ist doch praktisch alles ein Zeiger!? ;)

Ja klar, aber du weißt sicher was ich meine ;)
In Java kümmert man sich nicht selbst um Speicherreservierung und co. Und man hat auch gar nicht die Wahl zwischen Stack und Heap.

Bei den Folien geht es nicht darum, das kein new benutzt wird. In fast jedem der Beispiele mit new geht es darum, das ein new weiterhin ausgefuehrt wird aber irgendwo in der Standard Bibliothek versteckt ist.

Ok, wenn ich das richtig verstanden habe, dann könnte ich für mich folgende Regeln aufstellen:

* Instanzen grundsätzlich auf dem Stack erzeugen.
* Nur wenn es nicht anders geht, new benutzen.
* Wenn ich new benutze, am Besten shared oder unique pointer benutzen (je nachdem wo ich den pointer überall brauche), damit ich mich um die ordnungsgemäße Freigabe nicht selbst kümmern muss.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

8

08.04.2013, 16:08

Danke für die Antworten!

Wahlweise kommt man um Pointer nicht gut herum, wenn man Objekte non-copyable machen will oder muss.

Das versteh ich nicht ganz. Inwiefern wird durch die Benutzung von Pointern ein Objekt non-copyable?

Gar nicht. Aber muss man ein Objekt non-copyable machen, dann ist es sehr schwer dieses im Stack zu erzeugen, weil es sich nicht mehr aus der Funktion raus irgendwohin übergeben lässt, da es dazu ja copyable sein müsste.
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]

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

9

08.04.2013, 16:14

In Java ist doch praktisch alles ein Zeiger!? ;)

Ja klar, aber du weißt sicher was ich meine ;)
In Java kümmert man sich nicht selbst um Speicherreservierung und co. Und man hat auch gar nicht die Wahl zwischen Stack und Heap.

Richtig und das ist imo eine der, wenn nicht die größte Schwäche der Sprache überhaupt. Wenn man C++ richtig verwendet, dann braucht man keine Garbage Collection, weil es erst gar keinen Garbage gibt... ;)

Bei den Folien geht es nicht darum, das kein new benutzt wird. In fast jedem der Beispiele mit new geht es darum, das ein new weiterhin ausgefuehrt wird aber irgendwo in der Standard Bibliothek versteckt ist.

Ok, wenn ich das richtig verstanden habe, dann könnte ich für mich folgende Regeln aufstellen:

* Instanzen grundsätzlich auf dem Stack erzeugen.
* Nur wenn es nicht anders geht, new benutzen.
* Wenn ich new benutze, am Besten shared oder unique pointer benutzen (je nachdem wo ich den pointer überall brauche), damit ich mich um die ordnungsgemäße Freigabe nicht selbst kümmern muss.

unique_ptr ist das Mädchen für alles, shared_ptr ist für den sehr seltenen Spezialfall gedacht, dass ein Objekt mehr als einen Besitzer hat.

primat

Frischling

  • »primat« ist der Autor dieses Themas

Beiträge: 38

Wohnort: Hamburg

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

10

08.04.2013, 18:26

unique_ptr ist das Mädchen für alles, shared_ptr ist für den sehr seltenen Spezialfall gedacht, dass ein Objekt mehr als einen Besitzer hat.


Wie genau definiert sich der Besitzer eines Objekts? Ich dachte bis jetzt, dass shared pointer quasi der Normalfall ist und unique pointer nur benutzt werden können, wenn es wirklich nur einen Zeiger gibt.
Oder reicht ein unique pointer auch aus, wenn ich in einer getter-methode den Zeiger zurück gebe? Nach meinem Verständnis hätte ich dadurch einen zweiten Zeiger auf das Objekt erzeugt.

Werbeanzeige