История вопроса:
Макросы пришли из языка C как мощный способ автоматизации повторяющихся участков кода на этапе препроцессирования. В C++ их использование дало гибкость, но также принесло множество скрытых опасностей из-за отсутствия проверки типов и неочевидности работы препроцессора.
Проблема:
Главные риски использования макросов:
Решение:
В современных стандартах C++ рекомендуется использовать inline-функции, шаблоны, constexpr, enum class, а также constexpr переменные вместо макросов.
Пример кода:
// Плохо: #define MAX(a, b) ((a) > (b) ? (a) : (b)) // Хорошо: template<typename T> constexpr T max(T a, T b) { return a > b ? a : b; }
Ключевые особенности:
Может ли макрос быть опаснее inline-функции?
Да. Макрос не подчиняется правилам синтаксиса и типов. Возможен неожиданный результат при передаче параметров с побочными эффектами.
#define SQUARE(x) ((x) * (x')) int y = 5; int z = SQUARE(y++); // y инкрементируется дважды!
Является ли #include тоже макросом?
Нет, #include — директива препроцессора, но употребление макросов и include связано: через макрос можно изменять список подключаемых файлов (крайне не рекомендуется).
Можно ли отлаживать макрос как обычную функцию?
Нет, отладчик раскрывает макрос и показывает уже подставленный текст, нет отдельных именованных сущностей.
В старом коде были определены многочисленные вычислительные макросы с эффектами (например, инкремент), что влекло за собой трудноуловимые баги при эксплуатации новых функций.
Плюсы:
Минусы:
При рефакторинге макросы были заменены на шаблонные и constexpr-функции, а enum class применён вместо макросов-флагов.
Плюсы:
Минусы: