Как защитить header-файл от повторного включения в C++

Ответ

В C++ для защиты заголовочного файла от множественного включения и связанных с этим ошибок переопределения используются два основных метода.

1. Include Guards (Стражи включения) Стандартный, переносимый способ, который работает везде.

// File: my_class.h
#ifndef MY_CLASS_H // Если макрос не определен...
#define MY_CLASS_H // ... определяем его.

class MyClass {
    // ... объявление класса ...
};

#endif // MY_CLASS_H

Правила для имени макроса: Оно должно быть уникальным в рамках проекта. Обычно используют имя файла в верхнем регистре с подчеркиваниями (например, PROJECT_PATH_MY_CLASS_H).

2. Директива #pragma once Нестандартная, но широко поддерживаемая всеми основными компиляторами (GCC, Clang, MSVC) директива.

// File: my_class.h
#pragma once

class MyClass {
    // ... объявление класса ...
};
Сравнение: Критерий #pragma once Include Guards
Стандарт Не входит, расширение компилятора Стандартный C/C++
Скорость компиляции Может быть быстрее (компилятор отслеживает файлы) Медленнее (препроцессор проверяет макросы)
Надежность Может не сработать при симлинках или копиях файла Абсолютно надежна, если имя макроса уникально
Удобство Кратко, не требует придумывания имени Требует уникального имени и трех строк кода

Моя практика: В современных кроссплатформенных проектах на C++ я использую #pragma once из-за удобства и скорости. Однако для библиотек, требующих максимальной переносимости, или в корпоративных стандартах, запрещающих нестандартные расширения, следует использовать Include Guards.

Ответ 18+ 🔞

А, слушай, вот тебе тема, про которую все думают, что она простая, а на самом деле тут тоже свои подводные камни есть. Речь про то, как сделать так, чтобы твой хедер не вставлялся в код по десять раз, отчего компилятор начинает орать, что ты всё переопределил. Ёпта, волнение ебать, когда видишь эту ошибку впервые.

Так вот, способов два, оба рабочие, но с нюансами.

Первый — классические стражи, или Include Guards. Это как замок на двери, стандартный такой, железный. Работает везде, даже на древнем компиляторе, который помнит твоего деда.

// File: my_class.h
#ifndef MY_CLASS_H // Если этот замок ещё не закрыт...
#define MY_CLASS_H // ... закрываем его нахуй.

class MyClass {
    // ... ну и сам класс тут ...
};

#endif // MY_CLASS_H

Важный момент, чувак: имя для этого макроса надо придумать такое, чтобы оно было единственным и неповторимым во всём проекте. Иначе будет пиздец. Обычно берут имя файла, делают его большими буквами и суют туда подчёркивания. Типа PROJECT_SUBFOLDER_MY_CLASS_H. Главное — не повторяться, а то получится манда с ушами.

Второй — директива #pragma once. Это как современный домофон. Не по стандарту, но его уже все понимают — GCC, Clang, MSVC, все основные ребята.

// File: my_class.h
#pragma once

class MyClass {
    // ... и опять класс ...
};

Короче, красиво и коротко. Никаких макросов не надо.

А теперь, бля, сравнение, чтобы было понятно, что куда тыкать:

Критерий #pragma once Include Guards
Стандартность Расширение, не по ГОСТу Канон, священное писание C/C++
Скорость Часто быстрее, компилятор умный, файлы помнит Медленнее, препроцессор тупо макросы чекает
Надёжность Может накрыться медным тазом, если у тебя симлинки или копии файлов Железобетонная, если имя макроса уникальное
Удобство Одна строчка, и ты красавчик Три строчки, ещё и имя выдумывай

Что я сам делаю? В современных проектах, которые не должны собираться на допотопном железе, я юзаю #pragma once. Удобно, быстро, и код чище выглядит. Но если пишешь какую-нибудь библиотеку, которую будут тащить куда угодно, или сидишь в конторе, где стандарты строгие — тогда только классические guards, без вариантов. Терпения ноль ебать, но так надёжнее.