Include guard dari Chapter sebelumnya bekerja andal, tetapi jujur saja: tiga baris boilerplate #ifndef/#define/#endif di setiap header terasa membosankan, dan nama guard-nya masih bisa salah ketik. Karena itu hampir setiap compiler modern menyediakan jalan pintas satu baris: #pragma once. Pada Chapter ini kita melihat cara pakainya pada modul nyata, bagaimana compiler mengeksekusinya, posisi yang jujur terhadap standar ISO, dan pertimbangan memilih antara keduanya — pertanyaan yang pasti muncul saat Anda membuka codebase besar dan menemukan dua gaya berdampingan.

Satu Baris Pengganti Tiga Baris #

Kita bangun ulang skenario double include yang sama seperti Chapter lalu, kali ini dengan #pragma once. Header pertama mendefinisikan struct Kendaraan:

cpp kendaraan.h
#pragma once

#include <string>

struct Kendaraan {
    std::string nama;
    int roda;
};

void perkenalkan(const Kendaraan& k);

Header kedua memakai Kendaraan — dan sekali lagi menarik kendaraan.h ke dalam dirinya:

cpp garasi.h
#pragma once

#include <vector>
#include "kendaraan.h"

struct Garasi {
    std::vector<Kendaraan> isi;

    void tambah(const Kendaraan& k) {
        isi.push_back(k);
    }
};

Skenario jebakannya persis sama: utama.cpp meng-include kendaraan.h secara langsung (untuk memanggil perkenalkan) dan secara tidak langsung lewat garasi.h (untuk membuat Garasi). Dengan guard versi makro kita harus memikirkan dua nama pelindung yang unik; di sini cukup satu baris #pragma once di puncak setiap file:

cpp kendaraan.cpp
#include <iostream>
#include "kendaraan.h"

void perkenalkan(const Kendaraan& k) {
    std::cout << k.nama << " beroda " << k.roda << "\n";
}
cpp utama.cpp
#include "kendaraan.h"
#include "garasi.h"

int main() {
    Garasi g;
    g.tambah({"Scoopy", 2});
    g.tambah({"Avanza", 4});

    for (const Kendaraan& k : g.isi) {
        perkenalkan(k);
    }
    return 0;
}
bash
g++ -std=c++23 -Wall -Wextra utama.cpp kendaraan.cpp -o utama
./utama

Output yang Dihasilkan:

text
Scoopy beroda 2
Avanza beroda 4

Kompilasi lolos tanpa suara — double include tertangani otomatis, dan tidak ada satu pun nama pelindung yang kita tulis.

Cara Kerja dan Portabilitas #

Berbeda dari include guard yang bekerja lewat makro, #pragma once bekerja lewat identitas file: compiler mencatat “file ini sudah pernah dimasukkan” saat menemukan pragma pertama kali, dan setiap permintaan berikutnya terhadap file yang sama langsung dilewati. Tidak ada makro yang bocor ke namespace preprocessor, tidak ada risiko dua header memakai guard sama, dan tidak ada kemungkinan salah ketik KOTAK__H yang menyisakan header telanjang.

Posisinya terhadap standar memang perlu diketahui dengan jujur: #pragma once bukan bagian dari standar ISO C++ — #pragma pada dasarnya adalah celah ekstensi compiler. Meski begitu, praktiknya sudah bertahun-tahun: GCC, Clang, dan MSVC semuanya mendukung, dan pola ini sangat lazim di proyek berbasis MSVC. Konsekuensi praktis dari status non-standarnya hanya satu: pada skenario file sistem yang tidak lazim — misalnya file yang sama dijangkau lewat dua jalur berbeda (symlink atau drive jaringan) — compiler teoretis bisa menganggapnya dua file berbeda, sesuatu yang tidak terjadi pada include guard karena guard bertumpu pada nama makro, bukan jalur file. Untuk proyek normal di disk lokal, perbedaan ini tidak pernah terasa.

Include Guards vs pragma once #

AspekInclude guard#pragma once
StatusBagian standar ISOEkstensi de facto semua compiler utama
Baris boilerplate3 baris per header1 baris
Risiko khasGuard bentrok atau typoFile sama via dua jalur berbeda
JangkauanSemua compiler C++ yang adaSemua compiler utama modern
Bisa digabungYa — beberapa proyek menulis keduanya sekaligusYa

Baris terakhir tabel layak dijelaskan: karena #ifndef tidak mengganggu apa pun, beberapa proyek menuliskan #pragma once dan include guard bersamaan — siap menghadapi compiler apa pun yang mungkin mereka jumpai. Course ini memakai #pragma once saja agar contoh tetap ramping, dengan asumsi compiler modern seperti GCC 14 yang kita pakai sepanjang section ini.

Keduanya menyelesaikan persoalan yang sama dari sisi preprocessor. Dengan header yang kini aman total, kita siap melangkah ke level berikutnya: bukan lagi cara file saling menemukan, melainkan bagaimana banyak file dikompilasi secara terpisah lalu disatukan — model separate compilation yang menjadi dasar semua build system.

Referensi: Preprocessor di cppreference , Dokumentasi preprocessor GCC .