Statische Klassenvariablen oder doch globale?
-
Hallo zusammen,
Bisher habe ich mir diese Thematik gar nicht speziell überlegt. Globale Variablen benutze ich relativ selten, statische (in Klassen) schon ab und zu. In den meisten Fällen sind diese jedoch
private
, also nur für die Methoden der Klasse von Bedeutung. Nun habe ich mir gedacht, in vielen Fällen könnte man die Abhängigkeiten der Dateien untereinander verringern, wenn man statt statischen Klassenvariablen in den entsprechenden .cpp-Dateien globale Variablen deklarieren würde. Diese wären nur lokal sichtbar, und dafür benötigte Typen müssten nicht in den Header der Klassendefinition eingebunden werden.Ich sehe momentan keinen Vorteil darin, statische private Variablen in der Klasse zu deklarieren. Klar, man kann sagen, sie gehören logisch zur Klasse. Da bin ich auch einverstanden, vom Design her wäre es vielleicht schon etwas eleganter, aber dieses "Gefühl" scheint mir den Preis nicht wert zu sein.
Wie handhabt ihr das, seid ihr hier konsistent?
-
"global und static und nicht im header deklariert und nur in der meineKlasse.cpp bekannt"
anstatt
"member in meineKlasse und static und im header deklariert und private"
ist eine sehr, sehr gute idee.
-
oder statt global static gleich global im nameless namespace
*pflaum*
EDIT:
Wie handhabt ihr das, seid ihr hier konsistent?
Ich versuche soweit wie möglich auf statics (und globals) zu verzichten. U.a. da ich meist Code schreibe der threadsafe sein muss, und statics/globals da stören, bzw. eben zu Overhead führen (Locking).
Ansonsten... was ich öfters mache, ist private statische Funktionen in nameless Namespaces auszulagern, wenn das möglich ist. Also wenn sie z.B. keine Zugriff auf die "privates" einer Klasse brauchen, und nicht aus Templates aufgerufen werden.
Das führt nicht nur zu weniger Änderungen im Header-File, sondern auch zu kleineren, und daher übersichtlicheren Header-Files.
-
Da ich meistens sehr wenige
private static
Variablen in der Klasse habe, kommen die bei mir in den Header. Das praktische daran ist, wenn man sie doch malpublic
machen möchte, reicht das ändern eines Keywords. Und ja, mir ist das schon mehrmals passiert, dass die Dinger plötzlichpublic
wurden. Allerdings könnte es auch daher kommen, dass ich eigentlich nur konstante, statische Variablen habe.
Zudem das mit der Abhängigkeit: Ich weiss nicht, wann zum letzten Mal ich eine statische und nicht fundamentale Variable in meinen Klassen hatte. Höchstens vielleicht einmal einenstd::string
.Was machst du denn für statische Variablen, bzw. ihr? Das würde mich nun auch wundernehmen
Grüssli
-
Danke für die Antworten.
volkard schrieb:
[...] ist eine sehr, sehr gute idee.
Scheint mir eben auch so.
Aber werden dannprivate static
Variablen überhaupt ab und zu eingesetzt, haben die einen Vorteil?hustbaer schrieb:
oder statt global static gleich global im nameless namespace
Wieso muss die Variable eigentlich
static
oder im anonymen Namensraum sein? Wenn ich sie in der .cpp-Datei deklariere, wird sie sowieso nur in einer Übersetzungseinheit definiert.Dravere schrieb:
Da ich meistens sehr wenige
private static
Variablen in der Klasse habe, kommen die bei mir in den Header. Das praktische daran ist, wenn man sie doch malpublic
machen möchte, reicht das ändern eines Keywords. Und ja, mir ist das schon mehrmals passiert, dass die Dinger plötzlichpublic
wurden. Allerdings könnte es auch daher kommen, dass ich eigentlich nur konstante, statische Variablen habe.Die meisten sind bei mir eigentlich auch konstant. Aber plötzlich
public
werden sie ziemlich sicher nicht, von daher schaffe ich mir nur Hindernisse, wenn ich sie in der Klasse definiere.Dravere schrieb:
Was machst du denn für statische Variablen, bzw. ihr? Das würde mich nun auch wundernehmen
Naja, bei meinem Spiel gibt es für die einzelnen Klassen (Gegner, Tiles, ...) eben Attribute, die für alle Instanzen gelten. Das sind meistens Konstanten. Teilweise habe ich auch kleine Datensätze für die Eigenschaften unterschiedlicher Typen (z.B. Geschwindigkeit, Feuerrate, Grafik-Rechteck der Gegner) in einem statisch-konstanten
tr1::array
, also eine Art Tabelle.
-
Nexus schrieb:
Naja, bei meinem Spiel gibt es für die einzelnen Klassen (Gegner, Tiles, ...) eben Attribute, die für alle Instanzen gelten. Das sind meistens Konstanten. Teilweise habe ich auch kleine Datensätze für die Eigenschaften unterschiedlicher Typen (z.B. Geschwindigkeit, Feuerrate, Grafik-Rechteck der Gegner) in einem statisch-konstanten
tr1::array
, also eine Art Tabelle.Solche Daten lege ich in eine Managerklasse. Diese Klasse ist dann für die Verwaltung der Attribute verantwortlich. Die anderen Objekte referenzieren dann über Zeiger oder IDs auf die entsprechenden Attribute. So kann ich jederzeit auf einfache Art und Weise die Attribute von anderen Orten holen und/oder modifizieren. Meistens ist diese Managerklasse dann auch ein Singleton.
Das praktische daran ist zum Beispiel das Laden (und teilweise Speichern) der Attribute. Für diese Aufgabe gibt es eine weitere Klasse, welche dann die geladenen Attribute der Managerklasse übergibt. So kann man zum Beispiel Attribute aus einem XML File laden lassen oder allenfalls auch andere Formate zulassen.
Grüssli
-
Mit der Zeit könnte ich das auch überlegen, aber momentan habe ich die Daten sowieso hardcoded im Code (es sind nicht allzu viele, und ein Auslagern in Dateien würde nur zu mehr Aufwand, unnötigen Fehlerquellen und einer Flexibilität, die man nicht braucht, führen). Diese Daten sind auch in einer gemeinsamen .cpp-Datei definiert - die Frage ist also nur, wo die Deklaration hin soll.
-
Nexus schrieb:
Mit der Zeit könnte ich das auch überlegen, aber momentan habe ich die Daten sowieso hardcoded im Code (es sind nicht allzu viele, und ein Auslagern in Dateien würde nur zu mehr Aufwand, unnötigen Fehlerquellen und einer Flexibilität, die man nicht braucht, führen). Diese Daten sind auch in einer gemeinsamen .cpp-Datei definiert - die Frage ist also nur, wo die Deklaration hin soll.
Ich drück dir die Daumen, dass dir nicht sowas passiert wie bei mir:
3 Leute und ich haben ein Netzwerkspiel entwickelt für die Uni. Die Einheiten waren auch Hardcoded. Das war auch kein Problem, da wir nur 4 Einheiten reingesetzt haben. Dies reichte völlig aus für das Uniprojekt.
Später wollte ich die Sache weiterentwickeln. Also mehr Einheiten reintun und die ganze Sache ausbalancieren. Dies resultierte in den folgenden Zyklus:
- Server starten, spiel eröffnen (was einige Eingaben und Klicks benötigte)
- Client starten, mit Server verbinden (localhost, Spielername, usw. usf.)
- Testen und Notizen machen
- Client beenden
- Server beenden
- Code ändern
- Neu kompilieren (Server und Client)
- Server starten, spiel eröffnen
- Client starten, mit Server verbinden
- usw.Ein Zyklus hat wohl ca. 5 bis 10 Minuten benötigt. Da kam man einfach nicht mehr vom Fleck. Habe dann die Sache neuprogrammieren müssen und dann eben ein Managersystem eingeführt mit externem File. Zusätzlich eine Debugfunktion, welche die Files zur Laufzeit neu lädt. Dann war der Zyklus:
- Server starten, spiel eröffnen
- Client starten, Server beitreten
- Testen
- XML ändern
- neu einlesen
- Testen
- XML ändern
- usw.Zyklus von 30 Sekunden
Wirkliche Daten, welche Hardcoded sind, sind ein Graus.Grüssli
-
Wirkliche Daten, welche Hardcoded sind, sind ein Graus.
Hehe. Oh ja..
Ich liebe es ja, wenn ich die Daten Online ändern kann. Editoren für alles mögliche beschleunigt den Workflow und den Spass extrem.
-
- Server starten, spiel eröffnen (was einige Eingaben und Klicks benötigte)
//kommandozeilenparameter hätten hier geholfen. die will der user eh haben.- Client starten, mit Server verbinden (localhost, Spielername, usw. usf.)
//kommandozeilenparameter hätten hier geholfen. die will der user eh haben.- Testen und Notizen machen
//sollte möglich sein, gleich den code anzufassen und compilieren zu lassen- Client beenden
//darf sich selber beenden, wenn der server sich beendet- Server beenden
//kann auf strg+c beenden oder auf signal, das wegen codeänderung kommt- Code ändern
//hab ich schon- Neu kompilieren (Server und Client)
//macht makefile- Server starten, spiel eröffnen
//siehe oben- Client starten, mit Server verbinden
//siehe oben
- usw.Ein Zyklus hat wohl ca. 5 bis 10 Minuten benötigt. Da kam man einfach nicht mehr vom Fleck.
[/quote]
das geht alles auch hardcoded. du hättest nur makefiles einsetzen müssen und tricks wie exe-dateien, die sich selber beenden, wenn eine steuerdatei verändert wurde oder sowas. hättest du mal die symptome einzeln und entschieden bekämpft, wäre xml gar nicht ins spiel gekommen.
aber xml hat nen vorteil für mich als zocker: ich kann dran rumbasteln.
-
volkard schrieb:
- Server starten, spiel eröffnen (was einige Eingaben und Klicks benötigte)
//kommandozeilenparameter hätten hier geholfen. die will der user eh haben.Da der Server auch der Client war, war sowas nicht geplant. Und wieso sowas nicht geplant war, siehe danach:
volkard schrieb:
- Client starten, mit Server verbinden (localhost, Spielername, usw. usf.)
//kommandozeilenparameter hätten hier geholfen. die will der user eh haben.Ich habe bisher noch kaum ein Spiel gesehen, dass Kommandozeilenparameter hatte, bzw. wo der User welche verlangt oder eingesetzt hat. Viele von den Spielern wissen ja nicht einmal, was ein Kommandozeilenparameter sein soll.
volkard schrieb:
- Testen und Notizen machen
//sollte möglich sein, gleich den code anzufassen und compilieren zu lassenKlar kannst du das, nur ob es sinnvoll ist? Ich mache mir meisten lieber zuerst mehrere Notizen, bevor ich anfange im Code was zu ändern. Du kannst auch die Sache kompilieren lassen. Nur ist es dann irgendwie blöd, wenn du plötzlich merkst, dass du noch was ändern/testen willst -> nochmals kompilieren.
Bevor man Code ändert, sollte man wissen, was man tut.volkard schrieb:
- Client beenden
//darf sich selber beenden, wenn der server sich beendetGing nicht, da die Sache so aufgebaut war, dass ein Client automatisch die Funktion des Server übernommen hat, sobald dieser ausfiel. Sehr praktisches Feature, wenn man das Spiel zu Ende spielen möchte.
volkard schrieb:
- Server beenden
//kann auf strg+c beenden oder auf signal, das wegen codeänderung kommtSiehe oben.
volkard schrieb:
- Code ändern
//hab ich schonJa, aber die Zeit hast du trotzdem verbraten. Nur halt in einer anderen Reihenfolge.
volkard schrieb:
- Neu kompilieren (Server und Client)
//macht makefileWie bitte? Bei mir macht das sogar die IDE. Trotzdem muss kompiliert werden.
volkard schrieb:
- Server starten, spiel eröffnen
//siehe oben
- Client starten, mit Server verbinden
//siehe oben
- usw.siehe oben ...
volkard schrieb:
das geht alles auch hardcoded. du hättest nur makefiles einsetzen müssen und tricks wie exe-dateien, die sich selber beenden, wenn eine steuerdatei verändert wurde oder sowas. hättest du mal die symptome einzeln und entschieden bekämpft, wäre xml gar nicht ins spiel gekommen.
Muss ich wohl nix mehr dazu sagen, da alles schon oben steht.
Und auch wenn mein Spiel anders aufgebaut gewesen wäre, mit all den Beschleunigungen, welche du aufzeigst, würde es trotzdem noch länger dauern, als ein einfaches externes XML File, wo man dann die Clients gar nicht mal beenden oder neustarten muss. Wo man eine Situation nicht wieder bis zu einem Punkt hinarbeiten muss, um die den Test durchzuführen, usw. usf.volkard schrieb:
aber xml hat nen vorteil für mich als zocker: ich kann dran rumbasteln.
Und das wollte ich dann auch. Für Mods ist das der Himmel auf Erden.
Grüssli
-
Dravere schrieb:
Ich drück dir die Daumen, dass dir nicht sowas passiert wie bei mir:
Wird es nicht, zumindest nicht vorläufig.
Ich habe weder Netzwerk noch das von dir beschriebene Vorgehen. Eine Änderung ist bei nicht annähernd so aufwändig wie in deinem Fall.
volkard schrieb:
aber xml hat nen vorteil für mich als zocker: ich kann dran rumbasteln.
Genau. Ich muss bereits Leveldateien und Highscores absichern, damit nicht jeder daran manipulieren kann. (Wer jetzt sagen will, so gut könne ich das eh nicht absichern, ein 1337-Hacker würde es knacken - okay, von mir aus. Der könnte auch in den Arbeitsspeicher schauen oder das Programm disassembeln, aber darum gehts nicht. Tatsache ist, dass der normale Benutzer so nicht in Versuchung gerät, sich das Spiel zu einfach zu machen.)
Das meinte ich auch mit zusätzlichen Fehlerquellen: Ich habe wieder einen Ort mehr, bei dem ich prüfen muss, ob die Datei vorhanden ist, ob sämtliche Einträge das richtige Format haben, ob die Einträge realistisch und spielbar sind. Das ist einfach unnötig, wenn ich das gleiche im Code erreiche und keine Nachteile dadurch habe bzw. diese gut tragbar sind. Wenn es jetzt so wäre, dass ich zuerst das Spiel fertigstelle und dann eine separate Balancing-Phase durchführe, sähe das auch wieder anders aus. Aber während des Testens muss ich so oder so dauernd Code ändern, sei es aufgrund von Bugs oder Details, die mir noch nicht gefallen.
Soviel dazu. Aber grundsätzlich geht diese Diskussion am Thema vorbei. Selbst wenn man die Daten in jenem spezifischen Fall auslagern könnte, bleibt es ein allgemeines Problem, wie es um statische private Variablen steht. Wäre gut, wenn noch jemand dazu etwas sagen könnte. Hauptsächlich fragte ich mich, welche Vorteile solche Variablen hätten und was ein anonymer Namespace/
static
in der .cpp-Datei bringen würde.
-
Nexus schrieb:
Hauptsächlich fragte ich mich, welche Vorteile solche Variablen hätten und was ein anonymer Namespace/
static
in der .cpp-Datei bringen würde.Du verhinderst damit "multiple definition", wenn du alles zusammenlinkst.
-
Aquae schrieb:
Du verhinderst damit "multiple definition", wenn du alles zusammenlinkst.
Aber nicht, wenn die Definition nur in einem Modul vorkommt...
-
Nexus schrieb:
Aquae schrieb:
Du verhinderst damit "multiple definition", wenn du alles zusammenlinkst.
Aber nicht, wenn die Definition nur in einem Modul vorkommt...
Naja, so kannst du das Kollisions-Risiko ausschließen, ist an sich ziemlich praktisch
-
Badestrand schrieb:
Naja, so kannst du das Kollisions-Risiko ausschließen, ist an sich ziemlich praktisch
Du meinst, falls in einem anderen Modul eine Definition mit dem gleichen Bezeichner vorkommt? Oder wie ist das zu verstehen?
-
Nexus schrieb:
Badestrand schrieb:
Naja, so kannst du das Kollisions-Risiko ausschließen, ist an sich ziemlich praktisch
Du meinst, falls in einem anderen Modul eine Definition mit dem gleichen Bezeichner vorkommt? Oder wie ist das zu verstehen?
Ja, genau. Zur Vermeidung kann man natürlich auch kryptische Bezeichner oder fiese Prefixe vergeben, ist bei dem einfachen anonymer-namespace-Workaround aber imo ziemlich unnötig.
-
Gut, danke. Hat ein anonymer Namensraum eigentlich gewisse Vorteile gegenüber
static
? Beide führen ja zu interner Linkage, oder worin besteht der Unterschied?
-
Ich kenne keinen Unterschied, verwende aber immer anonyme Namensräume um mir static für die "normalen" static-Fälle freizuhalten. Dass static internal Linkage bewirkt, hab ich auch erst vor etwa einem Jahr erfahren, da war ich gut überrascht
-
Nexus schrieb:
Gut, danke. Hat ein anonymer Namensraum eigentlich gewisse Vorteile gegenüber
static
? Beide führen ja zu interner Linkage, oder worin besteht der Unterschied?der nameless namespace (der richtig glaub ich unnamed heisst, aber ich mag nameless mehr weil es sich reimt
) hat NICHT internal linkage. was auch gut so ist. denn dadurch kann man klassen, die man in einem unnamed namespace definiert, als template parameter einsetzen.
der vorteil von unnamed namespaces ist ganz klar dass man alles reinstecken kann, also auch klassen. mit static kann man bloss variablen und funktionen machen, aber keine klassen. unnamed namespaces sind der "c++ way of doing it". static für globale variablen/funktionen gibt's im prinzip nurnoch zwecks kompatibilität mit C und mit älteren C++ versionen.