In C würden Sie häufig / manchmal (aus Stilgründen) eine Dateibereichsvariable verwenden static
, in der Sie eine private Klassenmitgliedsvariable in C ++ verwenden würden. Wenn Sie auf Multithread-Programme skalieren, passt das einfache Hinzufügen thread_local
von C11 oder der lang unterstützten Erweiterung __thread
gut. Ich weiß, dass Sie in C genau das Gleiche tun können wie in C ++, indem Sie alles in a einfügen struct
und eine Reihe von Funktionen erstellen, die struct
als erstes Argument einen Zeiger darauf verwenden . Einige Bibliotheken tun dies ausgiebig. Aber mein persönlicher Stil ist struct
es, bei Bedarf so klein wie möglich zu halten .
Ich lese oder höre oft Leute, die argumentieren, dass 'globale' Variablen so schlecht sind. Ich folge ihren Gründen, und die meisten ihrer Argumente scheinen sich auf extern
globale Variablen in C-Begriffen zu beziehen . Was sie sagen, ist sicherlich wahr. Ich verwende manchmal 1 oder 2 extern
deklarierte Variablen im gesamten Programm, wenn dies die Dinge erheblich vereinfacht und wenn es einfach ist, sie im Auge zu behalten, aber wenn Sie weiter gehen, wird ein Programm leicht unvorhersehbar.
Was ist mit static
Variablen? Haben sie immer noch das gleiche Problem wie "echte" globale Variablen? Vielleicht muss ich diese Frage nicht einmal stellen und weitermachen, wenn ich denke, dass das, was ich tue, richtig ist, aber heute habe ich einen anderen Beitrag vom Typ "Globale Variablen sind SCHLECHT" gesehen und bin schließlich hierher gekommen und habe gedacht, dass dies vielleicht richtig ist Platz für solche Fragen. Was ist dein Gedanke?
Diese Frage ist kein Duplikat davon, da in dieser Frage nach extern
und static
nicht lokalen Variablen gefragt wird, während sich die andere Frage auf Variablen für den Dateibereich und den Blockbereich static
bezieht.
Antworten:
In einem gut gestalteten C-Programm ähnelt eine dateistatische Variable einem privaten statischen Mitglied einer Klasse:
Auf sie kann nur von Funktionen in dieser Datei zugegriffen werden, ähnlich wie auf eine private statische Mitgliedsvariable nur von Funktionen in der Klasse zugegriffen werden kann, in der sie definiert ist.
Es gibt nur eine Kopie der Variablen.
Seine Lebensdauer ist die Programmlebensdauer.
Eine
extern
Variable wäre eine echte globale Variable wie in jeder Sprache, die sie unterstützt.Eine
static
nicht globale Variable ist nicht so schlecht wie eine globale. in der Tat sind sie in einigen Fällen notwendig.Der Zugriff wird über die von Ihnen geschriebenen Funktionen gesteuert. Dies hilft bei der Datenintegrität, einschließlich der Überprüfung der Grenzen sowie der Thread-Sicherheit. (Hinweis: Dies garantiert keine Gewindesicherheit, es ist lediglich ein Hilfsmittel auf dem Weg)
Daten sind gekapselt: Nur diese Datei kann darauf zugreifen. Dies ist so nah wie C an der Kapselung, bei der mehrere Funktionen auf eine statische Variable zugreifen können.
Globale Variablen sind auf jeden Fall schlecht. Statische Dateivariablen haben die Vorteile einer privaten statischen Variablen, aber keinen der Nachteile einer globalen Variablen.
Das einzige Problem ist, dass andere Dateien im Gegensatz zu einer echten privaten statischen Variablen wie in C ++ eine
extern
Variable deklarieren können, die der Deklaration entspricht, und Sie den Zugriff nicht verhindern können. Mit anderen Worten, Sie verlassen sich auf das Ehrensystem, um zu vermeiden, dass es zu einer globalen Variablen wird.quelle
Der globale Status, einschließlich
extern
Variablen und Nichtvariablenconst
static
im Dateibereich oder in Funktionen, kann häufig eine einfache Lösung für ein bestimmtes Problem sein, es gibt jedoch drei Probleme:static
macht Code nicht testbar , dastatic
Variablen in der Regel nicht ersetzbare Abhängigkeiten sind. Oder in mehr OOP-y-Worten: Sie folgen nicht dem Prinzip der Abhängigkeitsinversion. Ich bin aus dynamischen Sprachen wie Perl zu C und C ++ gekommen, daher ist mein Kostenmodell auf virtuelle Versand- und Funktionszeiger usw. ausgerichtet. Bei den aktuellen Sprachen gibt es einen Konflikt zwischen Testbarkeit und guter Architektur, aber ich denke, dass das kleine Ärgernis, Ihre Abhängigkeiten explizit zu machen und sie in Tests überschreiben zu lassen, durch die einfache Schreibbarkeit von Tests spürbar ausgeglichen wird und somit sicherstellt, dass Ihre Software funktioniert erwartet. Ohne Ihren Code dynamischer zu gestalten, ist der einzige verfügbare Mechanismus zum Einfügen von Abhängigkeiten für einen Test die bedingte Kompilierung.Der globale Zustand macht es schwierig, über Korrektheit nachzudenken , und das führt zu Fehlern. Je mehr Teile auf eine Variable zugreifen und diese ändern können, desto einfacher ist es, den Überblick über das Geschehen zu verlieren. Stattdessen: lieber einzelne Zuordnung von Variablen! Lieber
const
wo immer vernünftig! Ziehen Sie es vor, Variablen durch Getter und Setter zu schützen, in denen Sie Korrektheitsprüfungen einführen können. Solange der Staat iststatic
und nichtextern
, ist es immer noch möglichum die Korrektheit aufrechtzuerhalten, aber es ist immer besser anzunehmen, dass ich in einer Woche nicht so schlau bin wie ich. Insbesondere in C ++ können wir Klassen verwenden, um verschiedene Abstraktionen zu modellieren, die es unmöglich machen, etwas zu missbrauchen. Versuchen Sie also, das Typsystem anstelle Ihrer Intelligenz zu verwenden - Sie müssen über wichtigere Dinge nachdenken.Der globale Status kann bedeuten, dass Ihre Funktionen nicht erneut eingegeben werden oder dass sie jeweils nur in einem Kontext verwendet werden können. Stellen Sie sich einen Datenbanktreiber vor, der nur eine Verbindung verwalten kann! Das ist eine völlig unnötige Einschränkung. In der Realität sind die Einschränkungen häufig subtiler, z. B. eine globale Variable, mit der Ergebnisse aggregiert werden. Machen Sie stattdessen Ihren Datenfluss explizit und übergeben Sie alles über Funktionsparameter. Auch hier können C ++ - Klassen dies leichter handhaben.
Offensichtlich
static const NAMED_CONSTANTS
sind OK. Die Verwendungstatic
innerhalb von Funktionen ist viel schwieriger: Während es für träge initialisierte Konstanten nützlich ist, kann es ziemlich unprüfbar sein. Ein Kompromiss besteht darin, die Berechnung des Anfangswertes von der statischen Variablen zu trennen, damit beide Teile separat getestet werden können.In kleinen, in sich geschlossenen Programmen spielt dies alles keine Rolle, und Sie können den
static
Status weiterhin nach Herzenslust verwenden. Wenn Sie jedoch ungefähr 500 LOC überschreiten oder eine wiederverwendbare Bibliothek schreiben, sollten Sie wirklich über eine gute Architektur und eine gute Benutzeroberfläche ohne unnötige Einschränkungen nachdenken.quelle
thread_local
Compilern als Erweiterung vor der Standardisierung unterstützt.static
Variablen reduziert, aber nicht beseitigt .Ich halte Variablen mit Dateibereich nicht für so schlecht wie globale Variablen. Schließlich sind alle Zugriffe auf diese Variablen auf eine einzige Quelldatei beschränkt. Mit dieser Einschränkung sind Dateibereichsvariablen so gut oder schlecht wie ein privates statisches C ++ - Datenelement, und Sie verbieten ihre Verwendung nicht, oder?
quelle
static
Speicher, die genau einmal existieren, nicht für jedes Objekt. Diese Variable kann nicht braucht eine Instanz der Klasse, um durch den Code der Klasse referenziert zu werden, so gibt es keine Notwendigkeit, übergeben Sie einen Verweis / Zeiger auf einen Singleton um. Genau das erreichen auch Dateibereichsvariablen. Der einzige Unterschied besteht darin, dass dasstatic
Mitglied einen Klassenbereich hat, während diestatic
"globale" Variable einen Dateibereich hat. Aber diese beiden Bereiche sind in ihrem Ausmaß sehr ähnlich, was mein Punkt ist. Ich stimme jedoch zu, dass die verschiedenen Bedeutungen vonstatic
verwirrend sind.Meiner Ansicht nach hängt alles vom Umfang der Variablen ab (nicht konstant, etwas Veränderliches). Es ist eine Ansicht, der zwar einige Nuancen fehlen, aber es ist ein pragmatischer Gegenpol und ein Appell, auf die grundlegendsten Grundlagen für diejenigen zurückzuarbeiten, die sagen: "Das ist absolut böse!" nur um dann auf Themen zu stoßen, die denen ähneln, die mit dem verbunden sind, was sie kritisieren, wie z. B. die Rennbedingungen.
Stellen Sie sich vor, Sie haben eine 50.000-Zeilen-Funktion mit allen Arten von Variablen, die oben deklariert sind, und
goto
Anweisungen, mit denen Sie überall herumhüpfen können . Das ist bei einem solch monströsen Variablenumfang nicht sehr angenehm, und es wird äußerst schwierig sein, über die Funktion und die Vorgänge mit solchen Variablen nachzudenken. In solch einem monströsen Fall verliert die normale Unterscheidung zwischen "äußerer" und "innerer" Nebenwirkung viel von ihrem praktischen Zweck.Stellen Sie sich vor, Sie haben ein einfaches Programm mit 80 Zeilen, das Sie nur einmal schreiben und mit einer globalen Variablen starten (entweder mit interner Verknüpfung und Dateibereich oder externer Verknüpfung, aber so oder so ist das Programm winzig). Das ist nicht so schlecht.
Stellen Sie sich vor, Sie haben eine monströse Klasse in einer objektorientierten Sprache, die die Logik Ihres gesamten Programms mit Tausenden und Abertausenden von Codezeilen für die Implementierung enthält. In diesem Fall sind die Mitgliedsvariablen problematischer als die globalen Variablen im obigen Programm mit 80 Zeilen.
Wenn Sie in der Lage sein möchten, Ihren Code besser und sicherer zu beurteilen, die Thread-Sicherheit (oder das Fehlen davon) besser vorhersehbar zu machen, sicherzustellen, dass Ihre Tests eine gute Abdeckung aufweisen, ohne dass alle Arten von potenziellen Randfällen übersehen werden usw. Dann hilft es, den Zugriff auf Variablen einzugrenzen.
Die Statik des Dateibereichs hat tendenziell einen engeren Bereich als die mit externer Verknüpfung. Wenn Ihre Quelldatei jedoch 100.000 Codezeilen umfasst, ist sie immer noch verdammt breit. Wenn Sie sie für die Statik des Dateibereichs nicht vermeiden können, würde ich versuchen, ihren Gültigkeitsbereich eng zu halten, indem Sie Ihre Quelldatei, die auf sie zugreifen kann, nicht riesig machen, da in diesem Fall die Reduzierung des Gültigkeitsbereichs eine Reduzierung der Größe und des Entwurfsbereichs bedeutet der Quelldatei im Gegensatz zur Funktion (für lokale Variablen, einschließlich Parameter), Klasse (für Mitgliedsvariablen) oder möglicherweise Modul (für Globals mit externer Verknüpfung, aber nur innerhalb des Moduls zugänglich) oder sogar der gesamten Software (für Globals mit externe Verknüpfung für die gesamte Software zugänglich).
quelle