2014-12-02 9 views
7

Ich benutze #pragma once (oder Sie verwenden Wächter à la #ifndef...) grundsätzlich in jeder Header-Datei in meinen C++ - Projekten. Ist das ein Zufall oder ist es das, was Sie in den meisten Open-Source-Projekten zum Beispiel finden (um Antworten zu vermeiden, die sich auf nur auf persönliche Projekterfahrung beziehen). Wenn ja, warum nicht umgekehrt? Wenn ich möchte, dass eine Header-Datei mehrmals eingefügt wird, verwende ich denselben speziellen Pre-Prozessor-Befehl und wenn nicht, lasse ich die Datei so, wie sie ist.Warum sind Wächter in C++ nicht die Standardeinstellung?

+8

'#pragma einmal 'ist nicht Standard; '# ifndef/# define/# endif' ist. –

+3

@ T.C. OK. Aber die Frage bleibt grundsätzlich gleich. – NOhs

+0

Sie können sich '' (und '') anschauen. Zwei Header, die verkrüppelt würden, wenn '#pragma once' oder ähnliches automatisch war. (Obwohl es immer noch gegen die Herstellung der normalen Sache einfach und kurz, ohne den ungewöhnlichen Weg unmöglich machen würde. Ich meine, könnte man "Pragma Redo" oder dergleichen.) – Deduplicator

Antwort

7

Das Verhalten eines C++ - Compilers wird in Bezug auf die Handhabung jeder Übersetzungseinheit angegeben. Eine Übersetzungseinheit ist eine einzelne Datei, nachdem der Präprozessor sie durchlaufen hat. Die Tatsache, dass wir eine Konvention haben, Deklarationen in bestimmten Dateien zu sammeln und sie als "Header" -Dateien zu bezeichnen, bedeutet nichts für einen Compiler oder den C++ - Standard.

Einfach gesagt, der Standard bietet keine "Header-Dateien", so dass es nicht automatisch Guard-Header-Dateien enthalten kann. Der Standard sieht nur die Präprozessor-Direktive #include vor und der Rest ist lediglich Konvention. Nichts hindert Sie davor, alles zu deklarieren und keine Header-Dateien zu verwenden (außer für jemanden, der diesen Code pflegen sollte ...).

Also Header-Dateien sind nicht besonders und es gibt keine Möglichkeit zu sagen, "das ist eine Header-Datei, bewachen Sie es", aber warum können wir nicht alles schützen, was bekommt #include 'd? Denn #include ist sowohl mehr als auch weniger leistungsfähig als ein Modulsystem wie andere Sprachen. #include bewirkt, dass der Präprozessor andere Dateien einfügt, nicht unbedingt Headerdateien. Manchmal kann dies nützlich sein, wenn Sie dieselben Deklarationen für die Verwendung und Typdefinition in einer Reihe verschiedener Namespaces in verschiedenen Dateien verwenden. Sie könnten sie in einer Datei und #include an einigen Orten sammeln. Sie würden nicht wollen, dass automatische Einschlusswachen Sie davon abhalten.

Die Verwendung von #ifndef und #define zur bedingten Include-Header ist auch nur Konvention. Der Standard hat kein Konzept von "include guard". (Moderne Compiler jedoch sind tatsächlich bewusst Wachen sind. Erkennen sind Wachen schnellere Kompilierung erlauben können, aber es hat nichts mit richtig zu tun, um den Standard zu implementieren.)

Erste pedantisch

Der Standard zügig das Wort nicht verwendet "header", insbesondere in Bezug auf die C- und C++ - Standardbibliotheken. Das Verhalten von #include ist jedoch unter § 16.2 *Source file* inclusion (emph. Mine) definiert und es gibt keine besonderen Befugnisse für Header-Dateien.

Es gibt Bemühungen, ein richtiges Modulsystem in den C++ Standard zu bekommen.

+1

Dort ist "Überschrift" und dort ist "Quelldatei". "Kopfzeilen" müssen keine tatsächlichen Dateien sein. –

+0

Wirklich? Erzählen Sie. Ist das so, weil der Standard es erlaubt, Dinge, die von Implementierungen definiert werden, zu liefern? – Praxeolitic

+4

Richtig (oder nicht spezifiziert, anstatt Implementierung definiert). Alles was benötigt wird, ist, dass das Schreiben von '#include ' bewirkt, dass die entsprechenden Deklarationen und Definitionen angezeigt werden. Der Compiler könnte eine spezielle Handhabung haben, um die Standard-Header-Namen zu erkennen und deren Inhalt fest codiert zu haben, oder er könnte einen Teil der AST-Daten aus einer Objektdatenbank holen ...oder es könnte den Präprozessor nur dazu bringen, eine Datei textlich einzubinden (was alle gängigen Implementierungen in der Praxis tun). –

4

Weil hysterische Rosinen.

Der C++ Präprozessor ist fast identisch mit dem C-Präprozessor, der vor über 40 Jahren entwickelt wurde. Compiler damals waren viel einfacher. Der Präprozessor war noch einfacher, nur ein dummer Makroprozessor, nicht einmal ein Compiler. Obwohl der C++ - Standard nicht angibt, wie er für Standardheader funktioniert, ist das Konzept #include immer noch das gleiche wie vor 40 Jahren: Es bewirkt, dass der Präprozessor den Inhalt der benannten Datei in die Include-Datei einfügt.

In einer einfachen C-Codebase der 1970er Jahre ohne viele Abhängigkeiten und Untermodule kann es sein, dass Sie keine Include-Wächter benötigen. Der frühe Vor-Standard C hat keine Funktionsprototypen benutzt, was die Mehrheit der Include-Dateien für diese Tage ist. Wenn das zweimalige Hinzufügen einer Kopfzeile einen Fehler verursacht hat, könnten Sie den Code wahrscheinlich neu anordnen, um zu verhindern, dass er zweimal eingefügt wird.

Als Codebase wuchs und komplexer wurde, nehme ich an, dass das Problem der zufälligen Aufnahme eines Headers zweimal (wahrscheinlich indirekt, über andere Header) häufiger geworden ist. Eine Lösung wäre gewesen, den Präprozessor zu ändern, um ihn schlauer zu machen, aber das hätte erfordert, dass jeder den neuen Präprozessor verwendet und ihn größer und langsamer gemacht hätte. Stattdessen wurde eine Konvention entwickelt, die das Problem unter Verwendung der vorhandenen Funktionen des Präprozessors (#define und #ifndef) löst, nicht indem verhindert wird, dass ein Header zweimal eingefügt wird, sondern einfach dadurch unschädlich gemacht wird, dass ein Header zweimal eingefügt wird Zeit es ist enthalten.

Im Laufe der Zeit wurde die Konvention breiter verwendet und ist jetzt fast universell, mit Ausnahme der seltenen Beispiele von Headern, die so entworfen wurden, dass sie zweimal enthalten sind und so geschrieben wurden, dass sie auf diese Weise funktionieren (z.B. <assert.h>).

Später noch, #pragma once wurde von einigen Compilern als eine alternative, nicht-portable Möglichkeit eingeführt, den gleichen Effekt wie Wächter zu haben, aber bis dahin gab es viele Tausende von Kopien verschiedener C-Präprozessoren im Einsatz rund um das Wort und enthalten Wachen waren zur Norm geworden.

So ist das aktuelle Verhalten fast sicher aus historischen Gründen. Moderne Sprachen, die heute für die unglaublich leistungsfähigen Computer von heute geschrieben werden, würden nicht so etwas wie den C-Präprozessor verwenden, wenn sie von Grund auf neu entwickelt würden. Aber C wurde nicht im 21. Jahrhundert entworfen. Ich glaube, dass die include guard-Konvention im Laufe der Zeit langsam eingeführt wurde und keine Änderungen an bestehender Software erforderlich waren, damit sie funktioniert. Wenn Sie jetzt Änderungen vornehmen, werden unbekannte Mengen von C- und C++ - Code, die auf dem aktuellen Verhalten beruhen, gebrochen und sind wahrscheinlich keine Option, da die Abwärtskompatibilität sowohl für C als auch für C++ wichtig ist.

0

Ich würde zustimmen müssen, dass der Hauptfaktor historisch ist, dass gelegentlich Code angezeigt wird, der darauf beruht, dass sie nicht da sind. MAME ist ein Beispiel: Es baut komplexe Datenstrukturen (oder zumindest das letzte Mal, als ich vor einiger Zeit nachgeschaut habe) aus einer lesbaren Makro-basierten Datei, indem ich sie mehrfach mit den unterschiedlich definierten Makros einschließe. Wenn die Wächter automatisch eingeschlossen wären, würden Sie auf einen Code stoßen, der eine Abschaltung erfordert.

Verwandte Themen