Ce face #pragma once
Același antet ajunge adesea să fie inclus de mai multe ori în același fișier .cpp: direct și prin alte anteturi. Dacă antetul definește o clasă, a doua copie dă eroarea „redefinition”. #pragma once, pusă la începutul antetului, îi spune compilatorului să ignore toate includerile după prima.
Alternativa standard sunt gărzile de includere: #ifndef PRODUS_H / #define PRODUS_H / … / #endif. La prima includere macro-ul nu există, deci conținutul este păstrat și macro-ul definit; la următoarele, conținutul este sărit. #pragma once este mai scurt și nu poate avea nume greșite sau duplicate, motiv pentru care este folosit de majoritatea proiectelor noi; tehnic nu face parte din standard, dar îl acceptă GCC, clang și Visual C++.
#pragma once are sens doar într-un fișier antet. Pus în fișierul principal (.cpp), clang dă avertismentul „#pragma once in main file”, motiv pentru care exemplele de mai jos arată fișierele separat.
Exemple
Exemplul 1: Antetul: produs.h
// produs.h
#pragma once
#include <string>
struct Produs {
std::string nume;
double pret;
};
Exemplul 2: Antetul inclus de două ori: main.cpp
Fără #pragma once în produs.h, a doua includere ar da „redefinition of ‘Produs’”. Cu el, a doua includere este ignorată. Exemplul are două fișiere, deci a fost compilat și rulat separat.
#include <print>
#include "produs.h"
#include "produs.h" // în practică vine indirect, prin alt antet
int main() {
Produs p{"Agendă", 35.5};
std::println("{} costă {} lei", p.nume, p.pret);
}
Rezultat (compilat cu produs.h de mai sus)
Agendă costă 35.5 lei
Exemplul 3: Același efect cu gărzi de includere
Aici „includerea” de două ori este simulată scriind de două ori conținutul antetului, cu gardă. A doua oară PRODUS_H este deja definit, deci structura nu mai este definită din nou.
#include <print>
#include <string>
// prima includere a lui produs.h
#ifndef PRODUS_H
#define PRODUS_H
struct Produs {
std::string nume;
double pret;
};
#endif
// a doua includere: conținutul este sărit
#ifndef PRODUS_H
#define PRODUS_H
struct Produs {
std::string nume;
double pret;
};
#endif
int main() {
Produs p{"Agendă", 35.5};
std::println("{} costă {} lei", p.nume, p.pret);
}
Rezultat
Agendă costă 35.5 lei
Greșeli frecvente
- Copiezi un antet și uiți să schimbi numele gărzii (
PRODUS_Hîn ambele fișiere). Al doilea antet este sărit în tăcere, iar erorile spun că lipsesc clase.#pragma oncenu are această problemă. - Ai același fișier accesibil pe două căi diferite (de exemplu prin legături simbolice). Unele compilatoare nu își dau seama că e același fișier și îl includ de două ori, chiar cu
#pragma once.
Comenzi înrudite
#include: Directivă a preprocesorului care copiază conținutul unui fișier antet în locul ei, ca să poți folosi funcțiile și clasele declarate acolo.#ifdef, #ifndef, #if, #endif: Compilare condiționată: păstrează sau elimină bucăți de cod înainte de compilare, după ce macro-uri sunt definite.#define: Definește un macro: preprocesorul înlocuiește textual numele cu valoarea lui, peste tot în cod, înainte de compilare.module, import, export: Modulele (C++20) înlocuiesc fișierele antet: un modul exportă ce vrea să fie vizibil, iar alt fișier îl folosește cu import.