HOWTO · C++
#pragma once in C++: Include Guards und Portabilität
Verhindern Sie wiederholte Header-Einbindungen in C++ mit #pragma once oder Include Guards und wählen Sie passend zu Ihrer Toolchain.
Verwenden Sie #pragma once am Anfang eines normalen C- oder C++-Headers, wenn alle unterstützten Compiler es implementieren. Es verarbeitet die Datei in jeder Übersetzungseinheit höchstens einmal. Für standardkonforme Portabilität, unbekannte Compiler oder eine bestehende Projektkonvention verwenden Sie einen eindeutig benannten Include Guard. Beides beschränkt einen Header nicht auf eine einzige Verwendung im gesamten Programm.
#pragma once vor Deklarationen setzen
// config.hpp
#pragma once
class Config {
public:
int port() const { return 8080; }
};
Die Direktive steht ohne Semikolon vor den Deklarationen. Der Präprozessor verarbeitet #include vor der Kompilierung; #pragma once verhindert deshalb erneuten Headertext beim Aufbau einer Übersetzungseinheit. Es ist eine weit verbreitete, aber keine ISO-C- oder ISO-C++-Direktive. GCC dokumentiert sie als Alternative zu Once-Only-Headers; prüfen Sie trotzdem die tatsächlich zugesagten Toolchains.
Direkte und indirekte Einbindung prüfen
Dieser exakt getestete Drei-Dateien-Fall bindet config.hpp über server.hpp und direkt aus main.cpp ein:
// server.hpp
#pragma once
#include "config.hpp"
class Server { Config config_; };
// main.cpp
#include "server.hpp"
#include "config.hpp"
int main() {
return Config{}.port() == 8080 ? 0 : 1;
}
Speichern Sie alle drei Dateien in einem Verzeichnis und führen Sie aus:
g++ -std=c++17 -Wall -Wextra main.cpp -o app
./app
Mit #pragma once gibt der Befehl nichts aus und endet mit Status 0. Getestet wurde mit g++ (Ubuntu 15.2.0-16ubuntu1) 15.2.0. Entfernen Sie nur die Direktive aus config.hpp, endet GCC mit Status 1 und meldet die Neudefinition von Config über die direkte und indirekte Einbindung. Das gilt pro Übersetzungseinheit und wurde nicht separat mit MSVC oder Clang getestet.
Standardportablen Include Guard verwenden
// config.hpp
#ifndef EXAMPLE_CONFIG_HPP
#define EXAMPLE_CONFIG_HPP
class Config {
public:
int port() const { return 8080; }
};
#endif // EXAMPLE_CONFIG_HPP
Ein Include Guard verwendet nur Standard-Präprozessor-Direktiven: Das Makro wird bei der ersten Einbindung definiert und spätere Einbindungen überspringen den Text. Wählen Sie einen eindeutigen Namen aus Projekt, Verzeichnis und Datei. CONFIG_H kann mit einem anderen Header kollidieren; Namen mit __ oder _ plus Großbuchstaben sind der Implementierung vorbehalten.
#include ist textuelle Einbindung: Der Präprozessor ersetzt die Direktive vor der C++-Prüfung durch Headertext. Im Beispiel gelangt config.hpp zuerst über server.hpp und danach direkt in main.cpp; ohne Schutz erhält der Compiler zwei Config-Definitionen. Deshalb gehört der Schutz in den Header, nicht nur in eine zufällig einbindende Quelldatei. Das ist ein Compile-Fehler und nicht mit einem Linkerfehler aus getrennt kompilierten Dateien zu verwechseln.
Die passende Variante wählen
| Situation | Wahl | Grund |
|---|---|---|
Alle Zielcompiler unterstützen #pragma once |
#pragma once |
Kurz, ohne Makrokollision. |
| Öffentliche Bibliothek, unbekannter Compiler oder strikte Portabilität | Include Guard | Standard-Präprozessor-Code. |
| Bestehende Repository-Konvention | Bestehende Konvention | Einheitliche Header sind wartbarer. |
| Absichtlich mehrfach eingebundener Header, etwa eine X-Macro-Liste | Standardmäßig keines von beiden | Jede Einbindung ist beabsichtigt. |
Setzen Sie nicht standardmäßig beide Mechanismen in jeden Header: Einer genügt normalerweise, und Compiler erkennen übliche Guards ebenfalls als Once-Only-Headers. Behaupten Sie auch keinen allgemeinen Geschwindigkeitsvorteil; messen Sie bei einer Umstellung. #pragma once hängt von der Dateidentität ab; Aliase, generierte Dateien, Netzwerkdateisysteme oder ungewöhnliche Include-Pfade können relevant sein. Guards umgehen dies, verlangen aber ein eindeutiges Makro.
Was Header-Schutz nicht löst
Der Schutz wirkt je Übersetzungseinheit und behebt weder jeden Linkerfehler mit mehrfachen Definitionen noch Verletzungen der One Definition Rule (ODR). Eine nicht als inline markierte freie Funktionsdefinition im Header kann in jeder einbindenden .cpp eine externe Definition erzeugen. Legen Sie gewöhnliche Definitionen in eine .cpp oder verwenden Sie bewusst inline beziehungsweise Templates. Guards lösen auch keine zyklische Abhängigkeit, wenn beide Typen vollständig sein müssen: Verwenden Sie Vorwärtsdeklarationen für Zeiger oder Referenzen oder verschieben Sie Implementierungsdetails. C++20-Module sind anders, denn import bindet keinen Headertext ein.
Zusammenfassung
Nutzen Sie #pragma once bei akzeptabler Erweiterungsunterstützung und einen eindeutigen Include Guard für Standardportabilität. Prüfen Sie den direkten und indirekten Include-Pfad; ODR-, Zyklus-, Mehrfacheinbindungs- und Modulfragen sind davon getrennt.