474 Stimmen

#pragma once vs include guards? #pragma einmal gegen Include-Guards?

Ich arbeite an einer Codebasis, die nur unter Windows ausgeführt und unter Visual Studio kompiliert werden kann (sie integriert sich eng mit Excel, daher bleibt sie bestehen). Ich frage mich, ob ich mich für die traditionellen Include-Wächter entscheiden sollte oder #pragma once für unseren Code verwenden soll. Ich denke, dass es schneller zu kompilieren ist und weniger fehleranfällig ist, wenn man den Compiler #pragma once behandeln lässt. Außerdem ist es etwas weniger hässlich ;)

Hinweis: Um schnellere Kompilierzeiten zu erreichen, könnten wir Redundante Include-Wächter verwenden, aber das führt zu einer engen Kopplung zwischen der eingebundenen Datei und der einbindenden Datei. Normalerweise ist das in Ordnung, da der Wächter auf dem Dateinamen basieren sollte und nur geändert werden würde, wenn Sie den Include-Namen ohnehin ändern müssten.

15voto

Michael Burr Punkte 320591

Im Allgemeinen kümmere ich mich nicht um #pragma once, da mein Code manchmal mit etwas anderem als MSVC oder GCC kompiliert werden muss (Compiler für eingebettete Systeme haben nicht immer die #pragma).

Also muss ich sowieso #include-Guards verwenden. Ich könnte auch #pragma once verwenden, wie einige Antworten vorschlagen, aber es scheint nicht viel Sinn zu machen und es wird oft unnötige Warnungen bei den Compilern verursachen, die es nicht unterstützen.

Ich bin mir nicht sicher, welche Zeitersparnis die pragma bringen könnte. Ich habe gehört, dass Compiler im Allgemeinen bereits erkennen, wenn eine Kopfzeile außerhalb der Guard-Makros nichts als Kommentare enthält und in diesem Fall das Äquivalent zu #pragma once tun werden (d. h. die Datei nie wieder verarbeiten). Aber ich bin mir nicht sicher, ob es wahr ist oder nur ein Fall von Compilern, die diese Optimierung machen könnten.

In jedem Fall ist es für mich einfacher, #include-Guards zu verwenden, die überall funktionieren und mir keine weiteren Sorgen machen.

8 Stimmen

Ich bin neugierig, welcher Compiler es nicht unterstützt. Es scheint, dass die meisten Compiler es tun und ich kenne keinen, der es nicht tut.

9voto

Richard Corden Punkte 20939

Ich denke, das Erste, was Sie tun sollten, ist zu überprüfen, ob sich dies wirklich auswirkt, d.h. Sie sollten zuerst die Leistung testen. Eine der Suchanfragen bei Google hat dieses ergeben.

In den Ergebnisseiten sind die Spalten für mich leicht versetzt, aber es ist klar, dass zumindest bis VC6 Microsoft die Include-Guard-Optimierungen, die die anderen Tools verwendeten, nicht implementierte. Wenn der Include-Guard intern war, dauerte es 50-mal so lange im Vergleich zu externen Include-Guards (externe Include-Guards sind mindestens so gut wie #pragma). Aber lassen Sie uns die möglichen Auswirkungen betrachten:

Laut den vorgelegten Tabellen ist die Zeit, um das Include zu öffnen und zu überprüfen, 50-mal so hoch wie bei einem #pragma-Äquivalent. Die tatsächliche Zeit dafür betrug jedoch im Jahr 1999 1 Mikrosekunde pro Datei!

Also, wie viele doppelte Header wird ein einzelnes TU haben? Das hängt von Ihrem Stil ab, aber wenn wir sagen, dass im Durchschnitt ein TU 100 Duplikate hat, zahlen wir im Jahr 1999 potenziell 100 Mikrosekunden pro TU. Mit den HDD-Verbesserungen ist dies wahrscheinlich inzwischen erheblich niedriger, aber selbst dann ist mit vor-kompilierten Headern und korrektem Abhängigkeits-Tracking die gesamte kumulative Kosten dieses für ein Projekt fast sicher ein unbedeutender Teil Ihrer Build-Zeit.

Nun, auf der anderen Seite, so unwahrscheinlich es auch sein mag, wenn Sie jemals zu einem Compiler wechseln, der #pragma einmal nicht unterstützt, überlegen Sie, wie viel Zeit es dauern wird, Ihre gesamte Quellcodebasis zu aktualisieren, um Include-Guards anstelle von #pragma zu haben?

Es gibt keinen Grund, warum Microsoft keine Include-Guard-Optimierung implementieren könnte, so wie es GCC und jeder andere Compiler tut (kann eigentlich jemand bestätigen, ob ihre neueren Versionen dies implementieren?). Meiner Meinung nach tut #pragma einmal sehr wenig anderes, als Ihre Wahl eines alternativen Compilers einzuschränken.

7 Stimmen

Nicht um pingelig zu sein, aber wenn Sie zu einem Compiler übertragen, der kein pragma-once unterstützt, wird das Schreiben eines einmaligen Tools zum Durchsuchen von Dateien und Ersetzen von #pragma once durch konventionelle Include-Wachen wahrscheinlich vor dem gesamten Übertragungsprozess banal sein.

2 Stimmen

"Kann eigentlich jemand bestätigen, ob ihre neueren Versionen dies implementieren?" Es wird in der Dokumentation zu VS2015 erwähnt, dass es jetzt die Optimierung des Include-Guards implementiert. msdn.microsoft.com/en-us/library/4141z1cx.aspx

3 Stimmen

"Überlegen Sie, wie viel Zeit es in Anspruch nehmen wird, um Ihre gesamte Quellbasis zu aktualisieren, um Include-Guards anstelle von #pragma zu haben?" Es sollte nicht lange dauern. Das einfacher zu machen, ist der Grund, warum ich guardonce geschrieben habe.

4voto

peterchen Punkte 39679

#pragma once ermöglicht es dem Compiler, die Datei vollständig zu überspringen, wenn sie erneut auftritt - anstatt die Datei zu parsen, bis sie auf die #include-Schutzvorrichtungen stößt.

Die Semantik ist etwas anders, aber sie sind identisch, wenn sie so verwendet werden, wie sie beabsichtigt sind.

Die Kombination beider ist wahrscheinlich der sicherste Weg, wie im schlimmsten Fall (ein Compiler, der unbekannte Pragmas als tatsächliche Fehler kennzeichnet, nicht nur als Warnungen), müssten Sie nur die #pragma's selbst entfernen.

Wenn Sie Ihre Plattformen auf beispielsweise "Mainstream-Compiler auf dem Desktop" beschränken, könnten Sie die #include-Schutzvorrichtungen sicherlich weglassen, aber auch darauf würde ich mich nicht ganz wohl fühlen.

OT: Wenn Sie andere Tipps/Erfahrungen zum Beschleunigen von Builds haben, wäre ich neugierig.

0 Stimmen

@Peterchen: Es ist falsch zu behaupten, dass ein Compiler die Datei für Include Guards erneut lesen muss. Das erste Mal, wenn der Compiler den Body analysiert, kann er aufzeichnen, ob der Header korrekte Include Guards hatte. Er kann daher den Header überspringen, wenn er später #included wird. Der wesentliche Unterschied besteht darin, dass #pragma nicht standardisiert ist und wenn Sie jemals einen Compiler verwenden müssen, der es nicht unterstützt, dann stecken Sie in großen Schwierigkeiten. Das Schlimmste, was bei Include Guards passieren kann, ist, dass die Leistung ein wenig leidet.

9 Stimmen

Nicht wirklich eine Welt des Schmerzes. Du könntest recht einfach ein Skript schreiben, das alle Vorkommen von #pragma once durch Include-Guards ersetzt. Das Schlimmste, was bei Include-Guards passieren kann, ist nicht die Leistung, sondern die Verwendung von falsch geschriebenen #ifdefs oder das Duplizieren einer Datei, das Ändern des Inhalts und das Vergessen, die #ifdefs zu aktualisieren.

1voto

Deqing Punkte 12880

Für diejenigen, die #pragma einmal und Include-Wachen zusammen verwenden möchten: Wenn Sie MSVC nicht verwenden, erhalten Sie nicht viel Optimierung von #pragma einmal.

Und Sie sollten "#pragma once" nicht in einen Header setzen, der möglicherweise mehrmals eingebunden werden soll, wobei jede Einbindung möglicherweise eine unterschiedliche Wirkung hat.

Hier ist eine ausführliche Diskussion mit Beispielen zur Verwendung von #pragma einmal.

1voto

parasrish Punkte 3416

Oben wird die Erklärung von Konrad Kleine dargestellt.

Ein kurzes Resümee:

  • Wenn wir # pragma once verwenden, liegt es hauptsächlich in der Verantwortung des Compilers, sicherzustellen, dass es nicht mehr als einmal eingebunden wird. Das bedeutet, nachdem du den Code-Schnipsel in der Datei erwähnt hast, liegt die Verantwortung nicht mehr bei dir.

Jetzt sucht der Compiler nach diesem Code-Schnipsel am Anfang der Datei und überspringt ihn, wenn er bereits einmal eingebunden wurde. Dies wird definitiv die Kompilierzeit reduzieren (im Durchschnitt und in großen Systemen). Allerdings wird es in Mocks/Testumgebungen die Implementierung von Testfällen schwieriger machen, aufgrund zirkulärer Abhängigkeiten usw.

  • Jetzt, wenn wir #ifndef XYZ_H für die Header verwenden, liegt es mehr in der Verantwortung der Entwickler, die Abhängigkeit der Headerdateien aufrechtzuerhalten. Das bedeutet, dass wenn aufgrund einer neuen Headerdatei die Möglichkeit von zirkulären Abhängigkeiten besteht, wird der Compiler nur einige "undefined .." Fehlermeldungen zur Kompilierzeit anzeigen und es liegt am Benutzer, die logische Verbindung/Reihenfolge der Entitäten zu überprüfen und unkorrekte Einbindungen zu korrigieren.

Dies wird definitiv zu einer längeren Kompilierzeit führen (da Korrekturen vorgenommen und erneut ausgeführt werden müssen). Außerdem funktioniert es auf der Basis der Einbindung der Datei, basierend auf dem "XYZ_H" -Definierungsstatus, und beschwert sich trotzdem, wenn nicht alle Definitionen erhalten werden können.

Daher sollten wir, um Situationen wie diese zu vermeiden, folgendes verwenden:

#pragma once
#ifndef XYZ_H
#define XYZ_H
...
#endif

d.h. die Kombination von beiden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X