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:
#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:
#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:
#include <iostream>
#include "kendaraan.h"
void perkenalkan(const Kendaraan& k) {
std::cout << k.nama << " beroda " << k.roda << "\n";
}#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;
}g++ -std=c++23 -Wall -Wextra utama.cpp kendaraan.cpp -o utama
./utamaOutput yang Dihasilkan:
Scoopy beroda 2
Avanza beroda 4Kompilasi 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 #
| Aspek | Include guard | #pragma once |
|---|---|---|
| Status | Bagian standar ISO | Ekstensi de facto semua compiler utama |
| Baris boilerplate | 3 baris per header | 1 baris |
| Risiko khas | Guard bentrok atau typo | File sama via dua jalur berbeda |
| Jangkauan | Semua compiler C++ yang ada | Semua compiler utama modern |
| Bisa digabung | Ya — beberapa proyek menulis keduanya sekaligus | Ya |
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 .