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.

435voto

Brian R. Bondy Punkte 325712

Ich glaube nicht, dass es einen signifikanten Unterschied in der Kompilierungszeit machen wird, aber #pragma once wird von den meisten Compilern sehr gut unterstützt, ist jedoch nicht Teil des Standards. Der Präprozessor kann mit ihm etwas schneller sein, da er einfacher zu verstehen ist und Ihre genaue Absicht klarer ist.

#pragma once ist weniger fehleranfällig und erfordert weniger Codeeingabe.

Um die Kompilierungszeit weiter zu verkürzen, deklarieren Sie einfach vorwärts anstatt in .h-Dateien einzufügen, wenn es möglich ist.

Ich ziehe es vor, #pragma once zu verwenden.

Siehe diesen Wikipedia-Artikel über die Möglichkeit, beide zu verwenden.

36 Stimmen

Laut Wikipedia optimieren einige Compiler "#pragma once", einige (wie gcc) optimieren auch Include-Guards. Mein Bauchgefühl sagt, bleib bei dem System, das dein Code bereits verwendet. Ich benutze SOWOHL "#pragma once" ALS AUCH Guards.

5 Stimmen

Es kann einen erheblichen Unterschied in der Kompilierzeit bewirken, da der C-Präprozessor nicht eingebunden werden muss - der Compiler selbst kann entscheiden, die Datei überhaupt nicht zu öffnen. Das Öffnen von Dateien immer wieder kostet Zyklen.

10 Stimmen

@Donnie - Das ist die Optimierung, die der Preprozessor von gcc auf include guards anwendet. Er erkennt automatisch include guards in Header-Dateien und behandelt die Datei so, als ob sie #pragma einmal gehabt hätte. Wenn sie erneut eingefügt wird und der include guard in der Zwischenzeit nicht aufgehoben wurde, wird die Datei nicht erneut geöffnet oder neu analysiert.

46voto

Klaim Punkte 63705

Bis zum Tag, an dem #pragma once Standard wird (das ist derzeit keine Priorität für die zukünftigen Standards), schlage ich vor, dass Sie es verwenden UND Wachen einsetzen, auf diese Weise:

#ifndef BLAH_H
#define BLAH_H
#pragma once

// ...

#endif

Die Gründe dafür sind:

  • #pragma once ist nicht standardmäßig, daher ist es möglich, dass einige Compiler die Funktionalität nicht bereitstellen. Alle großen Compiler unterstützen es jedoch. Wenn ein Compiler es nicht kennt, wird es zumindest ignoriert.
  • Da es kein Standardverhalten für #pragma once gibt, sollten Sie nicht davon ausgehen, dass das Verhalten auf allen Compilern gleich sein wird. Die Wachen stellen sicher, dass zumindest die grundlegende Annahme für alle Compiler gleich ist, die die erforderlichen Präprozessoranweisungen für Wachen implementieren.
  • Auf den meisten Compilern wird die Kompilierung (einer cpp) durch #pragma once beschleunigt, da der Compiler die Datei, die diese Anweisung enthält, nicht erneut öffnet. Es kann helfen, dies in einer Datei zu haben, oder auch nicht, abhängig vom Compiler. Ich habe gehört, dass g++ die gleiche Optimierung durchführen kann, wenn Wachen erkannt werden, aber das muss bestätigt werden.

Wenn Sie beides zusammen verwenden, erhalten Sie das Beste von jedem Compiler dafür.

Wenn Sie keine automatischen Skripte haben, um die Wachen zu generieren, ist es möglicherweise bequemer, einfach #pragma once zu verwenden. Wissen Sie jedoch, was das für portablen Code bedeutet. (Ich verwende VAssistX, um schnell die Wachen und pragma once zu generieren)

Sie sollten fast immer über Ihren Code in portabler Weise nachdenken (weil Sie nicht wissen, woraus die Zukunft besteht), aber wenn Sie wirklich glauben, dass er nicht mit einem anderen Compiler kompiliert werden soll (wie Code für sehr spezifische eingebettete Hardware zum Beispiel), sollten Sie einfach die Dokumentation Ihres Compilers zu #pragma once überprüfen, um zu wissen, was Sie wirklich tun.

7 Stimmen

Wer hat gesagt, dass #pragma einmal standardmäßig werden wird? Außerdem, wie viele Compiler verfolgen nicht, ob die Header-Datei vollständig von Include-Guards umgeben ist? Anders ausgedrückt - hat schon jemand gemessen, ob #pragma einmal tatsächlich einen Unterschied in der Realität macht?

4 Stimmen

@Richard, Ich habe es gemacht, und es trifft in einigen Fällen zu - unser Projekt hatte 5500 Einschlüsse, von denen etwa die Hälfte überflüssig war.

1 Stimmen

@Richard Ja, die Leistung wurde von vielen Personen getestet, die Optimierung ist zumindest in VC und gcc vorhanden. Ich habe nicht gesagt, dass #pragma einmal standardmäßig sein wird, es ist nur eine sehr große Möglichkeit, da der Standardisierungsprozess oft darin besteht, allgemeine Funktionen/Nutzungen zu standardisieren, die sich als effizient erwiesen haben, wie z.B. #pragma einmal.

44voto

Konrad Kleine Punkte 3958

Aus der Perspektive eines Software-Testers

#pragma once ist kürzer als ein Include-Wächter, weniger fehleranfällig, von den meisten Compilern unterstützt, und einige behaupten, dass es schneller kompiliert (was nicht mehr wahr ist).

Aber ich schlage immer noch vor, standardmäßige #ifndef Include-Wächter zu verwenden.

Warum #ifndef?

Betrachten Sie eine konstruierte Klassenhierarchie wie diese, in der jede der Klassen A, B und C in ihrer eigenen Datei lebt:

a.h

#ifndef A_H
#define A_H

class A {
public:
  // einige virtuelle Funktionen
};

#endif

b.h

#ifndef B_H
#define B_H

#include "a.h"

class B : public A {
public:
  // einige Funktionen
};

#endif

c.h

#ifndef C_H
#define C_H

#include "b.h"

class C : public B {
public:
  // einige Funktionen
};

#endif

Angenommen, Sie schreiben Tests für Ihre Klassen und müssen das Verhalten der wirklich komplexen Klasse B simulieren. Eine Möglichkeit dies zu tun wäre, eine Mock-Klasse zu schreiben, z. B. mit Google Mock und sie in einem Verzeichnis mocks/b.h abzulegen. Beachten Sie, dass der Klassenname unverändert bleibt, er wird nur in einem anderen Verzeichnis gespeichert. Aber am wichtigsten ist, dass der Include-Wächter genau den gleichen Namen hat wie in der Originaldatei b.h.

mocks/b.h

#ifndef B_H
#define B_H

#include "a.h"
#include "gmock/gmock.h"

class B : public A {
public:
  // einige Mock-Funktionen
  MOCK_METHOD0(SomeMethod, void());
};

#endif

Was ist der Nutzen?

Mit diesem Ansatz können Sie das Verhalten der Klasse B ohne Änderungen an der Originalklasse oder deren Verwendung in C verändern. Alles was Sie tun müssen, ist das Verzeichnis mocks/ in den Include-Pfad Ihres Compilers einzufügen.

Warum funktioniert dies nicht mit #pragma once?

Wenn Sie #pragma once verwendet hätten, käme es zu einem Namenskonflikt, da es Sie nicht davor schützen kann, die Klasse B zweimal zu definieren, einmal die Originale und einmal die gemockte Version.

9 Stimmen

Dies ist nicht praktikabel für ein großes Projekt. B aktuell zu halten wird schmerzhaft sein und ganz zu schweigen davon, dass dies die One Definition Rule verletzt.

6 Stimmen

@parapurarajkumar Der ODR wird nicht verletzt, weil, wenn Sie mocks/b.h vor b.h einfügen, der Präprozessor b.h vollständig überspringt und Sie nur eine Klasse B übrig bleibt. Ich frage mich, ob Sie einen weniger "schmerzhaften" Ansatz zum Testen von Klasse B haben. Welche Strategie schlagen Sie für das Testen von "jedem großen Projekt" vor?

0 Stimmen

Aber es wird andere Teile des Systems geben, d.h. Nicht-Testcode, wo Sie nicht gmock.h überschreiben können?

28voto

Donnie DeBoer Punkte 2517

Wenn Sie sicher sind, dass Sie diesen Code niemals in einem Compiler verwenden werden, der ihn nicht unterstützt (Windows/VS, GCC und Clang sind Beispiele für Compiler, die es unterstützen), können Sie #pragma once ohne Bedenken verwenden.

Sie können auch einfach beide verwenden (siehe Beispiel unten), um Portabilität und Kompilierungsgeschwindigkeit auf kompatiblen Systemen zu erhalten

#pragma once
#ifndef _HEADER_H_
#define _HEADER_H_

...

#endif

28 Stimmen

pragma once wird von vielen verschiedenen Compilern gut unterstützt, einschließlich GCC

6 Stimmen

Wahr, aber es wird nicht allgemein unterstützt. Include-Guards werden immer unterstützt... es hängt davon ab, wie portabel Sie Ihren Code haben möchten.

6 Stimmen

Ich möchte wirklich keine beiden, das macht den Code nur hässlich. Solange die Portabilität kein Problem darstellt, denke ich, dass Code-Hygiene-Probleme als nächstes auftreten.

16voto

Motti Punkte 104854

Es gibt eine verwandte Frage, zu der ich geantwortet habe:

#pragma once hat einen Nachteil (abgesehen davon, dass es nicht standardmäßig ist) und das ist, wenn Sie die gleiche Datei an verschiedenen Orten haben (wir haben das, weil unser Build-System Dateien kopiert), dann wird der Compiler denken, dass dies verschiedene Dateien sind.

Ich füge die Antwort hier auch hinzu, falls jemand über diese Frage stolpert und nicht über die andere.

0 Stimmen

@ShitalShah dies ist nur eine Überlegung, wenn Ihr Build-System Dateien herumkopiert

13 Stimmen

Beheben Sie die #pragma once-Implementierung, sie sollte zuverlässige Prüfsummen anstelle von fragwürdigen Dateinamen verwenden.

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