#pragma once în C++

Operatori alternativi și preprocesor Extensie nestandard (GCC, clang, MSVC)

Pusă la începutul unui fișier antet, face ca fișierul să fie inclus o singură dată, chiar dacă apare în mai multe #include-uri.

Sintaxă
#pragma once   // prima linie a unui fișier antet

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 once nu 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.